Pull to refresh

Comments 52

Буквально недавно читал статью на которую наткнулся по запросу «API best practices»… Знаете, там 90% материала изложенного в той статье совпадает с Вашим.
да очень много похожих частей… так сказать — творческий перевод :) с переименованием dog в user
Возможно. У меня свой опыт, а в целом тема не новая, информации много разной.
Всегда было непонятно, каким статусом отвечать на успешный DELETE. Что порекомендуете?
Спасибо. Как-то я не подумал про этот код.
А разве DELETE — идемпотентен? Если ресурс уже удален, разве запрос не должен вернуть 404?
Всегда думал что PUT — создать а POST — изменить…
Спасибо за минусы
Уточняю что по стандартам PUT предполагает создание ресурса при его отсутствии тогда как POST такого не предполагает.
В статье дается вариант несколько адаптированный для большей конкретизации и упрощения модели использования. В стандарте PUT применяется для добавления сущности, если она отсутствует и для обновления уже имеющейся. POST для создания
В стандарте какой версии? POST — это создание, PUT — обновление (полное), или создание (если ресурс не существует), а PATCH — частичное изменение существующего ресурса. Стандарт HTTP 1.1.

Но для API, мне кажется больше подходит Rails подход. POST — создание, PATCH — обновление. Это упрощает API, плюс при таком подходе, PUT становится частным случаем PATCH.
HTTP 1.0 PUT как таковой отсутствует.
Да, моя ошибка, не собран сегодня.
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. POST is designed to allow a uniform method to cover the following functions:


The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request. If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.

Спасибо!
Не совсем понятно для чего нужен supress_status_code. Не могли бы вы привести какой-нибудь пример?
В зависимости от контекста может возникать необходимость не выводить ошибки, например, если удаляется отсутствующая запись, а в каких-то случаях может понадобиться ошибка (в зависимости от задачи) — это делает API гибче. Никогда не знаешь, в чем у другого программиста может возникнуть потребность и при какой ситуации.
Какой-нибудь экзотический клиент может не обрабатывать тело ответа (с закодированными дополнительными сведениями об ошибке) в случае ошибочного HTTP STATUS.
У меня есть вопрос. Не могли бы вы по подробней описать использование паттерна «Фасад» для разработки API? Мне не совсем понятно, как и где его следует применять при столько ограниченном наборе глаголов. Я был бы признателен за небольшой жизненный пример.
Паттерн «Фасад», есть смысл использовать при разработке любого API. То есть перед началом программирования хорошо иметь представление о будущем функционале в конкретной и наглядной форме. Это может значительно упростить структуризацию кода и сократить его дублирование. «Фасад» скорее является грамотным подходом к проектированию опять таки любого API. Все сущности и связи в проекте желательно предусмотреть до начала программирования.
Насколько я понял, тут автор имеет ввиду, что вы сперва продумываете интерфейс и создаете заглушки для него — url, тип ответа, структуру ответа, а затем уже можно программировать внутренности, не переживая за то, что какой то клиент не сможет сделать запрос, потому что вы поменяли url, или необходимы дополнительные параметры.
Всегда было интересно, как быть с глаголами, если требуется реализовать набор примитивных действий над ресурсом (activate, block, rename, etc), что проще и читабельнее?

PUT /api/users/de840a?status=blocked
или
PUT /api/users/de840a/block
Считают, что для реализации подобного рода действий возможно применять глаголы, но их количество должно быть минимальным.
P.S.: Второй вариант мне больше импонирует. Первый скорее похож на фильтр по полю «status».
Может так:

PATCH /api/users/de840a
status=blocked
Да PATCH здесь вполне приемлемое решение.
UFO just landed and posted this here
Зачем ломать мозг, чтобы понять ФП? Каким образом связано ФП и Haskell в плане суждения об общем по частному? Вы уверены, что хотя бы правильно понимаете понятие ФП?

Чтобы построить хороший RPC нужно ломать мозг еще сильнее, так как RPC не имеет никаких ограничений, и полностью полагается на добросовестность разработчика в том плане, что он будет проектировать API, а не лепить бекенд код так, как ему удобно (что очень легко делать в RPC ввиду отсутствия ограничений, подобных тем, что присутствуют в REST).
UFO just landed and posted this here
О, рест это такая прелесть. Меня всегда интересовала идеология, что на метод GET мы ничего не меняем и только получаем данные. А вот конкретный пример — запрашиваем статью по ее ID. И есть у нас поле «число просмотров». Как предлагаете его инкрементить?
Подразумевается, что при GET запросе, мы не получаем от пользователя данных для записи.
Внутренние счетчики обновлять никто не мешает.

