Типы HTTP-запросов и философия REST / Хабр

Этот пост — ответ на вопрос, заданный в комментарии к одной из моих статей.

В статье я хочу рассказать, что же из себя представляют HTTP-методы GET/POST/PUT/DELETE и другие, для чего они были придуманы и как их использовать в соответствии с REST.

Итак, что же представляет из себя один из основных протоколов интернета? Педантов отправлю к RFC2616, а остальным расскажу по-человечески 🙂

Этот протокол описывает взаимодействие между двумя компьютерами (клиентом и сервером), построенное на базе сообщений, называемых запрос (Request) и ответ (Response). Каждое сообщение состоит из трех частей: стартовая строка, заголовки и тело. При этом обязательной является только стартовая строка.

Стартовые строки для запроса и ответа имеют различный формат — нам интересна только стартовая строка запроса, которая выглядит так:

METHOD URI HTTP/VERSION,

где METHOD — это как раз метод HTTP-запроса, URI — идентификатор ресурса, VERSION — версия протокола (на данный момент актуальна версия 1. 1).

Заголовки — это набор пар имя-значение, разделенных двоеточием. В заголовках передается различная служебная информация: кодировка сообщения, название и версия браузера, адрес, с которого пришел клиент (Referrer) и так далее.

Тело сообщения — это, собственно, передаваемые данные. В ответе передаваемыми данными, как правило, является html-страница, которую запросил браузер, а в запросе, например, в теле сообщения передается содержимое файлов, загружаемых на сервер. Но как правило, тело сообщения в запросе вообще отсутствует.

Рассмотрим пример.

Запрос:

GET /index.php HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5
Accept: text/html
Connection: close

Первая строка — это строка запроса, остальные — заголовки; тело сообщения отсутствует

Ответ:

HTTP/1.0 200 OK
Server: nginx/0.6.31
Content-Language: ru
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Connection: close
. .. САМА HTML-СТРАНИЦА ...

Вернемся к стартовой строке запроса и вспомним, что в ней присутствует такой параметр, как URI. Это расшифровывается, как Uniform Resource Identifier — единообразный идентификатор ресурса. Ресурс — это, как правило, файл на сервере (пример URI в данном случае ‘/styles.css’), но вообще ресурсом может являться и какой-либо абстрактный объект (‘/blogs/webdev/’ — указывает на блок «Веб-разработка», а не на конкретный файл).

Тип HTTP-запроса (также называемый HTTP-метод) указывает серверу на то, какое действие мы хотим произвести с ресурсом. Изначально (в начале 90-х) предполагалось, что клиент может хотеть от ресурса только одно — получить его, однако сейчас по протоколу HTTP можно создавать посты, редактировать профиль, удалять сообщения и многое другое. И эти действия сложно объединить термином «получение».

Для разграничения действий с ресурсами на уровне HTTP-методов и были придуманы следующие варианты:

  • GET — получение ресурса
  • POST — создание ресурса
  • PUT — обновление ресурса
  • DELETE — удаление ресурса

Обратите внимание на тот факт, что спецификация HTTP не обязывает сервер понимать все методы (которых на самом деле гораздо больше, чем 4) — обязателен только GET, а также не указывает серверу, что он должен делать при получении запроса с тем или иным методом.

А это значит, что сервер в ответ на запрос DELETE /index.php HTTP/1.1 не обязан удалять страницу index.php на сервере, так же как на запрос GET /index.php HTTP/1.1 не обязан возвращать вам страницу index.php, он может ее удалять, например 🙂

REST (REpresentational State Transfer) — это термин был введен в 2000-м году Роем Филдингом (Roy Fielding) — одним из разработчиков протокола HTTP — в качестве названия группы принципов построения веб-приложений. Вообще REST охватывает более широкую область, нежели HTTP — его можно применять и в других сетях с другими протоколами. REST описывает принципы взаимодействия клиента и сервера, основанные на понятиях «ресурса» и «глагола» (можно понимать их как подлежащее и сказуемое). В случае HTTP ресурс определяется своим URI, а глагол — это HTTP-метод.

REST предлагает отказаться от использования одинаковых URI для разных ресурсов (то есть адреса двух разных статей вроде /index. php?article_id=10 и /index.php?article_id=20 — это не REST-way) и использовать разные HTTP-методы для разных действий. То есть веб-приложение, написанное с использованием REST подхода будет удалять ресурс при обращении к нему с HTTP-методом DELETE (разумеется, это не значит, что надо давать возможность удалить всё и вся, но

любой запрос на удаление в приложении должен использовать HTTP-метод DELETE).

