В настоящее время я разрабатываю и внедряю RESTful API в PHP. Тем не менее, мне не удалось реализовать мой первоначальный дизайн.
GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1
Пока что довольно стандартно, правда?
Моя проблема с первым GET /users
. Я рассматривал отправку параметров в теле запроса для фильтрации списка. Это потому, что я хочу иметь возможность указывать сложные фильтры без получения очень длинного URL, например:
GET /users?parameter1=value1¶meter2=value2¶meter3=value3¶meter4=value4
Вместо этого я хотел иметь что-то вроде:
GET /users
# Request body:
{
"parameter1": "value1",
"parameter2": "value2",
"parameter3": "value3",
"parameter4": "value4"
}
который намного более читабелен и дает вам большие возможности для установки сложных фильтров.
Во всяком случае, file_get_contents('php://input')
не вернул тело запроса для GET
запросов. Я тоже пробовал http_get_request_body()
, но общего хостинга, который я использую, нет pecl_http
. Не уверен, что это помогло бы в любом случае.
Я нашел этот вопрос и понял, что GET, вероятно, не должен иметь тело запроса. Это было немного неокончательно, но они советовали против этого.
Так что теперь я не уверен, что делать. Как вы проектируете RESTful функцию поиска / фильтрации?
Я полагаю, я мог бы использовать POST
, но это не выглядит очень RESTful.
Ответы:
Лучший способ реализовать поиск RESTful - считать сам поиск ресурсом. Тогда вы можете использовать глагол POST, потому что вы создаете поиск. Вам не нужно буквально создавать что-то в базе данных, чтобы использовать POST.
Например:
Вы создаете поиск с точки зрения пользователя. Детали реализации этого не имеют значения. Некоторым API RESTful может даже не потребоваться постоянство. Это деталь реализации.
источник
Если вы используете тело запроса в GET-запросе, вы нарушаете принцип REST, потому что ваш GET-запрос не сможет быть кэширован, потому что система кеширования использует только URL.
И что еще хуже, ваш URL не может быть добавлен в закладки, потому что URL не содержит всю информацию, необходимую для перенаправления пользователя на эту страницу
Используйте параметры URL или Query вместо параметров тела запроса.
например:
На самом деле HTTP RFC 7231 говорит, что:
Полезная нагрузка в сообщении запроса GET не имеет определенной семантики; отправка тела полезной нагрузки по запросу GET может привести к тому, что некоторые существующие реализации отклонят запрос.
Для получения дополнительной информации посмотрите здесь
источник
Кажется, что фильтрация / поиск ресурсов может быть реализован RESTful способом. Идея состоит в том, чтобы ввести новую конечную точку под названием
/filters/
или/api/filters/
.Использование этого фильтра конечной точки можно рассматривать как ресурс и, следовательно, создавать с помощью
POST
метода. Таким образом, конечно, тело может быть использовано для передачи всех параметров, а также для создания сложных структур поиска / фильтрации.После создания такого фильтра есть две возможности получить результат поиска / фильтра.
Новый ресурс с уникальным идентификатором будет возвращен вместе с
201 Created
кодом состояния. Затем с помощью этого идентификатораGET
можно сделать запрос/api/users/
:После создания нового фильтра
POST
он не будет отвечать,201 Created
но сразу303 SeeOther
сLocation
заголовком, указывающим на/api/users/?filterId=1234-abcd
. Это перенаправление будет автоматически обработано через базовую библиотеку.В обоих сценариях необходимо сделать два запроса для получения отфильтрованных результатов - это может рассматриваться как недостаток, особенно для мобильных приложений. Для мобильных приложений я бы использовал один
POST
вызов/api/users/filter/
.Как сохранить созданные фильтры?
Они могут храниться в БД и использоваться позже. Они также могут храниться в некотором временном хранилище, например, в redis, и иметь некоторое TTL, после которого они истекают и будут удалены.
Каковы преимущества этой идеи?
Фильтры, отфильтрованные результаты кэшируются и даже могут быть добавлены в закладки.
источник
Я думаю, что вы должны использовать параметры запроса, но только до тех пор, пока нет соответствующего HTTP-заголовка для выполнения того, что вы хотите сделать. В спецификации HTTP явно не сказано, что GET не может иметь тела. Однако в этой статье говорится:
источник
Поскольку я использую бэкэнд laravel / php, я склонен делать что-то вроде этого:
PHP автоматически превращает
[]
params в массив, поэтому в этом примере я получу$filter
переменную, которая содержит массив / объект фильтров, а также страницу и любые связанные ресурсы, которые я хочу загрузить с нетерпением.Если вы используете другой язык, это может быть хорошим соглашением, и вы можете создать парсер для преобразования
[]
в массив.источник
[
и]
. Использование закодированных представлений этих символов для группировки параметров запроса является хорошо известной практикой. Он даже используется в спецификации JSON: API .Не беспокойтесь слишком сильно, если ваш начальный API полностью RESTful или нет (особенно когда вы находитесь на стадии альфа). Сначала включите работу сантехники. Вы всегда можете выполнить какое-то преобразование / переписывание URL-адреса, чтобы наметить все, уточняя итеративно, пока не получите что-то достаточно стабильное для широкого тестирования («бета»).
Вы можете определить URI, параметры которых закодированы в соответствии с положением и соглашением, на самих URI, с префиксом пути, который, как вы знаете, всегда будет сопоставляться с чем-либо. Я не знаю PHP, но я бы предположил, что такая возможность существует (как она существует в других языках с веб-фреймворками):
.ie. Выполните поиск «пользовательского» типа с параметром [i] = значение [i] для i = 1..4 в хранилище № 1 (со значением 1, значением 2, значением 3, ... в качестве сокращения для параметров запроса URI):
или
или как следует (хотя я бы не советовал, об этом позже)
При выборе варианта 1 вы отображаете все URI с префиксом
/store1/search/user
в обработчик поиска (или любое другое обозначение PHP) по умолчанию для поиска ресурсов в хранилище store1 (эквивалентно/search?location=store1&type=user
.По соглашению, задокументированному и применяемому API, значения параметров с 1 по 4 разделяются запятыми и представляются в указанном порядке.
Вариант 2 добавляет тип поиска (в данном случае
user
) в качестве позиционного параметра # 1. Любой из вариантов - просто косметический выбор.Вариант 3 также возможен, но я не думаю, что мне это понравится. Я думаю, что возможность поиска в определенных ресурсах должна быть представлена в самом URI, предшествующем самому поиску (как если бы в URI четко указывалось, что поиск специфичен для ресурса).
Преимущество этого перед передачей параметров по URI заключается в том, что поиск является частью URI (таким образом, поиск рассматривается как ресурс, ресурс, содержимое которого может и будет меняться со временем.) Недостатком является то, что порядок параметров является обязательным ,
Как только вы сделаете что-то подобное, вы можете использовать GET, и это будет ресурс только для чтения (так как вы не можете POST или PUT к нему - он обновляется, когда он GET'ed). Это также будет ресурс, который появляется только при его вызове.
Можно также добавить к нему больше семантики, кэшируя результаты в течение определенного периода времени или удаляя DELETE, вызывая удаление кэша. Это, однако, может противоречить тому, для чего люди обычно используют DELETE (и потому, что люди обычно управляют кэшированием с помощью заголовков кэширования).
То, как вы поступите, было бы дизайнерским решением, но я бы так и поступил. Это не идеально, и я уверен, что будут случаи, когда делать это не лучше (особенно для очень сложных критериев поиска).
источник
К вашему сведению: я знаю, что уже немного поздно, но для тех, кто заинтересован. Зависит от того, насколько RESTful вы хотите быть, вам придется реализовать свои собственные стратегии фильтрации, так как спецификация HTTP не очень ясна по этому поводу. Я хотел бы предложить URL-кодирование всех параметров фильтра, например
Я знаю, что это некрасиво, но я думаю, что это самый ОТЛИЧНЫЙ способ сделать это, и его должно быть легко разобрать на стороне сервера :)
источник