* Но вообще все это сильно зависит от бизнес логики.
Строго говоря, какое нибудь мобильное приложение, может закешировать статью и не вызывать каждый раз GET при множественных просмотрах.
Если показатель числа просмотров важен — сделайте отдельный POST метод для статистики просмотров.
Ну логика простая. Мобилочка показывает статьи сайта, например. Типа браузер. И кэш тут особо-то и ни при чем, ибо все равно хотя бы раз надо же запросить. И, предположим, при выводе есть такой глазик с числом просмотров.

Просто такой вот простой пример меня лично ставит в тупик и ломает самый простой догмат рест.
Есть сайт. У него статьи. У статьи помимо присущих данных, есть read-only поле счетчика просмотров, которое инкрементирует внутренняя бизнес-логика приложения, при запросе на просмотр например. Пользователь данные не отправляет.

При запросе коллекции статей — мы получаем список статей с их счетчиками. В чем проблема?
Вот есть у меня 10 идентификаторов пользователей. И мне для построения тела страницы необходимы подробные данные о них. Как мне поступить?
1. десять раз сделать get в котором я запрашиваю данные по одному пользователю,
2. или сделать один get, в котором в параметрах передаются их идентификаторы (GET /api/users/123/321/567/432/865/6432/54322/8654/9876/439)
Здесь тема о построении серверной части. Если вы уверены, что всем нужны такие таблицы, делайте метод для возврата уже таблицы. Если нет — то 10 GET запросов. На самом деле 10 паралельных запросов, это не так долго.
Если просто у вас система работы с пользователями, то делайте, чтобы метод принимал массив.
Скорее всего у вас не сферическое API в вакууме.
Мне кажется оптимальным будет вариант в духе:

GET: api.example.com/users?ids=1,2,3
Учитывая контекст — веб и как следствия браузер как клиент, быстрее будет одним коннектом через keep-alive.
Почему контекст — веб? Айфон, андроид, винфон. И почему не websockets? Но тогда это уже явно не REST.
Всю сколько-нибудь сложную логику стоит выносить за знак "?".
Например, как-то так:
GET /api/users?ids=123,321,567,432
HTTP Status Code: 401

{«status»: 401, «message»: «Authentication Required», «code»: 20003, «more_info»: «http://www.example.com/docs/errors/20003»}

Все эти status в body ответа дублирующие header это нарушение REST.

Используются ли cookie, и если та, то для чего?
Авторизация/аутентификация. OAuth, круто. Но как конкретно передаются токены (http header only? cookie? query string?)?
Не вижу особой проблемы в дублировании заголовков. В конце концов основная цель это удобство использования API, не всегда можно предусмотреть все варианты использования сервиса, такое дублирование может сделать его более гибким и расширяемым.
Cookies это, преймущественно, дело клиента который будет писаться под созданный API.
Токен от клиента обычно передается в header.
Я тоже не считаю это сильно критичным. Просто если уж говорить про REST как идеологию, то при трепетном отношение к ней нужно констатировать, что подобные практики — отступление от REST-а.
Предпринимались ли попытки использования range для постраничного вывода?
И вы бы с ценами на курсы определились что ли… Когда будут известны?
Подавайте заявку, Вам сообщат.
Можете подсказать по статусам для ошибок?
Какие статусы выдавать в случаях:
1. Ошибка пользователя, ее необходимо показать. Например «Вы ввели некорректную дату» или «форамат логина неверный»
2. Ошибка для developer'а. Например «Не передан параметер id. Без него зарезервировать билет невозможно»
В обоих случаях обычно используется статус «400 Bad Request» с соответствующим описанием необходимых для исправления действий
На мой взгляд отображение зависимостей между объектами не должно выполняться через построение иерархии объектов в URL, т.к. в дальнейшем это может привести к тому что объект будет доступен по нескольким уникальным URL, что в итоге противоречит принципам REST. И приводит к неприятным эффектам с кешированием.
Поясню на вашем же примере:
/api/users/ae43bc/cars/c7b45e

Допустим два пользователя могут иметь одну и ту же машину во владении (муж и жена) или более приземленный вариант разделяемый документ между двумя пользователями. Соответственно получаем один и тот же ресурс доступен по двум уникальным URL
/api/users/ae43bc/cars/c7b45e
/api/users/bc11aa/cars/c7b45e

/api/users/ae43bc/docs/c7b45e
/api/users/bc11aa/docs/c7b45e

В итоге клиенту придется кэшировать два объекта или анализировать содержимое сущности для оптимизации кэширования.

На мой взгляд более правильный метод реализации иерархии объектов как раз выполняется через принцип HATEOAS. Т.е. в указанном выше примере у обоих пользователей в содержимом сущности должна быть коллекция ссылок на машины которыми они владеют, e.g.:

{
    ...
    cars:[
        "/cars/c7b45e"
    ]
    ...
}
Sign up to leave a comment.