REST дает программистам возможность писать стандартизованные и чуть более красивые веб-приложения, чем раньше. Используя REST, URI для добавления нового юзера будет не /user.php?action=create (метод GET/POST), а просто /user.php (метод строго POST).

В итоге, совместив имеющуюся спецификацию HTTP и REST-подход наконец-то обретают смысл различные HTTP-методы. GET — возвращает ресурс, POST — создает новый, PUT — обновляет существующий, DELETE — удаляет.

Да, есть небольшая проблема с применением REST на практике. Проблема эта называется HTML.

PUT/DELETE запросы можно отправлять посредством XMLHttpRequest, посредством обращения к серверу «вручную» (скажем, через curl или даже через telnet), но нельзя сделать HTML-форму, отправляющую полноценный PUT/DELETE-запрос.

Дело в том, спецификация HTML не позволяет создавать формы, отправляющие данные иначе, чем через GET или POST. Поэтому для нормальной работы с другими методами приходится имитировать их искусственно. Например, в Rack (механизм, на базе которого Ruby взаимодействует с веб-сервером; с применением Rack сделаны Rails, Merb и другие Ruby-фреймворки) в форму можно добавить hidden-поле с именем «_method», а в качестве значения указать название метода (например, «PUT») — в этом случае будет отправлен POST-запрос, но Rack сможет сделать вид, что получил PUT, а не POST.

Методы HTTP запроса — W3Docs

❮ Назад Вперед ❯

HTTP расшифровывается как HyperText Transfer Protocol, «протокол передачи гипертекста». Изначально этот протокол использовался для передачи гипертекстовых документов в формате HTML. Сегодня он используется для передачи произвольных данных.

В основе HTTP — клиент-серверная структура передачи данных․ Клиент формирует запрос (request) и отправляет на сервер; на сервере запрос обрабатывается, формируется ответ (response) и передается клиенту.

HTTP не шифрует передаваемую информацию. Для защиты передаваемых данных используется расширение HTTPS (Hyper Text Transfer Protocol Secure), которое “упаковывает” передаваемые данные в криптографический протокол SSL или TLS.

HTTP запрос состоит из трех основных частей: строка запроса (request line), заголовок (message header) и тело сообщения (entity body). Тело сообщения не является обязательным параметром. Между заголовком и телом есть пустая разделительная строка.

В строке запроса указывается метод передачи, версия протокола HTTP и URL, к которому должен обратиться сервер. Заголовки содержат тело сообщения, передаваемые параметры и другие сведения.

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

Для того, чтобы указать серверу на то, какое действие мы хотим произвести с ресурсом, используется тип HTTP-запроса, который также называется HTTP метод. Существует несколько HTTP методов, которые описывают действия с ресурсами. Наиболее часто используемыми являются GET и POST.

Метод GET запрашивает информацию из указанного источника и не влияет на его содержимое. Запрос доступен для кеширования данных и добавления в закладки. Длина запроса ограничена (макс. длина URL — 2048).

Примечание: Строка запроса (имя/значение) отправляется в URL

/login-check.php?argument1=value1&argument2=value2

Синтакс кода

<form  method="get">
     First name: <input type="text" placeholder="Your name"><br/>
     <input type="submit" value="Submit">
</form>

Метод POST

Метод POST используется для отправки данных, что может оказывать влияние на содержимое ресурса. В отличие от метода GET запросы POST не могут быть кешированы, они не остаются в истории браузера и их нельзя добавить в закладки. Запросы POST не ограничиваются в объеме.

Примечание: Отправляемые данные содержатся в теле запроса.

POST /login-check.php HTTP/1.1
Host: w3docs.com
argument1=value1&argument2=value2

Синтакс кода

<form  method="post">
     First name: <input type="text" placeholder="Your name"><br/>
     <input type="submit" value="Submit">
</form>

Сравнение методов GET и POST

Кроме методов GET и POST methods используются другие методы, которые описывают действия с ресурсами.

Таблица кодов состояния HTTP

ОПЦИИ — HTTP | MDN

Метод HTTP OPTIONS запрашивает разрешенные параметры связи для данного URL-адреса или сервера. С помощью этого метода клиент может указать URL-адрес или звездочку ( * ) для ссылки на весь сервер.

Запрос имеет тело
Успешный ответ имеет тело Да
Сейф Да
Идемпотент Да
Кэшируемый
Разрешено в формах HTML
 ВАРИАНТЫ /index.html HTTP/1.1
