Pull to refresh

Comments 29

Если уж речь зашла про Created, то стоит упомянуть и 204: No Content. Уместен, например, как результат успешного запроса PUT.

202 Accepted response status code indicates that the request has been accepted for processing, but the processing has not been completed

Нет, не логичнее. Если операция успешно завершилась до конца — код 202 неприменим.

Нет. 202 — ждите, вас (не)обязательно обслужат.

202 is non-committal, meaning that there is no way for the HTTP to later send an asynchronous response indicating the outcome of processing the request. It is intended for cases where another process or server handles the request, or for batch processing.

Вот было бы круто, если бы кто-то написал не о том, что нужно использовать все коды возврата, а рассказал бы зачем это надо делать )) . И в каких случаях какие коды использовать. Я бы сказал спасибо даже за статью в которой рассказывается в каких случаях надо вернуть 404. За статью, полностью посвящённую только одному этому http коду.

Скажем, потому что недостаток прав при доступе страницы не является её отсутсвием. Github например так приватные репы отдаёт если доступа не было. Логичнее было бы отдавать 403 или 401. Хотя тут ещё нюансы с приватностью есть.

Надеюсь, что всё же никто не будет использовать 205: Reset Content если при заполнении формы авторизации пользователь опечатался в емейле

И в чем информативность ваших котов?

Не сильно ниже и противоречивей HTTP кодов

Весь этот старый набор кодов давно следует пересмотреть. Часть пометить как deprecated, для остальных расписать когда использовать.

В grpc вообще обошлись порядка 15 кодов состояний (если мне не изменяет память).

И есть отличный JSON-RPC, в котором коды/ошибки транспортного уровня четко отделены от кодов/ошибок уровня приложения. Что убрало весь этот зоопарк с чайниками и котами.

Думаю важны только http коды API, которые может обработать клиент.

Часто в инструкциях или советах опытных web разработчиков можно узнать информацию совершенно обратную. Чтобы не слить(случайно или не-) внутрянку системы используется код 404 и только он. Если не прав, поправьте пожалуйста.

К слову, в посте не обнаружил обратной аргументации.

В случае API коды ответа правильные часто используют, чтоб клиент не разбирал данные из ответа, ибо будет и так понятно, что что-то пошло не так. 404 вместо 401/403 все же применяют для скрытия того, что условно может быть доступно определенному кругу пользователей прямо в браузере.

Из практики. Когда-то давно реализовали мы REST-like API для одного сервиса, задокументировали и раздали пользователям (их тогда было несколько сотен).

Поскольку стандартных HTTP-кодов было недостаточно, каждый ответ (если он шёл от приложения) содержал развернутый индикацию успешности или отчёт об ошибке (или сразу о нескольких, если их было больше одной).

А когда пошли первые жалобы мы столкнулись с одним интересным явлением - пользователи начисто игнорировали тело если получали что-то кроме 200 в ответ, и совершенно искренне не могли понять зачем читать тело если есть код.

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

По этой причине следующая версия API была построена по простому принципу - 200 - запрос дошёл до приложения, >= 300 - не дошёл. И дело пошло - с тех пор проблем и вопросов у пользователей сильно поубавилось, потому что детальные отчеты в теле были достаточно красноречивы, и самое главное - их стали читать и парсить.

С тех пор у меня есть чёткое убеждение - не надо смешивать транспорт (HTTP) и приложение (JSON/XML/etc), а также не надо пытаться впихнуть в куцые варианты кодов HTTP варианты ответов приложения - их банально недостаточно и они практически бесполезны в сложных API. А если API доступно не только через HTTP (да, есть и другие транспорты), то отсутствие привязки только упрощает разработку.

К тому же, если уж заставлять пользователя читать тело вне зависимости от кода - сами коды имеют мало смысла, их основное назначение - индикация того дошёл ли запрос до приложения или нет, и на этом всё. В самом крайнем случае можно использовать серию 5xx для индикации того что запрос не был обработан (т.е. безопасно его повторить), но не более того.

