Должен ли я использовать коды состояния HTTP для описания событий уровня приложения

54

Несколько серверов, с которыми я имел дело, будут возвращать HTTP 200 для запросов, которые клиент должен рассматривать как сбой, с чем-то вроде «success: false» в теле.

Это не похоже на правильную реализацию HTTP-кодов, особенно в случаях неудачной аутентификации. Я прочитал коды ошибок HTTP довольно кратко, суммируя их как «4xx» указывает, что запрос не должен повторяться до тех пор, пока не будет изменен, в то время как «5xx» указывает, что запрос может или не может быть действительным и может быть повторен, но был неудачным. В этом случае 200: не удалось войти в систему, или 200: не удалось найти этот файл, или 200: отсутствует параметр x, определенно кажутся неправильными.

С другой стороны, я мог видеть аргумент, что «4xx» должен указывать только на структурную проблему с запросом. Так что это правильно, чтобы вернуть 200: неверный пользователь / пароль, а не 401 неавторизованный, потому что клиенту разрешено сделать запрос, но это, оказывается, неверно. Этот аргумент может быть обобщен следующим образом: если сервер был в состоянии обработать запрос и сделать определение вообще, код ответа должен быть 200, и клиент должен проверить тело для получения дополнительной информации.

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

Каган Маттсон
источник
9
success: falseподразумевает, что запрос не выполнен, и вы это знаете. Это должно быть 500. Что-то вроде вашего неверного имени пользователя / пароля будет 401. Это не так уж многозначно.
Пит
1
cf stackoverflow.com/questions/11714485/…
Каган Маттсон
4
Я думаю, это один из тех вопросов, которые могут вызвать религиозные войны. Для RESTful API ответ ясен, но есть и другие виды API, где HTTP обрабатывается просто как транспортный уровень, и в этих случаях ошибки приложения не должны попадать на этот уровень.
Gort the Robot
5
Когда я действительно не уверен, какой статус http возвращать, всегда хочется написать 418 «Я чайник».
Joshp
1
Примером являются множественные (пакетные) запросы и ответы . Пакетирование - это не успокоительная вещь; но практические проблемы эффективности часто требуют некоторой поддержки для группирования проблем элегантности.
Rwong

Ответы:

35

Интересный вопрос.

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

Однако на практике сервер почти всегда является ретранслятором, а клиент - веб-браузером, отвечающим за интерпретацию и рендеринг контента: сервер просто передает данные в произвольное приложение, и эти приложения отправляют обратно произвольные сценарии, которые браузер несет ответственность за выполнение. Само HTTP-взаимодействие - формы запроса / ответа, коды состояния и т. Д. - в основном связано с тем, как запрашивать, обслуживать и отображать произвольный контент настолько эффективно, насколько это возможно, не мешая. Многие из кодов состояния и заголовков действительно предназначены для этих целей.

Проблема с попыткой использовать протокол HTTP для обработки потоков, специфичных для приложения, состоит в том, что у вас остается один из двух вариантов: 1) вы должны сделать свою логику запроса / ответа подмножеством правил HTTP; или 2) Вы должны повторно использовать определенные правила, и тогда разделение интересов становится нечетким. Поначалу это может выглядеть красиво и чисто, но я думаю, что это одно из тех дизайнерских решений, о которых вы сожалеете по мере развития вашего проекта.

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

Другое преимущество этого подхода, о котором стоит упомянуть, состоит в том, что приложения, вообще говоря, не должны зависеть от базового транспортного протокола (с логической точки зрения). Сам HTTP изменился в прошлом, и теперь мы запускаем HTTP 2, следуя SPDY. Если вы рассматриваете свое приложение как не что иное, как плагин функциональности HTTP, вы можете застрять там, когда новые инфраструктуры вступят во владение.

Ям Маркович
источник
8
Очень проницательно Самый сильный аргумент здесь - это (полное сопротивление) несоответствие между кодами состояния HTTP и возвращаемыми значениями вашего приложения. Это может стать кошмаром в долгосрочной перспективе. Кроме того, я очень поддерживаю разделение проблем между транспортом (HTTP) и полезной нагрузкой (данные приложения). Если вы неправильно наберете URL-адрес конечной точки службы, вы получите 404. Если вы запросите у службы несуществующий элемент, вы получите сообщение для конкретного приложения (возможно, с дополнительной информацией, которую вы можете использовать для решения проблемы).
2
Если вы неправильно наберете URL, вы можете даже не оказаться на нужном сервере, и тогда может случиться что угодно .
gnasher729
Это приятный нюанс Я думаю, что проблема превращения HTTP в псевдотранспортный уровень - это реальная проблема с определением. Я чаще всего сталкиваюсь с этим вопросом самостоятельно, когда у вас есть сервер nginx или apache, проксирующий сервер nodejs, где прокси-сервер уже имеет правила для отправки этих кодов, и возникает вопрос, подходит ли бэкэнд для соответствия стандарту. В некоторых из этих случаев может быть причина не отправлять код ошибки, поскольку nginx может интерпретировать его как «backend down».
Каган Мэтсон
4
Я согласен. Нет ничего плохого в том, что в ответе HTTP 200 сообщается об ошибке уровня приложения. Значение 200 указывает, что сам запрос / ответ HTTP был успешным, не говоря уже о его содержимом или семантике прикладного уровня, вызываемой в то время.
Легкость гонки с Моникой
22

Этот вопрос немного основан на мнении, но в любом случае.

На мой взгляд, 200 могут служить «мягкими ошибками». Когда дело доходит до создания API, я стараюсь различать их и «серьезные ошибки».

