Приоритет и статус кода HTTP

Допустим, веб-приложение получает следующий запрос:

POST /some/endpoint HTTP/1.1
Host: <something>
Accept: application/json
Accept-Language: pt
Content-Type: application/json
If-Match: "blabla"

Some body
  • Если сервер не поддерживает HTTP 1.1 и конечную точку /some/endpoint не существует, первая проблема, скорее всего, должна быть проверена первой, и 505, а не 404 должны быть возвращены.
  • Если так получится, что ни одна из конечных точек сервера не примет POST и конечная точка /some/endpoint не существует, последний должен получить приоритет, и должно быть возвращено 404, а не 405.
  • Если Accept не может быть предоставлено, и тело не может быть должным образом декодировано / проверено, вероятно, 406 должен иметь приоритет над 400.

Это случаи, когда интуиции может быть достаточно. Но есть множество других, где не ясно, какой из двух кодов состояния, отличных от 2XX, должен быть предпочтен / проверен в первую очередь. Например, должен Content-Type (в результате чего 415) или Accept-Language (406) быть возвращенным, если оба потерпят неудачу? 415 или 412? И это идет...

Большая часть ошибок по времени является попарно независимой: если аспект, относящийся к одной выдаваемой ошибке (такой как конкретное значение заголовка), является фиксированным, состояние успеха / ошибки другой не будет затронуто. В этих случаях неправильная ошибка "приоритет" может быть только неприятностью. Но иногда может быть так, что эти ошибки не являются независимыми: у меня могут быть ресурсы в виде HTML на португальском языке, но в JSON только на английском (юмор), так что, если клиент ожидает от меня приоритетов Accept-Language над Acceptи я делаю наоборот, результат будет довольно плохим.

Теперь вопрос должен быть очевиден: существуют ли какие-либо стандарты относительно того, каким ошибкам следует отдавать приоритет?

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

2 ответа

TL;DR: спецификации дают серверу максимальные полномочия в отношении того, как он выполняет запрос, даже позволяя серверу игнорировать приемлемые форматы, запрашиваемые клиентом. Тем не менее, в спецификациях указано, что сервер должен приложить максимум усилий и реагировать так, чтобы клиент мог лучше восстановиться после ошибок.


Спецификации предоставляют руководство, даже если они не (или не могут) определять приоритетность всех возможных режимов ошибок.

RFC 2616 § 10.4.7 говорит:

Серверы HTTP/1.1 могут возвращать ответы, которые не являются приемлемыми в соответствии с заголовками принятия, отправленными в запросе. В некоторых случаях это может быть даже предпочтительнее, чем отправка ответа 406. Пользовательским агентам рекомендуется проверять заголовки входящего ответа, чтобы определить, является ли он приемлемым.

RFC 7231 § 3 гласит:

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

RFC 7231 § 3.4 говорит:

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

RFC 7231 § 3.3 гласит:

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

RFC 2616 § 14.46 говорит:

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


(Подчеркивает все мое.)

Раздел 3 RFC 7231 предоставляет серверу происхождения окончательные полномочия для принятия решения о соответствующем ответе, даже если этот ответ является отвратительным. Одновременно раздел 3 призывает исходный сервер удовлетворить запрос или предоставить уведомление о том, что он удовлетворил часть запроса (Vary), или предоставить возможность выбора ("Пассивное согласование").

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

Рассматривая ваши попарные примеры:

  • "Если сервер не поддерживает HTTP 1.1, а конечная точка / некоторая / конечная точка не существует, скорее всего, следует сначала проверить первую проблему и вернуть 505, а не 404".

Согласно спецификации, клиент HTTP 1.1 может GET с версии 1.0 сервера по протоколу понижения, так что этот вид согласования версий обрабатывается спецификацией. Отправьте 404 (или 301, если это известно), чтобы пользователь мог исправить это.

  • "Если так получилось, что ни одна из конечных точек сервера не принимает POST, а конечная точка / некоторая / конечная точка не существует, последняя должна получить приоритет, и должно быть возвращено 404, а не 405."

Да, 404. Если вы не получаете доступ к ресурсу, метод вряд ли имеет значение.

  • "Если Accept не может быть предоставлен и тело не может быть соответствующим образом декодировано / проверено, вероятно, 406 должен иметь приоритет над 400".

Никогда не отправляйте 400, если знаете, что 406 применяется. Вы даете клиенту меньше информации, что менее полезно. Однако исходный сервер может игнорировать Accept заголовок согласно RFC 7231 § 5.3.2:

Если поле заголовка [Accept] присутствует в запросе, и ни одно из доступных представлений для ответа не имеет тип носителя, который указан как приемлемый, сервер происхождения может либо заполнить поле заголовка, отправив ответ 406 (не приемлемо), либо не обращайте внимания на поле заголовка, рассматривая ответ так, как будто он не подлежит согласованию содержимого.

  • "У меня могут быть ресурсы в виде HTML на португальском языке, но в JSON только на английском (утешите меня), так что если клиент ожидает от меня приоритета Accept-Language над Accept, а я делаю наоборот, результат будет довольно плохим. "

Я не согласен с тем, что результат будет плохим. См. RFC 7231 § 5.3.5:

исходный сервер может либо игнорировать поле заголовка [Accept-Language], обрабатывая ответ, как если бы он не подлежал согласованию содержимого, либо обрабатывать поле заголовка, отправляя ответ 406 (не приемлемо). Однако последнее не приветствуется, поскольку это может помешать пользователям получить доступ к контенту, который они могут использовать (например, с помощью программного обеспечения для перевода).

Этот образец языка спецификации встречается более одного раза. "Сервер может игнорировать [независимо от того, что запросил клиент], обрабатывая ответ, как если бы он не подпадает под действие [этой части спецификации], или сервер может выполнить [запрос клиента] и отправить [соответствующий код ошибки]. Но, лучше [отправить что-то вразумительное], чем только отправить [непостижимый код ошибки]. "

В конце концов, это ваш API. HTTP предоставляет только окно в вашу семантику. Документируйте, что вы принимаете, как вы реагируете и чем. Отправьте понятные ответы (HATEOAS - это хорошо) и, если применимо, наиболее конкретные доступные коды ошибок.

Очевидно, вы не можете ожидать, что на этот вопрос ответят "нет", хотя это, вероятно, правильный ответ.

Поэтому позвольте мне вместо этого обратиться к вашему конкретному вопросу:

У меня могут быть ресурсы в виде HTML на португальском языке, но в JSON только на английском (юмор), так что, если клиент ожидает от меня приоритетов Accept-Language над Acceptи я делаю наоборот, результат будет довольно плохим.

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

Accept: text/json
Accept-Language: pt, en;q=0.1

Затем сервер может умножить ваши веса, получив 1×0,1=0,1 для английского JSON и 0×1=0 для португальского HTML, и выбрав первый.

(Sidenote 1: нет text/json Тип носителя в реестре. Вы, вероятно, хотите application/json.)

(Sidenote 2: 415 Неподдерживаемый тип носителя не является правильным кодом ответа для упомянутых вами сценариев. Он касается тела запроса. Если вы не можете выполнить Accept заголовок, вы можете ответить 406 Не приемлемо, так же как и с Accept-Language.)

Другие вопросы по тегам