Понимание REST: глаголы, коды ошибок и аутентификация
=====================================================

Я ищу способ обернуть API-интерфейсы вокруг функций по умолчанию в моих веб-приложениях, базах данных и CMS на основе PHP.

Я огляделся и нашел несколько каркасов "скелета". В дополнение к ответам в моем вопросе, [Tonic](http://tonic.sourceforge.net/), мне нравится структура REST, потому что она очень легкая.

Мне нравится, что REST лучше всего подходит для его простоты, и хотел бы создать на нем архитектуру API. Я пытаюсь понять основные принципы и до сих пор не совсем понял. Поэтому ряд вопросов.

  **1. Я правильно понимаю это?**

Скажем, у меня есть ресурс "пользователи". Я мог бы настроить несколько URI, например:

```
/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

```

- это правильное представление архитектуры RESTful до сих пор?

  **2. Мне нужно больше глаголов**

Создать, обновить и удалить может быть достаточно в теории, но на практике у меня будет потребность в гораздо более глаголах. Я понимаю, что это те вещи, которые могут быть встроены в запрос на обновление, но они являются конкретными действиями, которые могут иметь определенные коды возврата, и я не хочу бросать их всех в одно действие.

Некоторые из них приходят на ум в примере пользователя:

```
activate_login
deactivate_login
change_password
add_credit

```

как я могу выразить такие действия, как в архитектуре URL RESTful?

Моим инстинктом было бы сделать GET-вызов для URL-адреса, например

```
/api/users/1/activate_login 

```

и ожидайте возврата кода состояния.

Это отклоняется от идеи использования HTTP-глаголов. Как вы думаете?

  **3. Как вернуть сообщения об ошибках и коды**

Большая часть красоты REST проистекает из использования стандартных HTTP-методов. При ошибке я выдаю заголовок с кодом состояния ошибки 3xx, 4xx или 5xx. Подробное описание ошибки, я могу использовать тело (правильно?). Все идет нормально. Но каким образом можно было бы передать  **собственный код ошибки**, который более подробно описывает, что пошло не так (например, "не удалось подключиться к базе данных" или "неверный вход в базу данных" )? Если я поместил его в тело вместе с сообщением, я должен разобрать его потом. Есть стандартный заголовок для такого рода вещей?

  **4. Как сделать аутентификацию**

* Как будет выглядеть аутентификация на основе API, основанная на принципах REST?
* Существуют ли сильные стороны против использования сеансов при аутентификации клиента REST, кроме того, что это вопиющее нарушение принципа REST?:) (только половина шуток здесь, аутентификация на основе сеанса будет хорошо работать с моей существующей инфраструктурой.)

 

Я заметил этот вопрос на пару дней позже, но я чувствую, что могу добавить некоторое понимание. Я надеюсь, что это может быть полезно для вашего предприятия RESTful.

---

  **Пункт 1: Я правильно понимаю?**