«Мягкие ошибки» будут обслуживаться с кодом состояния 200, но будут содержать описание ошибки и статус успеха false. «Мягкие ошибки» будут возникать только тогда, когда результат будет «как ожидается», но не в самом строгом смысле.

Важно отметить, что «мягкие ошибки» - это скорее подсказка для разработчика. Поэтому важно также предоставить больше информации об ошибке, такой как читаемое человеком сообщение об ошибке и / или некоторый код, который можно использовать для предоставления конечному пользователю обратной связи. Эти ошибки предоставляют разработчику (и конечному пользователю) больше информации о том, что произошло на стороне сервера .

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

Пример в формате JSON:

{
    "meta" {
        "success": false,
        "message": "Search yielded no results",
        "code": "NORESULTS"
    }
    "data": []
}

«Сильные ошибки», с другой стороны, будут сопровождаться кодом состояния, который рекомендуется для ошибки. Пользователь не вошел в систему? - 403/401. Неправильный ввод? - 400. Ошибка сервера? - 50Х. И так далее.

Опять же, это немного основано на мнении. Некоторые люди хотят одинаково относиться ко всем ошибкам, ко всем - к "жесткой ошибке". Нет Результатов Поиска? Это 404! На другой стороне медали нет результатов поиска? - Это, как и ожидалось, без ошибок.

Другим важным фактором, который следует принимать во внимание, является, например, ваша архитектура; если вы взаимодействуете с вашим API, используя JavaScript XHR-запросы и jQuery или AngularJS. Эти "серьезные ошибки" должны обрабатываться отдельным обратным вызовом, тогда как "мягкие ошибки" могут обрабатываться с помощью "success" -callback. Ничего не нарушая, результат все еще "как и ожидалось". Код на стороне клиента может затем посмотреть на статус успеха и код (или сообщение). И распечатайте это для конечного пользователя.

умереть
источник
2
На самом деле, классифицировать это как ошибку на уровне API - это любопытное решение. Даже если клиент может по своему усмотрению классифицировать его как неожиданный на уровне пользователя.
Дедупликатор
1
Есть много вещей, которые должны быть учтены. Все зависит от реализации API. Это также опять-таки, немного основано на мнении, а также до того, что API определяет как «успех» и / или «ошибка». "success": false-Flag это скорее намек на реализатор , что - то случилось. Обычно это должно идти с внутренним кодом состояния. Либо, "code": "NORESULTS"либо числовой код - все, что придумал создатель API. Это в основном там, так что, кто бы ни реализовывал API, он может вывести информацию о том, что произошло на сервере.
Die Maus
15

Существует два аспекта API: усилия по реализации API и усилия всех клиентов правильно использовать API.

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

Разрабатывая API, вы должны посмотреть, что проще всего обрабатывать клиенту. Если клиент отправляет правильно сформированный запрос, и вы можете делать то, что запрашивает ваш запрос, вы должны дать ответ в диапазоне 200 (в некоторых случаях подходит число, отличное от 200 в этом диапазоне).

Если клиент спрашивает «дать мне все записи, как ...», и их число равно нулю, тогда 200 с успехом и массив с нулевыми записями полностью уместны. Случаи, которые вы упоминаете:

«Ошибка входа в систему» ​​обычно должна быть 401. «Файл не найден» должен быть 404. «Отсутствующий параметр x» должен быть около 500 (на самом деле, 400, если сервер обнаруживает, что запрос плохой, и 500 если сервер полностью сбит с толку моим запросом и не знает, что происходит). Возвращать 200 в этих случаях бессмысленно. Это просто означает, что, как автор клиента, я не могу просто посмотреть на код состояния, я также должен изучить ответ. Я не могу просто сказать «статус 200, отлично, вот данные».

Особенно «отсутствие параметров» - это не то, с чем я бы когда-либо справился . Это означает, что мой запрос неверен. Если мой запрос неверный, у меня нет запасного варианта, чтобы исправить этот неправильный запрос - я бы отправил правильный запрос для начала. Теперь я вынужден справиться с этим. Я получаю 200 и должен проверить, есть ли ответ «отсутствует параметр». Это ужасно.

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

gnasher729
источник
3
При подключении к API я лично предпочел бы получить 200 для «файла, не найденного» при подключении к действительной конечной точке, так как тогда моя обработка HTTP не должна сливаться с уровнем, который обрабатывает API поверх него.
whatsisname
4
«Отсутствующий параметр x» должен быть 400 BAD_REQUEST, потому что клиент делает что-то не так. 500 INTERNAL_SERVER_ERROR должен быть зарезервирован для случаев, когда сервер делает неправильные вещи. 500 означает, что клиент может попытаться снова. 400 подразумевает, что кто-то должен исправить клиента.
Gort the Robot
1
Если вы пишете интерфейс RESTful, URL-адрес идентифицирует конкретный объект, поэтому 404 подходит. Концептуально то же самое /customers/premium/johndoe.jsonотносится к клиенту, которого нет в базе данных, и если /files/morefiles/customers.htmlотносится к странице, не входящей в файловую систему.
Gort the Robot
@whatsisname То, что вы говорите, имеет смысл, потому что тогда неясно, является ли конечная точка плохой или ресурс не существует. Вы также можете утверждать, что независимо от того, является ли конечная точка действительной или нет, по этому адресу не существует ресурса, поэтому 404в обоих случаях это верно.
Пит
2
Одна вещь, о которой я не упомянул, это то, что когда вы добавляете ошибки приложения в коды состояния HTTP, вы можете потерять информацию. Если приложение просто возвращает 404 и ничего больше, вы не знаете, было ли это из-за того, что ваш API возвратил 404, или потому что сервер не смог найти файл. Это может добавить еще один шаг к вашей отладке.
AmadeusDrZaius