Разумеется, в очень простых API, где всё влезает в HTTP-коды их можно использовать (хотя и остаётся неоднозначность с 404), но это частный случай.

По поводу кэширования - есть же вполне себе стандартизованный Cache-Control, который решает проблемы с прокси и прочими посредниками, а если прокси его не понимает или пытается "умничать" - то грош ему цена.

На стороне клиентов тоже иногда очень весело обрабатывать миллиард вариантов ответов от сервера:

"Если код равен 200 или код равен 201 или код равен 202 или код равен ....."
(то ответ как бы пришёл и его можно обрабатывать одинаково).
"Если код равен 300 или код равен 301 или код равен 302 или код равен ....."
(то ответ как бы перемещён, но и его тоже в целом можно обрабатывать одинаково).

И для каждого вызова апишечки предлагается писать такую телегу. И это не учитывая что внутри json'а в ответе будут свои отдельные статусы, и их как-то тоже надо сопоставлять, от чего деревья if'ов разрастаются, поддержка-отладка затрудняется и так далее.

Единственные ответы для которых как бы имеет значение именно код - 4xx, так как там можно запросить повторно, совершив некоторые телодвижения на клиенте, и получить нужный результат. Во всех остальных случаях, от транспорта как правило требуется только одно: "Оно отработало?" или "Оно не отработало?", особенно когда в теле сообщеньки будут дополнительные статусы.

Не всё так хорошо в этом подходе.

Во-первых, бесит получать ошибки с кодом 200.

Во-вторых, помимо пользователей есть промежуточные edge proxy, которые штатно выдают полезные метрики, но эти метрики не рассчитаны на ошибки с кодом 200. Да и помимо них хватает сторонних инструментов (напр. для нагрузочного тестирования), которые так же ориентируются на стандартные коды и не распознают ошибки с кодом 200.

В-третьих, возврат вообще всех ответов с кодом 200 не избавляет клиентов от поддержки других кодов - потому что да, есть тот самый edge/ingress proxy, который отлично умеет возвращать 502/504 если микросервис за ним не работает и временами другие ошибки тоже.

В-четвёртых, нередко в приложении используется какой-то фреймворк (напр. swagger/openapi) или банальная сторонняя библиотека для роутинга входящих запросов, который на некорректные запросы или запросы к неизвестным роутам всё-равно будет возвращать далеко не 200.

В-третьих, возврат вообще всех ответов с кодом 200 не избавляет клиентов от поддержки других кодов — потому что да, есть тот самый edge/ingress proxy, который отлично умеет возвращать 502/504 если микросервис за ним не работает и временами другие ошибки тоже.

Так в этом и суть отделения ошибок логики от ошибок транспорта — чтобы клиентский код мог обрабатывать их не путая друг с другом.


В-четвёртых, нередко в приложении используется какой-то фреймворк (напр. swagger/openapi) или банальная сторонняя библиотека для роутинга входящих запросов, который на некорректные запросы или запросы к неизвестным роутам всё-равно будет возвращать далеко не 200.

Вот они-то и являются основным источником тех самых транспортных ошибок, что так "весело" проникают в бизнес-логику и всё путают.

Так в этом и суть отделения ошибок логики от ошибок транспорта — чтобы клиентский код мог обрабатывать их не путая друг с другом.

Проблема в том, что для клиентского кода любые ошибки возвращаемые инфраструктурой нашего проекта - это ошибки сервиса (вы их тут ошибками логики называете, что не совсем верно). А ошибками транспорта для него являются сетевые ошибки (DNS, no route to host, connection refused, etc.).

Для клиента есть только одна точка входа в наше API - и в случае микросервисного проекта это тот самый edge proxy, который дальше проксирует запрос одному из кучи разных микросервисов, которые могут быть написаны на разных ЯП и фреймворках. И любой ответ этого edge proxy - включая 404/500/502/504, - для клиента это ответ нашего API, а не транспортная ошибка.