ВАРИАНТЫ * HTTP/1.1
 

Идентификация разрешенных методов запроса

Чтобы узнать, какие методы запроса поддерживает сервер, можно использовать программу командной строки curl для выполнения запроса OPTIONS :

 curl -X ВАРИАНТЫ https://example.org -i
 

Затем ответ содержит заголовок Allow , содержащий разрешенные методы:

 HTTP/1.1 204 Нет содержимого
Разрешить: OPTIONS, GET, HEAD, POST
Кэш-контроль: max-age=604800
Дата: Чт, 13 октября 2016 г. , 11:45:00 по Гринвичу
Сервер: EOS (lax004/2813)
 

Предварительные запросы в CORS

В CORS предварительный запрос отправляется с помощью метода OPTIONS , чтобы сервер мог ответить, допустимо ли отправлять запрос. В этом примере мы запросим разрешение на следующие параметры:

  • Заголовок Access-Control-Request-Method , отправленный в предварительном запросе, сообщает серверу, что при отправке фактического запроса он будет иметь метод запроса POST .
  • Заголовок Access-Control-Request-Headers сообщает серверу, что при отправке фактического запроса он будет иметь заголовки X-PINGOTHER и Content-Type .
 ВАРИАНТЫ /resources/post-here/ HTTP/1.1
