Время на прочтение
Если вы не специалист по REST, то, вероятно, в своих ответах постоянно используете одни и те же HTTP-коды, в основном 200, 404 и 500. Если применяется аутентификация, то, возможно, добавляются 401 и 403; если есть переадресации, то 301 и 302, но на этом, скорее всего, список заканчивается. Но спектр возможных кодов состояний гораздо шире и он может сильно улучшить семантику. Хотя во многих обсуждениях REST упор делается на сущностях и методах, применение подходящих кодов ответов о состояниях может повысить удобство вашего API.
Код состояния HTTP (англ. HTTP status code) — часть первой строки ответа сервера при запросах по протоколу HTTP.
Он представляет собой целое трёхразрядное десятичное число. Первая цифра указывает на класс состояния. За кодом ответа обычно следует отделённая пробелом поясняющая фраза на английском языке, которая разъясняет человеку причину именно такого ответа. Примеры:
Клиент может не знать все коды состояния, но он обязан отреагировать в соответствии с классом кода. В настоящее время выделено пять классов кодов состояния.
Веб-сервер Internet Information Services в своих файлах журналов, кроме стандартных кодов состояния, использует подкоды, записывая их через точку после основного. При этом в ответах от сервера данный подкод не размещается — он нужен администратору сервера, чтобы тот мог более точно определять источники проблем.
Ситуацию с использованием кодов ответов HTTP можно заносить в палату мер и весов: вот что происходит, когда благие намерения разработчиков спецификации сталкиваются с жестокой реальностью. Даже с двумя жестокими реальностями.
Как мы обсудили в Главе 10, одна из целей существования семантических ошибок — помочь клиенту понять, что стало причиной ошибки. При разработке спецификации HTTP (в частности, RFC 7231) эта цель очевидно была одной из главных. Более того, архитектурные ограничения REST, как их описал Фьелдинг в своей диссертации, предполагают, что не только клиенты должны понимать семантику ошибки, но и все сетевые агенты (прокси) между клиентом и сервером в «многослойной» архитектуре. И, в соответствии с этим, номенклатура статус-кодов HTTP действительно весьма подробно описывает почти любые проблемы, которые могут случиться с HTTP-запросом: недопустимые значения Accept-*-заголовков, отсутствующий Content-Length, неподдерживаемый HTTP-метод, слишком длинный URI и так далее.
Но вот с чем RFC совершенно не помогает — это с вопросом, а что собственно клиенту или прокси делать с ошибкой. Как мы обсуждали, ошибки могут быть устранимыми или неустранимыми. Если ошибки неустранимая, то клиентам по большому счёту наплевать на всю эту петрушку со статус-кодами и заголовками, а уж промежуточным прокси тем более. Для этого на самом деле трёх кодов было бы достаточно:
Замечание: кстати, обратите внимание на проблему дизайна спецификации. По умолчанию все 4xx коды не кэшируются, за исключением: 404, 405, 410, 414. Мы не сомневаемся, что это было сделано из благих намерений, но подозреваем, что количество людей, знающих об этой тонкости, примерно равно количеству редакторов спецификации. В результате мы имеем множество ситуаций (автор лично разгребал последствия одной из них), когда 404-ки были возвращены ошибочно, но клиент их закэшировал, тем самым продлив факап на неопределённое время.
Что касается устранимых проблем — то да, статус-коды в чем-то помогают. Некоторые из них вполне конкретны, например 411 Length Required. А некоторые — нет. Можно привести множество ситуаций, где под одним кодом прячутся разнородные ошибки:
Таким образом, мы вполне естественным образом приходим к идее отдавать детальное описание ошибки в заголовках и/или теле ответа, не пытаясь изобрести новый код для каждой ситуации — абсолютно очевидно, что нельзя задизайнить по ошибке на каждый потенциально неправильный параметр вместо единой 400-ки, например.
Замечание: авторы спецификации тоже это понимали, и добавили следующую фразу: ‘The response message will usually contain a representation that explains the status’. Мы с ними, конечно, полностью согласны, но не можем не отметить, что эта фраза не только делает кусок спецификации бесполезным (а зачем нужны коды-то тогда?), но и противоречит парадигме REST: другие агенты в многоуровневой системе не могут понять, что же там «объясняет» представление ошибки, и сама ошибка становится для них непрозрачной.
Казалось бы, мы пришли к логичному выводу: используйте статус-коды для индикации «класса» ошибки в терминах протокола HTTP, а детали положите в ответ. Но вот тут теория повторно на всех парах напарывается на практику. С самого появления Web все фреймворки и серверное ПО полагаются на статус-коды для логирования и построения мониторингов. Я не думаю, что сильно совру, если скажу, что буквально не существует платформы, которая из коробки умеет строить графики по семантическим данным в ответе ошибки, а не по статус-кодам. И отсюда автоматически следует дальнейшее усугубление проблемы: чтобы отсечь в своих мониторингах незначимые ошибки и эскалировать значимые, разработчики начали попросту придумывать новые статус-коды — или использовать существующие не по назначению.
В современном мире мы буквально живём в этом бардаке: статус-коды HTTP используются вовсе не в целях поддержания чистоты протокола, а для графиков; их истинное значение забыто; клиенты обычно и не пытаются хоть какие-то выводы из кода ответа сделать, редуцируя его до первой цифры. ( Честно говоря, ещё неизвестно, что хуже — игнорировать код или, напротив, писать логику поверх кодов, использованных не по назначению.) Ну и, конечно, нельзя не упомянуть о широко распространённой практике отдавать ошибки внутри 200-ок.
А какие ваши предложения?
На самом деле есть три подхода к решению этой ситуации:
Выбор за вами, но на всякий случай заметим, что подход #3 весьма дорог в реализации.
— Этот текст написан в рамках подготовки будущего раздела про HTTP API для моей книги, работы ведутся на Гитхабе.
Англоязычная версия этого же текста здесь.
Я буду признателен, если кто-то пошарит её на реддите, я сам по правилам реддита не могу.
HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
Responses are grouped in five classes:
The status codes listed below are defined by RFC 9110.
Note: If you receive a response that is not in this list, it is a non-standard response, possibly custom to the server’s software.
Information responses
This interim response indicates that the client should continue the request or ignore the response if the request is already finished.
101 Switching Protocols
This code is sent in response to an Upgrade request header from the client and indicates the protocol the server is switching to.
102 Processing (WebDAV)
This code indicates that the server has received and is processing the request, but no response is available yet.
103 Early Hints
Successful responses
The request succeeded. The result meaning of “success” depends on the HTTP method:
The request succeeded, and a new resource was created as a result. This is typically the response sent after POST requests, or some PUT requests.
The request has been received but not yet acted upon.
It is noncommittal, since there is no way in HTTP to later send an asynchronous response indicating the outcome of the request.
It is intended for cases where another process or server handles the request, or for batch processing.
This response code means the returned metadata is not exactly the same as is available from the origin server, but is collected from a local or a third-party copy.
This is mostly used for mirrors or backups of another resource.
Except for that specific case, the 200 OK response is preferred to this status.
204 No Content
205 Reset Content
206 Partial Content
This response code is used when the Range header is sent from the client to request only part of a resource.
207 Multi-Status (WebDAV)
Conveys information about multiple resources, for situations where multiple status codes might be appropriate.
208 Already Reported (WebDAV)
226 IM Used (HTTP Delta encoding)
The server has fulfilled a GET request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.
Redirection messages
300 Multiple Choices
301 Moved Permanently
The URL of the requested resource has been changed permanently. The new URL is given in the response.
This response code means that the URI of requested resource has been changed temporarily.
Further changes in the URI might be made in the future. Therefore, this same URI should be used by the client in future requests.
303 See Other
The server sent this response to direct the client to get the requested resource at another URI with a GET request.
304 Не изменено
Используется для целей кэширования.
Он сообщает клиенту, что ответ не был изменен, поэтому клиент может продолжать использовать ту же кэшированную версию ответа.
305 Использовать прокси
Определено в предыдущей версии спецификации HTTP, чтобы указать, что запрошенный ответ должен быть доступен через прокси.
Он устарел из-за проблем безопасности, связанных с внутренней настройкой прокси-сервера.
Этот код ответа больше не используется; это просто зарезервировано. Он использовался в предыдущей версии спецификации HTTP/1.1.
307 Временное перенаправление
308 Постоянное перенаправление
400 Неверный запрос
Сервер не может или не будет обрабатывать запрос из-за чего-то, что воспринимается как ошибка клиента (например, неправильный синтаксис запроса, неверный кадр сообщения запроса или обманная маршрутизация запроса).
Хотя стандарт HTTP определяет «неавторизованный», семантически этот ответ означает «неаутентифицированный».
То есть клиент должен аутентифицировать себя, чтобы получить запрошенный ответ.
402 Требуется оплата
Этот код ответа зарезервирован для использования в будущем.
Первоначальной целью создания этого кода было использование его для цифровых платежных систем, однако этот код состояния используется очень редко, и стандартного соглашения не существует.
Клиент не имеет прав доступа к контенту; то есть он неавторизован, поэтому сервер отказывается предоставить запрошенный ресурс.
В отличие от 401 Unauthorized, личность клиента известна серверу.
404 Не найден
Сервер не может найти запрошенный ресурс.
В браузере это означает, что URL-адрес не распознан.
В API это также может означать, что конечная точка действительна, но сам ресурс не существует.
Серверы также могут отправить этот ответ вместо 403 Forbidden, чтобы скрыть существование ресурса от неавторизованного клиента.
Этот код ответа, вероятно, наиболее известен из-за его частого появления в сети.
405 Метод не разрешен
Метод запроса известен серверу, но не поддерживается целевым ресурсом.
Например, API может не разрешать вызов DELETE для удаления ресурса.
406 Неприемлемо
407 Требуется аутентификация прокси
Это похоже на 401 Unauthorized, но аутентификация должна выполняться через прокси-сервер.
408 Тайм-аут запроса
Некоторые серверы отправляют этот ответ при незанятом соединении даже без предварительного запроса со стороны клиента.
Это означает, что сервер хотел бы закрыть это неиспользуемое соединение.
Этот ответ используется гораздо чаще, поскольку некоторые браузеры, такие как Chrome, Firefox 27+ или IE9, используют механизмы предварительного подключения HTTP для ускорения серфинга.
Также обратите внимание, что некоторые серверы просто отключают соединение, не отправляя это сообщение.
Этот ответ отправляется, когда запрос конфликтует с текущим состоянием сервера.
Этот ответ отправляется, когда запрошенный контент был окончательно удален с сервера без адреса пересылки.
Ожидается, что клиенты удалят свои кеши и ссылки на ресурс.
Спецификация HTTP предполагает, что этот код состояния будет использоваться для «ограниченных по времени рекламных услуг».
API не должны чувствовать себя обязанными указывать ресурсы, которые были удалены, с помощью этого кода состояния.
Сервер отклонил запрос, поскольку поле заголовка Content-Length не определено и оно требуется серверу.
412 Предварительное условие не выполнено
Клиент указал в своих заголовках предварительные условия, которым сервер не соответствует.
413 Слишком большая полезная нагрузка
Объект запроса превышает пределы, определенные сервером.
Сервер может закрыть соединение или вернуть поле заголовка Retry-After.
414 Слишком длинный URI
URI, запрошенный клиентом, длиннее, чем сервер готов интерпретировать.
415 Неподдерживаемый тип носителя
Медиа-формат запрошенных данных не поддерживается сервером, поэтому сервер отклоняет запрос.
416 Диапазон неудовлетворителен
Диапазон, указанный в поле заголовка Range в запросе, не может быть заполнен.
Возможно, диапазон выходит за пределы размера данных целевого URI.
417 Ожидание не оправдалось
Этот код ответа означает, что ожидание, указанное в поле заголовка запроса Expect, не может быть выполнено сервером.
418 Я чайник
Официант отклоняет попытку заварить кофе в чайнике.
421 Неверно направленный запрос
Запрос был направлен на сервер, который не может дать ответ.
Его может отправить сервер, который не настроен на выдачу ответов для комбинации схемы и полномочий, включенных в URI запроса.
422 необработанного контента (WebDAV)
423 Заблокировано (WebDAV)
Ресурс, к которому осуществляется доступ, заблокирован.
424 Неудачная зависимость (WebDAV)
Запрос не выполнен из-за сбоя предыдущего запроса.
425 Слишком рано
Указывает, что сервер не желает рисковать обработкой запроса, который может быть воспроизведен.
426 Требуется обновление
Сервер отказывается выполнять запрос с использованием текущего протокола, но может быть готов сделать это после того, как клиент перейдет на другой протокол.
Сервер отправляет заголовок Upgrade в ответе 426, чтобы указать требуемый протокол(ы).
428 Требуется предварительное условие
Исходный сервер требует, чтобы запрос был условным.
Этот ответ предназначен для предотвращения проблемы «потерянного обновления», когда клиент ПОЛУЧАЕТ состояние ресурса, изменяет его и отправляет обратно на сервер, в то время как третья сторона изменила состояние на сервере, что привело к конфликту.
429 Слишком много запросов
Сервер не желает обрабатывать запрос, поскольку поля его заголовка слишком велики.
Запрос может быть отправлен повторно после уменьшения размера полей заголовка запроса.
451 Недоступно по юридическим причинам
500 Внутренняя ошибка сервера
Сервер столкнулся с ситуацией, с которой не знает, как справиться.
501 Не реализовано
Метод запроса не поддерживается сервером и не может быть обработан. Единственные методы, которые серверы должны поддерживать (и, следовательно, не должны возвращать этот код), — это GET и HEAD.
502 Плохие врата
Этот ошибочный ответ означает, что сервер, работая как шлюз для получения ответа, необходимого для обработки запроса, получил недопустимый ответ.
503 Служба недоступна
504 Тайм-аут шлюза
Этот ответ об ошибке выдается, когда сервер действует как шлюз и не может получить ответ вовремя.
505 Версия HTTP не поддерживается
Версия HTTP, использованная в запросе, не поддерживается сервером.
506 Вариант также ведет переговоры
На сервере произошла внутренняя ошибка конфигурации: выбранный вариантный ресурс сам настроен на участие в прозрачном согласовании контента и поэтому не является подходящей конечной точкой в процессе согласования.
507 Недостаточно места для хранения (WebDAV)
Метод не может быть выполнен на ресурсе, поскольку сервер не может сохранить представление, необходимое для успешного выполнения запроса.
Обнаружена петля 508 (WebDAV)
Сервер обнаружил бесконечный цикл при обработке запроса.
510 Не расширенный
Для того чтобы сервер выполнил запрос, требуются дальнейшие расширения.
511 Требуется сетевая аутентификация
Указывает, что клиенту необходимо пройти аутентификацию для получения доступа к сети.
Совместимость с браузером
Таблицы BCD загружаются только в браузере
См. также
Многие приложения позволяют создавать эффекты: аккаунты, заказы и т. д. В общем случае применяется HTTP-код состояния 200, и этого вполне достаточно. Однако код 201 более конкретен и подходит лучше:
.
HTTP-код ответа 201 Создано в успешном состоянии показывает, что запрос успешно выполнен и привёл к созданию ресурса. По сути, новый ресурс был создан до отправки этого ответа, а сам новый ресурс возвращается в телесообщение, его местоположением становится или запрос URL, или заголовок заголовка Location.
— Веб-документация MDN
Сбросить содержимое
Аутентификация при помощи формы может быть успешной или неудачной. При неудачном выполнении обычно повторно отображается форма с уточненными полями.
И как раз для этого предназначен код состояния 205:
HTTP-код состояния 205 Сбросить содержимое сообщает о том, что в Китае нужно бросить визуализацию документа, то есть, например, современные традиционные формы, сбрасывающие состояние холста или обновление пользовательского интерфейса.
Требуется предварительное условие
При использовании Optimistic Locking проверка при обновлении может оказаться неудачной, поскольку данные уже были обновлены кем-то ещё. По умолчанию фреймворки (например, Hibernate) в таком случае выбрасывают определения. В свою очередь, разработчики могут перехватывать его и отображать удобное информационное окно, просящее перезагрузить страницу и ввести данные повторно.
Давайте проверим код состояния 428:
Исходный сервер требует, чтобы запрос был условным. Это нужно для устранения проблемы «утерянного обновления», когда клиент при помощи GET получает состояние ресурса, изменяет его и помещает при помощи PUT обратно на сервер, в то время как третья сторона изменила состояние на сервере, что приводит к конфликту.
Этот код чётко описывает случай конфликта при optimistic locking!
В RFC 6585 упоминается термин условный и показывается пример использования заголовка If-Match. Однако в нём не показано, как конкретно достичь этого условия.
Conflict
Любопытно, что по поводу кода 409 написано следующее:
HTTP-код ответа состояния 409 Conflict говорит о конфликте запроса с текущим состоянием сервера.
Он также применим в предыдущем случае, но более обобщённый. Например, типичным примером использования будет обновление уже удалённого ресурса.
Gone
Чаще всего, когда вы пытаетесь получить при помощи GET ненайденный ресурс, сервер возвращает код 404. Но что, если ресурс существовал ранее, но теперь его нет? Интересно, что для конкретно этого случая есть альтернатива: об этом может сообщить семантика возвращаемого HTTP-кода. И именно поэтому используется 410.
HTTP-код ответа о клиентской ошибке 410 Gone показывает, что доступ к целевому ресурсу уже отсутствует на исходном сервере и это состояние, скорее всего, будет постоянным.
Если неизвестно, временное это состояние или постоянное, то нужно использовать код состояния 404.
Multiple choices
Это может показаться натянутым выбором, но спецификация IETF соответствует данному случаю.
Приложения на основе HATEOAS имеют корневую страницу, которая становится точкой входа, позволяющей выполнять дальнейшую навигацию.
Например, вот какой ответ возникает при доступе к Spring Boot Actuator:
В этом местоположении отсутствует обычный ресурс. Сервер предоставляет множество ресурсов, каждый из которых имеет свой идентификатор. Это подходит для кода состояния 300:
— IETF HTTP 1.1: Semantics and Content
Заключение
В общем случае конкретные состояния HTTP имеют смысл при наличии REST-бэкенда, доступ к которому выполняет JavaScript-фронтенд. Например, сброс формы (205) не имеет смысла, если страницу генерирует сервер.
Проблема этих кодов связана с семантикой: интерпретировать их можно по-разному. Зачем выбирать 409 вместо 428? В конечном итоге, это может быть вопросом интерпретации.
Если вы предоставляете публичный REST API, то у вас есть комбинация этих (и других) кодов, а также заголовки. Вам нужна подробная документация на все случаи, чтобы уточнить общую семантику для вашего конкретного контекста. Это не должно отталкивать вас от их использования, ведь они представляют собой богатое множество, из которого можно выбирать подходящие ответы.
Дальнейшее чтение
В этот класс выделены коды, информирующие о процессе передачи. При работе через протокол версии 1.0 сообщения с такими кодами должны игнорироваться. В версии 1.1 клиент должен быть готов принять этот класс сообщений как обычный ответ, но серверу отправлять что-либо не нужно. Сами сообщения от сервера содержат только стартовую строку ответа и, если требуется, несколько специфичных для ответа полей заголовка. Прокси-серверы подобные сообщения должны отправлять дальше от сервера к клиенту.
Сообщения данного класса информируют о случаях успешного принятия и обработки запроса клиента.
В зависимости от статуса сервер может ещё передать заголовки и тело сообщения.
Коды этого класса сообщают клиенту, что для успешного выполнения операции необходимо сделать другой запрос, как правило, по другому URI. Из данного класса пять кодов 301, 302, 303, 305 и 307 относятся непосредственно к перенаправлениям. Адрес, по которому клиенту следует произвести запрос, сервер указывает в заголовке Location. При этом допускается использование фрагментов в целевом URI.
Поведение клиентов при различных перенаправлениях описано в таблице:
Класс кодов 4xx предназначен для указания ошибок со стороны клиента. При использовании всех методов, кроме HEAD, сервер должен вернуть в теле сообщения гипертекстовое пояснение для пользователя.
Сервер вернул ошибку 403 при попытке просмотра каталога «cgi-bin», доступ к которому был запрещён
Пример ошибки 502 Bad Gateway
Коды 5xx выделены под случаи необработанных исключений при выполнении операций на стороне сервера. Для всех ситуаций, кроме использования метода HEAD, сервер должен включать в тело сообщения объяснение, которое клиент отобразит пользователю.
Основные документы по протоколу HTTP (по убыванию даты публикации)
Документы по расширениям и обновлениям протокола HTTP (по убыванию даты публикации)
Ниже представлен обзорный список всех описанных в данной статье кодов ответа:
Диаграмма принятия веб-сервером решений на основе заголовков
Статистика по кодам ответа, сгенерированная анализатором логов Webalizer