В ином случае, следуя Вашей логике, если первый микросервис отправил запрос в другой (аналогично тому, как edge proxy отправил запрос в первый микросервис) и не смог получить адекватный ответ (напр. из-за connection refused) то что, первый микросервис должен вернуть 503/504 вместо описываемого Вами 200? Если да, то Ваша концепция "микросервис всегда возвращает 200" рассыпается, а если нет, то наш сервис в идентичных ситуациях начинает отвечать иногда 5xx а иногда 200, в зависимости от того, в каком из наших микросервисов (edge proxy считаем таким же микросервисом, как и все остальные) случилась проблема.

Я уже молчу о том, что в проекте могут использоваться не только микросервисы, написанные нами по единому гайдлайну, но и сторонние сервисы - тот же edge proxy, centrifugo, etc. И все такие сервисы обычно следуют стандартным рекомендациям HTTP, и не возвращают ошибки с кодом 200 и телом в json.

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

В ином случае, следуя Вашей логике, если первый микросервис отправил запрос в другой (аналогично тому, как edge proxy отправил запрос в первый микросервис) и не смог получить адекватный ответ (напр. из-за connection refused) то что, первый микросервис должен вернуть 503/504 вместо описываемого Вами 200?

Если произошла транспортная ошибка — скорее всего, надо вернуть 503. Это не противоречит тому, что при логических ошибках лучше возвращать 200.

Чем конкретно это лучше? Что это ломает - я описал, а вот в чём польза - неясно.

Разработчики клиентов API, которые не в состоянии прочитать доку на API и/или понять, что с кодом >=400 может прилететь как `Content-Type: application/json` и тогда в теле будут подробности ошибки, так и другой Content-Type и тогда в теле ничего полезного не будет - ну, ой. Как по мне, чем раньше таких "специалистов" поувольняют - тем лучше будет для всех, включая самих "специалистов". А иначе получаем известное: сделайте систему для идиотов и только идиоты захотят ей пользоваться. Я вот не хочу пользоваться системой, которая ошибки возвращает с кодом 200, потому что это много чего ломает в библиотеках и инструментах корректно поддерживающих HTTP и тем самым сильно усложняет мою жизнь.

И даже если проблему с непонятливыми разработчиками клиентов API действительно необходимо решить чтобы снять их вопросы в саппорт, то зачастую это намного проще сделать другим способом: выпустив SDK для тех 2-3 ЯП которыми пользуются разработчики клиентов, и реализовав корректную работу с API внутри SDK. Тем более, что одним распознаванием деталей ошибок проблема корректного использования API не ограничивается, а разработчики которые даже Content-Type проверить не в состоянии вряд ли реализуют и другие критичные фичи (напр. экспоненциальную задержку с повтором при ошибках чтобы не DDoS-ить сервис, или автоматический повтор идемпотентных запросов при ошибках, таймауты при слишком долгом выполнении запросов, рейт-лимиты, etc.).

Есть стандарт Problem Details для передачи подробной информации об ошибке. Нужно использовать его в дополнение к корректному статусу ошибки. Если в логика приложения содержит клиент-серверное взаимодействие, значит она содержит и детали этого взаимодействия. Не нужно делать вид, поедая суп, что ложки не существует :) Есть таки разница.

Самое худшее, с чем я когда-либо встречался, это запихивание ошибок в 200 ОК (ошибка внутри). Последствия данного решения довольно печальны для развития и обслуживания системы.

Возврат кода 201: Created разве не говорит что создан "ресурс-ссылка" см. (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201) и если создается только запись в БД без новой ссылки, то и надо возвращать 200 ?

Поддержу логику что нужно отдавать кодом 200 с указанием кода ошибки и описания в json например. Другие http коды применять при явной их необходимости.

Sign up to leave a comment.

Articles