Хост: bar.example
Принять: текст/html, приложение/xhtml+xml, приложение/xml; q=0,9,*/*;q=0,8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Соединение: Keep-alive
Происхождение: https://foo.example
Метод запроса-управления-доступом: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
 

Теперь сервер может ответить, если он примет запрос при таких обстоятельствах. В этом примере ответ сервера говорит следующее:

Access-Control-Allow-Origin

Источнику https://foo.example разрешено запрашивать bar.example/resources/post-here/ URL по следующему адресу:

Методы разрешения доступа

POST , GET и OPTIONS являются разрешенными методами для URL-адреса. (Этот заголовок аналогичен заголовку ответа Allow , но используется только для CORS.)

X-PINGOTHER и Content-Type являются разрешенными заголовками запроса для URL.

Контроль доступа-Max-Age

Указанные выше разрешения могут кэшироваться на 86 400 секунд (1 день).

 HTTP/1.1 200 Нет содержимого
Дата: понедельник, 01 декабря 2008 г., 01:15:39 по Гринвичу
Сервер: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: https://foo. example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Максимальный возраст контроля доступа: 86400
Варьировать: Accept-Encoding, Origin
Keep-Alive: таймаут=2, макс=100
Соединение: Keep-Alive
 

И 200 OK, и 204 No Content являются разрешенными кодами состояния, но некоторые браузеры ошибочно полагают, что 204 No Content относится к ресурсу, и не отправляют последующий запрос на его получение.

Спецификация
Семантика HTTP
# ОПЦИИ

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

  • Разрешить заголовок
  • КОРС

Обнаружили проблему с содержанием этой страницы?

  • Отредактируйте страницу на GitHub.
  • Сообщить о проблеме с содержимым.
  • Посмотреть исходный код на GitHub.

Хотите принять больше участия?

Узнайте, как внести свой вклад.

Последний раз эта страница была изменена участниками MDN.

Методы HTTP для служб RESTful

Ниже более подробно рассматриваются основные методы HTTP. Нажмите на вкладку для получения дополнительной информации о желаемом методе HTTP.

Глагол POST чаще всего используется для **создания** новых ресурсов. В частности, он используется для создания подчиненных ресурсов. То есть подчиняться какому-то другому (например, родительскому) ресурсу. Другими словами, при создании нового ресурса выполняется POST для родителя, и служба заботится о том, чтобы связать новый ресурс с родителем, назначить идентификатор (URI нового ресурса) и т. д.

При успешном создании вернуть HTTP-статус 201, возвращая заголовок Location со ссылкой на вновь созданный ресурс с HTTP-статусом 201.

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

Примеры:

  • POST http://www.example.com/customers
  • ОТПРАВКА http://www.example.com/customers/12345/orders

Метод HTTP GET используется для **чтения** (или извлечения) представления ресурса. В «счастливом» (или безошибочном) пути GET возвращает представление в XML или JSON и код ответа HTTP 200 (ОК). В случае ошибки чаще всего возвращается 404 (НЕ НАЙДЕНО) или 400 (НЕПРАВИЛЬНЫЙ ЗАПРОС).

В соответствии со спецификацией HTTP запросы GET (наряду с HEAD) используются только для чтения данных, но не для их изменения. Поэтому при таком использовании они считаются безопасными. То есть их можно вызывать без риска модификации или повреждения данных — однократный вызов имеет тот же эффект, что и 10-кратный вызов, или вообще не вызывается. Кроме того, GET (и HEAD) являются идемпотентными, что означает, что выполнение нескольких идентичных запросов приводит к тому же результату, что и одиночный запрос.

Не раскрывайте небезопасные операции через GET — он никогда не должен изменять какие-либо ресурсы на сервере.

Примеры:

  • ПОЛУЧИТЬ http://www.example.com/customers/12345
  • ПОЛУЧИТЬ http://www.example.com/customers/12345/orders
  • ПОЛУЧИТЬ http://www.example.com/buckets/sample

PUT чаще всего используется для **обновления** возможностей, PUT-запроса на известный URI ресурса с телом запроса, содержащим недавно обновленное представление исходного ресурса.

Однако PUT также можно использовать для создания ресурса в случае, когда идентификатор ресурса выбирается клиентом, а не сервером. Другими словами, если PUT относится к URI, который содержит значение идентификатора несуществующего ресурса. Опять же, тело запроса содержит представление ресурса. Многим это кажется запутанным и запутанным. Следовательно, этот метод создания должен использоваться экономно, если вообще.

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

При успешном обновлении вернуть 200 (или 204, если не возвращается никакого содержимого в теле) из PUT. Если для создания используется PUT, возвращайте HTTP-статус 201 при успешном создании. Тело в ответе является необязательным — при условии, что оно потребляет больше пропускной способности. Нет необходимости возвращать ссылку через заголовок Location в случае создания, так как клиент уже установил идентификатор ресурса.

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

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

Примеры:

  • PUT http://www.example.com/customers/12345
  • ПОСТАВИТЬ http://www.example.com/customers/12345/orders/98765
  • ПОСТАВИТЬ http://www.example.com/buckets/secret_stuff

PATCH используется для **изменения** возможностей. Запрос PATCH должен содержать только изменения ресурса, а не весь ресурс.

Это похоже на PUT, но тело содержит набор инструкций, описывающих, как ресурс, находящийся в настоящее время на сервере, должен быть изменен для создания новой версии. Это означает, что тело PATCH должно быть не просто измененной частью ресурса, а каким-то языком исправления, например JSON Patch или XML Patch.

PATCH не является ни безопасным, ни идемпотентным. Однако запрос PATCH может быть выполнен таким образом, чтобы он был идемпотентным, что также помогает предотвратить неблагоприятные результаты из-за коллизий между двумя запросами PATCH на одном и том же ресурсе в одинаковый период времени. Коллизии от нескольких запросов PATCH могут быть более опасными, чем коллизии PUT, потому что некоторые форматы исправлений должны работать с известной базовой точкой, иначе они повредят ресурс. Клиенты, использующие этот тип приложения исправления, должны использовать условный запрос, чтобы запрос не выполнялся, если ресурс был обновлен с момента последнего доступа клиента к ресурсу. Например, клиент может использовать сильный ETag в заголовке If-Match в запросе PATCH.

Примеры:

  • ИСПРАВЛЕНИЕ http://www.example.com/customers/12345
  • ИСПРАВЛЕНИЕ http://www.example.com/customers/12345/orders/98765
  • ИСПРАВЛЕНИЕ http://www. example.com/buckets/secret_stuff

DELETE довольно легко понять. Он используется для **удаления** ресурса, идентифицированного URI.

При успешном удалении вернуть HTTP-статус 200 (ОК) вместе с текстом ответа, возможно, представлением удаленного элемента (часто требует слишком большой пропускной способности) или обернутым ответом (см. Возвращаемые значения ниже). Либо так, либо вернуть статус HTTP 204 (NO CONTENT) без тела ответа. Другими словами, рекомендуемыми ответами являются статус 204 без тела или ответ в стиле JSEND и статус HTTP 200.

Для HTTP операции DELETE являются идемпотентными. Если вы УДАЛИТЕ ресурс, он будет удален. Многократный вызов DELETE для этого ресурса заканчивается тем же: ресурс исчез. Если вызов DELETE, скажем, уменьшает счетчик (внутри ресурса), вызов DELETE больше не является идемпотентным. Как упоминалось ранее, статистика использования и измерения могут быть обновлены, при этом сервис будет считаться идемпотентным до тех пор, пока не будут изменены данные о ресурсах.