Вы правильно поняли. Это правильное представление архитектуры RESTful. Вы можете найти следующую матрицу из [Wikipedia](https://en.wikipedia.org/wiki/Representational_State_Transfer#Applied_to_web_services) очень полезную для определения ваших существительных и глаголов:

---

При работе с URI  **Collection**:  **`http://example.com/resources/`**

* **GET**. Перечислите членов коллекции с их URI-членами для дальнейшей навигации. Например, перечислите все автомобили для продажи.
* **PUT**. Значение определяется как "заменить всю коллекцию на другую коллекцию".
* **POST**. Создайте новую запись в коллекции, где идентификатор автоматически присваивается коллекцией. Созданный идентификатор обычно включается как часть данных, возвращаемых этой операцией.
* **УДАЛИТЬ**. Значение определяется как "удалить всю коллекцию".

---

При работе с  **Member** URI:  **`http://example.com/resources/7HOU57Y`**

* **GET**: получить представление адресата элемента коллекции, выраженного в соответствующем типе MIME.
* **PUT**. Обновите адресный элемент коллекции или создайте его с указанным ID.
* **POST**. Обрабатывает адресованный элемент как самостоятельную коллекцию и создает новый подчиненный.
* **УДАЛИТЬ**: удалить адресный элемент коллекции.

---

  **Точка 2: мне нужно больше глаголов**

В целом, когда вы думаете, что вам нужно больше глаголов, это может фактически означать, что ваши ресурсы должны быть повторно идентифицированы. Помните, что в REST вы всегда действуете на ресурсе или в наборе ресурсов. То, что вы выбрали в качестве ресурса, очень важно для вашего определения API.

  **Активировать/деактивировать вход**. Если вы создаете новый сеанс, вам может потребоваться рассмотреть "сеанс" в качестве ресурса. Чтобы создать новый сеанс, используйте POST для `http://example.com/sessions/` с учетными данными в теле. Для его окончания используйте PUT или DELETE (возможно, в зависимости от того, хотите ли вы сохранить историю сеансов) до `http://example.com/sessions/SESSION_ID`.

 (function(w, d, n, s, t) {
 w[n] = w[n] || [];
 w[n].push(function() {
 Ya.Context.AdvManager.render({
 blockId: "R-A-248011-3",
 renderTo: "yandex\_rtb\_R-A-248011-3",
 async: true
 });
 });
 t = d.getElementsByTagName("script")[0];
 s = d.createElement("script");
 s.src = "//an.yandex.ru/system/context.js";
 s.async = true;
 t.parentNode.insertBefore(s, t);
 })(this, this.document, "yandexContextAsyncCallbacks");

  **Сменить пароль:** На этот раз ресурс "пользователь". Вам понадобится PUT для `http://example.com/users/USER_ID` со старыми и новыми паролями в теле. Вы действуете на ресурсе пользователя, а пароль для изменения - это просто запрос на обновление. Это очень похоже на инструкцию UPDATE в реляционной базе данных.

>  Моим инстинктом было бы сделать звонок GET к URL-адресу, подобному `/api/users/1/activate_login`
> 
> 
> 

Это противоречит очень основному принципу REST: правильное использование HTTP-глаголов. Любой запрос GET никогда не должен оставлять побочный эффект.

Например, запрос GET никогда не должен создавать сеанс в базе данных, возвращать файл cookie с новым идентификатором сеанса или оставлять остатки на сервере. Глагол GET похож на оператор SELECT в движке базы данных. Помните, что ответ на любой запрос с помощью GET-глагола должен быть кэширован при запросе с теми же параметрами, как и при запросе статической веб-страницы.

---

  **Пункт 3: Как вернуть сообщения об ошибках и коды**

Учитывайте коды статуса 4xx или 5xx HTTP как категории ошибок. Вы можете разработать ошибку в теле.

  **Не удалось подключиться к базе данных:**/ **Неправильный вход в базу данных**. Обычно вы должны использовать ошибку 500 для этих типов ошибок. Это ошибка на стороне сервера. Клиент не сделал ничего плохого. 500 ошибок обычно считаются "повторимыми". то есть клиент может повторить один и тот же точный запрос и ожидать, что он будет успешным после устранения проблем с сервером. Укажите детали в теле, чтобы клиент мог предоставить нам некоторый контекст.

 Другая категория ошибок - это семейство 4xx, что в целом указывает на то, что клиент сделал что-то неправильно. В частности, эта категория ошибок обычно указывает клиенту, что нет необходимости повторять запрос как есть, потому что он будет продолжать терпеть неудачу навсегда. то есть клиент должен что-то изменить, прежде чем повторять этот запрос. Например, в эту категорию попадают ошибки "Ресурс не найден" (HTTP 404) или "Ошибочный запрос" (HTTP 400).

---

  **Пункт 4: Как сделать аутентификацию**

Как указано в пункте 1, вместо аутентификации пользователя вы можете подумать о создании сеанса. Вам будет отправлен новый "Идентификатор сеанса" вместе с соответствующим кодом состояния HTTP (200: Доступ разрешен или 403: Отказано в доступе).

Затем вы спросите свой сервер RESTful: "Можете ли вы получить ресурс для этого идентификатора сеанса?".

Нет аутентифицированного режима. REST не имеет статуса: вы создаете сеанс, вы запрашиваете у сервера ресурсы, используя этот идентификатор сеанса в качестве параметра, а при выходе из системы вы прекращаете или завершаете сеанс.

 

Проще говоря, вы делаете это полностью назад.

Вы не должны приближаться к этому из того, какие URL-адреса вы должны использовать. URL-адреса будут эффективно "бесплатными" после того, как вы решите, какие ресурсы необходимы для вашей системы, и как вы будете представлять эти ресурсы, а также взаимодействия между ресурсами и состоянием приложения.

Чтобы процитировать [Рой Филдинг](http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)

>  API REST должен тратить почти все его описательное усилие в определении тип мультимедиа, используемые для представления ресурсов и приложения для вождения состояния или определения расширенных имена отношений и/или гипертекстовая разметка для существующих стандартные типы носителей. Любые усилия описание способов использования URI, представляющие интерес, должны быть полностью определенных в рамках правила обработки для типа носителя (и, в большинстве случаев, уже определены существующими типами носителей). [Отказ здесь подразумевается, что внеполосный информация ведет к взаимодействию вместо гипертекста.]
> 
> 
> 

Люди всегда начинают с URI и считают, что это решение, и затем они склонны пропустить ключевую концепцию в архитектуре REST, в частности, как указано выше: "Ошибка здесь означает, что внеполосная информация ведет к взаимодействию вместо этого гипертекста." 

Честно говоря, многие видят кучу URI и некоторые GET, PUT и POST и считают, что REST легко. REST непросто. RPC через HTTP легко, перемещение блоков данных назад и вперед проксимируется через полезные нагрузки HTTP. REST, однако, выходит за рамки этого. REST является протокольным агностиком. HTTP очень популярен и подходит для систем REST.

REST живет в типах мультимедиа, их определениях и о том, как приложение управляет действиями для этих ресурсов через гипертекст (ссылки, эффективно).

В системах REST существует различное представление о типах носителей. Некоторые из них предпочитают специальные приложения, в то время как другим нравится поднимать существующие типы медиа в роли, подходящие для приложения. Например, с одной стороны, у вас есть определенные XML-схемы, разработанные для вашего приложения, вместо того, чтобы использовать что-то вроде XHTML в качестве вашего представления, возможно, через микроформаты и другие механизмы.

Оба подхода имеют свое место, я думаю, что XHTML работает очень хорошо в сценариях, которые перекрывают как управляемую человеком, так и машину, управляемую машиной, тогда как первые более конкретные типы данных, которые я чувствую, лучше облегчают взаимодействие машины с машиной. Я считаю, что повышение товарных форматов может затруднить согласование контента. "application/xml + yourresource" гораздо более специфичен в качестве типа носителя, чем "application/xhtml + xml", поскольку последний может применяться ко многим полезным нагрузкам, которые могут быть или не быть чем-то, чего действительно интересует машинный клиент, и не может определять без интроспекции.

Тем не менее, XHTML работает очень хорошо (очевидно) в веб-сети, где веб-браузеры и рендеринг очень важны.

Ваше приложение поможет вам в таких решениях.

Часть процесса проектирования системы REST - это открытие ресурсов первого класса в вашей системе вместе с производными ресурсами поддержки, необходимыми для поддержки операций с основными ресурсами. Как только ресурсы будут обнаружены, то представление этих ресурсов, а также диаграммы состояний, показывающие поток ресурсов через гипертекст в представлениях, потому что следующая задача.

Напомним, что каждое представление ресурса в гипертекстовой системе объединяет как фактическое представление ресурсов, так и переходы состояния, доступные для ресурса. Рассмотрим каждый ресурс a node в графе, причем ссылки являются линиями, оставляющими это node для других состояний. Эти ссылки информируют клиентов не только о том, что можно сделать, но и того, что требуется для них (как хорошая ссылка объединяет URI и требуемый тип носителя).

 (function(w, d, n, s, t) {
 w[n] = w[n] || [];
 w[n].push(function() {
 Ya.Context.AdvManager.render({
 blockId: "R-A-248011-4",
 renderTo: "yandex\_rtb\_R-A-248011-4",
 async: true
 });
 });
 t = d.getElementsByTagName("script")[0];
 s = d.createElement("script");
 s.src = "//an.yandex.ru/system/context.js";
 s.async = true;
 t.parentNode.insertBefore(s, t);
 })(this, this.document, "yandexContextAsyncCallbacks");

Например, у вас может быть:

```
<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

```

В вашей документации будет указано поле rel с именем "пользователи" и тип носителя "application/xml + youruser".

Эти ссылки могут показаться излишними, они все общаются с тем же URI, в значительной степени. Но это не так.

Это связано с тем, что для отношения "пользователи" эта ссылка относится к коллекции пользователей, и вы можете использовать единый интерфейс для работы с коллекцией (GET для извлечения всех из них, DELETE, чтобы удалить все из них, и др.)

 Если вы отправляете POST на этот URL-адрес, вам нужно будет передать документ "application/xml + usercollection", который, вероятно, будет содержать только один экземпляр пользователя в документе, чтобы вы могли добавить пользователя или нет, возможно, добавьте несколько сразу. Возможно, ваша документация предполагает, что вы можете просто передать один тип пользователя вместо коллекции.

Вы можете посмотреть, что требуется приложению для выполнения поиска, как определено ссылкой "поиск" и его медиатипом. Документация для типа носителя поиска сообщит вам, как это происходит, и что ожидать в результате.

Вывод здесь, однако, является самими URI, в основном несущественными. Приложение контролирует URI, а не клиенты. Помимо нескольких "точек входа", ваши клиенты должны полагаться на URI, предоставленные приложением для его работы.

Клиент должен знать, как манипулировать и интерпретировать типы медиа, но ему не нужно заботиться о том, куда он идет.

Эти две ссылки семантически идентичны в глазах клиентов:

```
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

```

Итак, сосредоточьтесь на своих ресурсах. Сосредоточьтесь на своих переходах состояния в приложении и как это наилучшим образом достигнуто.

 

  **re 1**: Пока это прекрасно. Не забудьте вернуть URI вновь созданного пользователя в заголовок "Location:" как часть ответа на POST вместе с кодом статуса "201 Created".

  **re 2**: Активация через GET - плохая идея, и в том числе глагол в URI - это дизайнерский запах. Возможно, вам захочется вернуться к форме в GET. В веб-приложении это будет форма HTML с кнопкой отправки; в случае использования API вы можете захотеть вернуть представление, содержащее URI для PUT, чтобы активировать учетную запись. Конечно, вы можете включить этот URI в ответ на POST для пользователей. Использование PUT гарантирует, что ваш запрос является идемпотентным, то есть его можно безопасно отправить снова, если клиент не уверен в успехе. В общем, подумайте о том, какие ресурсы вы можете превратить в свои глаголы (вроде "вымывание глаголов" ). Спросите себя, с каким методом ваши конкретные действия наиболее тесно связаны. Например. change\_password → PUT; деактивировать → возможно DELETE; add\_credit → возможно POST или PUT. Направьте клиента на соответствующие URI, включив их в свои представления.

  **re 3.** Не придумывайте новые коды статуса, если вы не считаете их такими универсальными, что они заслуживают стандартизации во всем мире. Постарайтесь использовать наиболее подходящий код состояния (читайте обо всех них в RFC 2616). Включите дополнительную информацию в орган ответа. Если вы действительно уверены, что хотите придумать новый код состояния, подумайте еще раз; если вы все еще верите, убедитесь, что по крайней мере выберите нужную категорию (1xx → OK, 2xx → информационное, 3xx → перенаправление, ошибка 4xx- > клиента, ошибка 5xx → сервер). Я упоминал, что изобретать новые коды статуса - это плохая идея?

  **re 4.** Если это возможно, используйте структуру проверки подлинности, встроенную в HTTP. Проверьте способ аутентификации Google в GData. В общем, не добавляйте ключи API в свои URI. Старайтесь избегать сеансов для повышения масштабируемости и поддержки кеширования - если ответ на запрос отличается из-за того, что произошло раньше, вы обычно привязывались к определенному экземпляру сервера. Гораздо лучше включить состояние сеанса в состояние клиента (например, сделать его частью последующих запросов) или сделать его явным, превратив его в состояние ресурса (сервера), т.е. Предоставить ему свой собственный URI.

 

  **1.** У вас есть правильная идея о том, как создавать свои ресурсы, ИМХО. Я ничего не изменил бы.

  **2.** Вместо того, чтобы пытаться расширить HTTP с большим количеством глаголов, рассмотрите, какие предлагаемые глаголы можно свести к базовым методам и ресурсам HTTP. Например, вместо глагола `activate_login` вы можете настроить такие ресурсы, как: `/api/users/1/login/active`, который является простым булевым. Чтобы активировать логин, просто `PUT` документ там, где указано "true" или 1 или что-то еще. Чтобы деактивировать, `PUT` документ там пустой или говорит 0 или false.

Аналогично, чтобы изменить или установить пароли, просто от `PUT` до `/api/users/1/password`.

Всякий раз, когда вам нужно что-то добавить (например, кредит), подумайте в терминах `POST` s. Например, вы можете сделать `POST` для ресурса, такого как `/api/users/1/credits`, с телом, содержащим количество добавляемых кредитов. A `PUT` на том же ресурсе может использоваться для перезаписывания значения, а не для добавления. A `POST` с отрицательным числом в теле будет вычитать и т.д.

  **3.** Я бы настоятельно советовал не распространять базовые коды состояния HTTP. Если вы не можете найти тот, который точно соответствует вашей ситуации, выберите ближайший и поместите данные об ошибке в тело ответа. Кроме того, помните, что HTTP-заголовки расширяемы; ваше приложение может определять все пользовательские заголовки, которые вам нравятся. Например, одно приложение, к которому я работал, может вернуть `404 Not Found` при нескольких обстоятельствах. Вместо того, чтобы заставить клиент проанализировать тело ответа по этой причине, мы просто добавили новый заголовок `X-Status-Extended`, который содержал наши проприетарные расширения кода состояния. Таким образом, вы можете увидеть ответ вроде:

```
HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

```

Таким образом, HTTP-клиент, такой как веб-браузер, все равно будет знать, что делать с обычным кодом 404, а более сложный HTTP-клиент может выбрать заголовок `X-Status-Extended` для получения более подробной информации.

  **4.** Для аутентификации я рекомендую использовать HTTP-аутентификацию, если это возможно. Но IMHO нет ничего плохого в использовании аутентификации на основе файлов cookie, если вам это проще.

 

В приведенных примерах я бы использовал следующее:

activate\_login

 `POST /users/1/activation`

deactivate\_login

 `DELETE /users/1/activation`

change\_password

 `PUT /passwords` (это предполагает, что пользователь аутентифицирован)

add\_credit

 `POST /credits` (это предполагает, что пользователь аутентифицирован)

Для ошибок вы должны вернуть ошибку в теле в том формате, в котором вы получили запрос, поэтому, если вы получаете:

 `DELETE /users/1.xml`

Вы отправите ответ обратно в XML, то же самое будет верно для JSON и т.д.

Для аутентификации вы должны использовать http-аутентификацию.

 

 Основы REST
============

REST имеет унифицированное ограничение интерфейса, в котором говорится, что клиент REST должен полагаться на стандарты, а не на конкретные данные конкретной службы REST, поэтому клиент REST не будет прерываться незначительными изменениями и, вероятно, будет повторно использоваться.

Таким образом, существует договор между клиентом REST и службой REST. Если вы используете HTTP в качестве базового протокола, то следующие условия являются частью контракта:

* [HTTP 1.1](https://www.w3.org/Protocols/rfc2616/rfc2616.html)
	+ определения методов
	+ определения кода состояния
	+ заголовки управления кэшем
	Заголовки
	+ accept и content-type
	+ заголовки auth
* [IRI](https://www.ietf.org/rfc/rfc3987.txt) (utf8 [URI](https://tools.ietf.org/html/rfc3986))
* тело (выберите один)
	+ зарегистрированный специфичный для приложения тип MIME, например. [лабиринт + xml](http://amundsen.com/media-types/maze/)
	+ тип MIME поставщика, например. [vnd.github + json](https://developer.github.com/v3/media/)
	+ общий тип MIME с
		- специфический для приложения RDF vocab, например. [ld + json](http://json-ld.org/) и [hydra](http://www.markus-lanthaler.com/hydra/), [schema.org](https://schema.org/)
		- конкретный профиль приложения, например. [hal + json](http://stateless.co/hal_specification.html) и параметр профиля профиля (я думаю)
* гиперссылок
	+ что должно содержать их (выберите один)
		- отправка [заголовков ссылок](https://tools.ietf.org/html/rfc5988)
		- отправка ответа гипермедиа, например. html, atom + xml, hal + json, ld + json & hydra и т.д.
	+ семантика
		- использовать отношения ссылок IANA и, возможно, настраиваемые отношения ссылок
		- использовать специфический для приложения RDF vocab

У REST есть ограничение без состояния, которое заявляет, что связь между службой REST и клиентом должна быть неактивной. Это означает, что служба REST не может поддерживать состояния клиента, поэтому вы не можете иметь хранилище сеансов на стороне сервера. Вы должны аутентифицировать каждый запрос. Так, например, HTTP basic auth (часть стандарта HTTP) в порядке, потому что он отправляет имя пользователя и пароль с каждым запросом.

 Чтобы ответить на ваши вопросы
===============================

* Да, это может быть.

Как раз упомянуть, клиенты не заботятся о структуре IRI, они заботятся о семантике, потому что они следуют за ссылками, связанными с отношениями ссылок или связанными данными (RDF).

Единственное, что важно для IRI, заключается в том, что один IRI должен идентифицировать только один ресурс. Каждому ресурсу, как и пользователю, разрешено иметь много разных IRI.

Это довольно просто, почему мы используем интересные IRI, такие как `/users/123/password`; гораздо проще написать логику маршрутизации на сервере, когда вы понимаете IRI, просто прочитав ее.
* У вас больше глаголов, таких как PUT, PATCH, OPTIONS и даже больше, но вам не нужно больше их... Вместо добавления новых глаголов вам нужно научиться добавлять новые ресурсы.

 `activate_login -> PUT /login/active true
deactivate_login -> PUT /login/active false
change_password -> PUT /user/xy/password "newpass"
add_credit -> POST /credit/raise {details: {}}`

(Вход в систему не имеет смысла с точки зрения REST из-за ограничения без сохранения.)
* Ваши пользователи не заботятся о том, почему проблема существует. Они хотят знать только, если есть успех или ошибка, и, вероятно, сообщение об ошибке, которое они могут понять, например: "Извините, но мы не смогли сохранить ваше сообщение" и т.д.

Заголовки статуса HTTP являются стандартными заголовками. Все остальное должно быть в теле, которое я думаю. Одного заголовка недостаточно, чтобы описать, например, подробные многоязычные сообщения об ошибках.
* Ограничение без сохранения (вместе с ограничениями на кеш и многоуровневые системы) гарантирует, что служба масштабируется. Вы, конечно, не хотите поддерживать миллионы сеансов на сервере, когда вы можете сделать то же самое на клиентах.

Сторонний клиент получает токен доступа, если пользователь предоставляет ему доступ с помощью основного клиента. После этого сторонний клиент отправляет токен доступа с каждым запросом. Существуют более сложные решения, например, вы можете подписывать каждый отдельный запрос и т.д. Подробнее см. Руководство OAuth.

 Связанная литература
=====================

* [Архитектурные стили и дизайн сетевых архитектурных приложений](https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)   

Диссертация Рой Томас Филдинг (автор REST)   

2000, Калифорнийский университет, Ирвин
* [Веб-интерфейсы третьего поколения - устранение разрыва между REST и связанными данными](http://www.markus-lanthaler.com/research/third-generation-web-apis-bridging-the-gap-between-rest-and-linked-data.pdf)   

Диссертация Маркус Лантхалер (соавтор JSON-LD и автор Hydra)   

2014, Технологический университет Граца, Австрия.

 

* Используйте пост, когда вы не знаете, как будет выглядеть новый URI ресурса (вы создаете нового пользователя, приложение назначит нового пользователя его идентификатор), PUT для обновления или создания ресурсов, которые вы знаете, как они будут (пример: PUT/myfiles/thisismynewfile.txt)
* возвращает описание ошибки в теле сообщения
* Вы можете использовать HTTP-аутентификацию (если это достаточно)
Веб-сервисы должны быть stateles

 

Я бы предложил (в качестве первого прохода), что `PUT` должен использоваться только для обновления существующих объектов. `POST` следует использовать для создания новых. то есть.

```
/api/users     when called with PUT, creates user record

```

мне не нравится. Остальная часть вашего первого раздела (использование re-verb) выглядит логично.

 

Подробный, но скопированный из спецификации метода HTTP 1.1 в [http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)

 9.3 GET
--------

Метод GET означает получение любой информации (в форме объекта), идентифицируемой Request-URI. Если Request-URI ссылается на процесс создания данных, то полученные данные должны быть возвращены в качестве объекта в ответе, а не в исходном тексте процесса, если только этот текст не является результатом процесса.

Семантика метода GET изменяется на "условное GET", если сообщение запроса включает в себя поле If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match или If-Range, Условный метод GET запрашивает передачу объекта только при обстоятельствах, описанных условным полем заголовков. Условный метод GET предназначен для уменьшения ненужного использования сети, позволяя обновлять кэшированные объекты, не требуя нескольких запросов или передачи данных, уже сохраненных клиентом.

Семантика метода GET изменяется на "частичное GET", если сообщение запроса включает поле заголовка Range. Частичные запросы GET передают только часть объекта, как описано в разделе 14.35. Частичный метод GET предназначен для уменьшения ненужного использования сети, позволяя завершить частично восстановленные объекты без переноса данных, уже находящихся у клиента.

Ответ на запрос GET можно кэшировать тогда и только тогда, когда он отвечает требованиям кэширования HTTP, описанным в разделе 13.

См. раздел 15.1.3 для соображений безопасности при использовании для форм.

 9.5 POST
---------

Метод POST используется для запроса, чтобы исходный сервер принял объект, заключенный в запросе, в качестве нового подчиненного ресурса, идентифицированного Request-URI в строке запроса. POST предназначен для обеспечения единообразного метода для покрытия следующих функций:

```
  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

```

Фактическая функция, выполняемая методом POST, определяется сервером и обычно зависит от Request-URI. Опубликованный объект подчинен этому URI таким же образом, что файл подчинен каталогу, содержащему его, новостная статья подчинена группе новостей, на которую она отправлена, или запись подчинена базе данных.

Действие, выполняемое методом POST, может не привести к ресурсу, который может быть идентифицирован с помощью URI. В этом случае либо 200 (OK), либо 204 (No Content) - это соответствующий статус ответа, в зависимости от того, включает ли ответ объект, который описывает результат.

Если ресурс был создан на исходном сервере, ответ должен быть равен 201 (создан) и содержать объект, который описывает статус запроса и ссылается на новый ресурс, и заголовок Location (см. раздел 14.30).

Ответы на этот метод не могут быть кэшируемыми, если только ответ не включает соответствующие поля заголовка Cache-Control или Expires. Тем не менее, ответ 303 (см. Раздел "Другие" ) можно использовать для направления агента пользователя для получения кэшируемого ресурса.

Запросы POST ДОЛЖНЫ подчиняться требованиям к передаче сообщений, изложенным в разделе 8.2.

См. раздел 15.1.3 для соображений безопасности.

 9.6 PUT
--------

Метод PUT запрашивает, чтобы закрытый объект хранился в запрошенном Request-URI. Если Request-URI ссылается на уже существующий ресурс, закрытый объект СЛЕДУЕТ считаться модифицированной версией той, которая находится на исходном сервере. Если Request-URI не указывает на существующий ресурс и что URI может быть определен как новый ресурс запрашивающим пользовательским агентом, исходный сервер может создать ресурс с этим URI. Если новый ресурс создан, исходный сервер ДОЛЖЕН информировать пользовательский агент через ответ 201 (создан). Если существующий ресурс изменен, коды ответов 200 (OK) или 204 (Нет содержимого) ДОЛЖНЫ быть отправлены, чтобы указать успешное завершение запроса. Если ресурс не может быть создан или изменен с помощью Request-URI, необходимо дать соответствующий ответ об ошибке, который отражает характер проблемы. Получатель объекта НЕ ДОЛЖЕН игнорировать заголовки Content- * (например, Content-Range), которые он не понимает и не реализует, и ДОЛЖЕН вернуть ответ 501 (не реализованный) в таких случаях.

 Если запрос проходит через кэш, а Request-URI идентифицирует один или несколько кэшированных объектов в настоящее время, эти записи ДОЛЖНЫ считаться устаревшими. Ответы на этот метод не подлежат кэшированию.

Основное различие между запросами POST и PUT отражается в различном значении Request-URI. URI в запросе POST идентифицирует ресурс, который будет обрабатывать заключенный объект. Этот ресурс может быть процессом принятия данных, шлюзом к другому протоколу или отдельным объектом, который принимает аннотации. Напротив, URI в запросе PUT идентифицирует объект, заключенный с запросом - пользовательский агент знает, что такое URI, и сервер НЕ ДОЛЖЕН пытаться применить запрос к другому ресурсу. Если сервер желает, чтобы запрос был применен к другому URI,

он ДОЛЖЕН отправить 301 (перемещенный постоянный) ответ; пользовательский агент МОЖЕТ затем принять собственное решение относительно перенаправления запроса.

Один ресурс МОЖЕТ быть идентифицирован многими различными URI. Например, статья может иметь URI для идентификации "текущей версии", которая отделена от URI, идентифицирующего каждую конкретную версию. В этом случае запрос PUT на общем URI может привести к тому, что несколько других URI будут определены сервером происхождения.

HTTP/1.1 не определяет, как метод PUT влияет на состояние исходного сервера.

Запросы PUT ДОЛЖНЫ подчиняться требованиям к передаче сообщений, изложенным в разделе 8.2.

Если не указано иное для конкретного заголовка объекта, заголовки сущностей в запросе PUT ДОЛЖНЫ применяться к ресурсу, созданному или измененному PUT.

 9.7 УДАЛИТЬ
------------

Метод DELETE запрашивает, чтобы исходный сервер удалял ресурс, идентифицированный Request-URI. Этот метод МОЖЕТ быть переопределен вмешательством человека (или другими средствами) на исходном сервере. Клиент не может гарантировать, что операция выполнена, даже если код состояния, возвращенный с исходного сервера, указывает, что действие выполнено успешно. Тем не менее, сервер НЕ ДОЛЖЕН указывать на успех, если в момент ответа ответа он не захочет удалить ресурс или переместить его в недоступное место.

Успешный ответ ДОЛЖЕН быть 200 (ОК), если ответ включает объект, описывающий статус, 202 (Принято), если действие еще не было принято, или 204 (Нет содержимого), если действие было принято, но ответ не включает объект.

Если запрос проходит через кэш, а Request-URI идентифицирует один или несколько кэшированных объектов в настоящее время, эти записи ДОЛЖНЫ считаться устаревшими. Ответы на этот метод не подлежат кэшированию.

 

О кодах возврата REST:  **неверно**, чтобы смешивать коды протокола HTTP и результаты REST.

Однако, я видел много реализаций, смешивающих их, и многие разработчики могут не согласиться со мной.

Коды возврата HTTP связаны с самим `HTTP Request`. Вызов REST выполняется с использованием запроса протокола Hypertext Transfer Protocol и работает на более низком уровне, чем сам метод REST. REST - это концепция/подход, а его вывод - бизнес-логический результат, а HTTP-код результата - транспортный.

Например, если вы вызываете /users/, вы возвращаете "404 Not found", потому что это может означать:

* URI ошибочен (HTTP)
* Пользователи не найдены (REST) 

 "403 Запрещено/Доступ запрещен" может означать:

* Требуется специальное разрешение. Браузеры могут справиться с этим, попросив пользователя/пароль. (HTTP)
* Неверные разрешения доступа, настроенные на сервере. (HTTP)
* Вам необходимо пройти аутентификацию (REST) 

И список может продолжаться с ошибкой "500 Server" (ошибка HTTP-атаки Apache/Nginx или ошибка бизнес-ограничения в REST) или другие ошибки HTTP и т.д....

Из кода трудно понять, в чем причина сбоя, сбой HTTP (транспортный) или отказ REST (логический).

Если запрос HTTP физически был успешно выполнен, он должен всегда возвращать код 200, независимо от того, найдена запись или нет. Поскольку ресурс URI найден и обрабатывается сервером http. Да, он может вернуть пустой набор. Можно ли получить пустую веб-страницу с 200 в качестве результата http, правильно?

Вместо этого вы можете вернуть 200 HTTP-код и просто JSON с пустым массивом/объектом или использовать флаг bool result/success, чтобы сообщить о выполненном состоянии операции.

Кроме того, некоторые интернет-провайдеры могут перехватывать ваши запросы и возвращать вам 404 http-код. Это не означает, что ваши данные не найдены, но что-то не так на транспортном уровне.

От [Wiki](https://en.wikipedia.org/wiki/HTTP_404#Soft_404_Errors):

>  В июле 2004 года британская телекоммуникационная компания BT Group развернула Cleanfeed система блокирования контента, которая возвращает ошибку 404 для любого запроса для содержание, идентифицированное как потенциально незаконное с помощью Internet Watch Фонд. Другие провайдеры возвращают HTTP 403 "запретную" ошибку в том же обстоятельства. Практика использования поддельных ошибок 404 в качестве средства для в Таиланде и Тунисе также сообщается о скрытой цензуре. В Тунис, где цензура была серьезной до революции 2011 года, люди узнали о природе поддельных ошибок 404 и создали воображаемый персонаж под названием "Аммар 404", который представляет "невидимый цензор".
> 
> 
>