Какой правильный код ответа REST для действительного запроса, но пустых данных?

220

Например, вы запускаете запрос GET для users/9, но нет пользователя с идентификатором # 9. Какой лучший код ответа?

  • 200 OK
  • 202 Принято
  • 204 Нет содержимого
  • 400 Bad Request
  • 404 Не найдено
  • 13
    Подсказка: вы нашли пользователя 9?
  • 0
    @ChristopherPfohl Нет, но 404 и 204 кажутся наиболее подходящими
Показать ещё 9 комментариев
Теги:
rest
http
api-design

14 ответов

190
Лучший ответ

TL; DR: использовать 404

Смотрите этот блог. Это очень хорошо объясняет.

Резюме комментариев блога на 204:

  1. 204 No Content не очень полезен в качестве кода ответа для браузера (хотя согласно спецификации HTTP браузеры должны понимать его как код ответа "не меняйте представление").
  2. 204 No Content, однако, очень полезен для веб-сервисов ajax, которые могут указывать на успех без необходимости что-либо возвращать. (Особенно в таких случаях, как DELETE или POST которые не требуют обратной связи).

Поэтому ответом на ваш вопрос является использование 404 в вашем случае. 204 - это специальный код ответа, который вы не должны часто возвращать в браузер в ответ на GET.

Другие коды ответов, которые даже менее подходят, чем 204 и 404:

  1. 200 должны быть возвращены с телом того, что вы успешно получили. Не подходит, когда сущность, которую вы выбираете, не существует.
  2. 202 используется, когда сервер начал работу над объектом, но объект еще не полностью готов. Конечно, дело не в этом. Вы не начали и не начнете создание пользователя 9 в ответ на запрос GET. Это нарушает все виды правил.
  3. 400 используется в ответ на плохо отформатированный HTTP-запрос (например, искаженные заголовки http, неправильно упорядоченные сегменты и т.д.). Это почти наверняка будет обработано любой платформой, которую вы используете. Вам не придется иметь дело с этим, если вы не пишете свой собственный сервер с нуля. Редактировать: более новые RFC теперь позволяют использовать 400 для семантически неверных запросов.

Википедия с описанием кодов состояния HTTP особенно полезна. Вы также можете увидеть определения в документе HTTP/1.1 RFC2616 на www.w3.org

  • 7
    Примечание: коды ответов в 200-х указывают на успех. Коды ответа в 400-х годах указывают на сбой. Сводка, пункты первая и вторая, касается кода ответа 204 (без содержимого).
  • 148
    -1, поскольку я слишком категорически против 404 в качестве ответа на успешный вызов, который не имеет записей для возврата. Как разработчик, имеющий дело с API для не-веб-приложения, я потратил часы на то, чтобы связаться с разработчиками API, чтобы выяснить, почему конечной точки API, которую я вызывал, не существовало, а на самом деле это не имело данные для отчета. Что касается 204, который не особенно полезен для браузера, это немного похоже на то, как хвост машет собакой, поскольку большинство применений конечных точек API в нашей вселенной интеллектуальных устройств не основаны на браузере, а те, которые, вероятно, используют AJAX. Извините, что забрал очки, хотя.
Показать ещё 13 комментариев
244

Я решительно выступаю против 404 в пользу 204 или 200 с пустыми данными.

Запрос был получен и обработан надлежащим образом - он вызывал код приложения на сервере, поэтому нельзя сказать, что это была ошибка клиента, и, следовательно, весь класс кодов ошибок клиента (4xx) не подходит.

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

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

- > Для приложений, которые заботятся о качестве своих данных, 404, следовательно, в значительной степени не работает.

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

Единственное преимущество 404 над 204 заключается в том, что он может вернуть объект ответа, который может содержать некоторую информацию о том, почему запрошенный ресурс не найден. Но если это действительно актуально, то можно также рассмотреть возможность использования ответа 200 OK и разработать систему таким образом, чтобы допускать ошибки в данных полезной нагрузки. В качестве альтернативы, можно использовать полезную нагрузку ответа 404 для возврата структурированной информации вызывающему абоненту. Если он получает, например, html-страницу вместо XML или JSON, которую он может анализировать, то это хороший показатель того, что что-то техническое пошло не так, как ответ "нет результата", который может быть действительным с точки зрения вызывающего. Или для этого можно использовать заголовок ответа HTTP.

Тем не менее, я бы предпочел 204 или 200 с пустым ответом. Таким образом, статус технического выполнения запроса отделен от логического результата запроса. 2xx означает "техническое выполнение ok, это результат, с этим справиться".

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

Другая быстрая аналогия: Возвращение 404 для "не найденного результата" похоже на то, что бросает исключение DatabaseConnectionException, если SQL-запрос не дал никаких результатов. Он может выполнить эту работу, но есть много возможных технических причин, которые вызывают одно и то же исключение, которое тогда было бы ошибочно принято за допустимый результат.

  • 21
    Технические причины «не найдены» также известны как ошибки сервера. Это должно быть в 500-х. В частности: «Служба не найдена» - это 503 Service Unavailable .
  • 34
    Спрашивающий также спрашивал о конкретном ресурсе: один пользователь (не маршрут /users , а /users/9 , то есть «пользователь, известный как # 9»), поэтому сравнение с «пустым набором результатов» не имеет смысла , 404 означает, что объект не существует.
Показать ещё 22 комментария
19

Если он ожидает, что ресурс существует, но он может быть пустым, я бы сказал, что было бы проще просто получить 200 OK с представлением, указывающим, что предмет пуст.

Итак, я предпочел бы, чтобы/вещи возвращали 200 OK с { "Items": []}, чем 204, где ничего нет, потому что таким образом коллекция с 0 элементами может обрабатываться точно так же, как коллекция с одним или несколькими элементами в нем.

Я просто оставил 204 No Content для PUT и DELETE, где может быть и то, что действительно нет полезного представления.

В случае, если /thing/ 9 действительно не существует, подходит 404.

  • 0
    Похоже, вы предпочитаете программировать с использованием API, используя более абстрактную форму RPC. Вместо того, чтобы обращаться к ресурсам, внимательно следуя глаголам, и URL- GetCustomerAddressBook на основе ресурсов, таким как customers/1/addressbook , RPC-способ заключается в вызове конечной точки, такой как GetCustomerAddressBook и либо получении данных, либо, по существу, нулевом GetCustomerAddressBook и не нужно слишком беспокоиться о сложностях HTTP. Есть плюсы и минусы для обоих.
  • 2
    @ Маффин, я не уверен, как ты можешь притвориться, что знаешь, предпочитаю ли я REST или RPC, и почему это имеет отношение к обсуждению того, какой код состояния возвращать в запрос HTTP GET.
Показать ещё 1 комментарий
18

Сначала я думал, что 204 будет иметь смысл, но после обсуждений я считаю, что 404 - единственно верный правильный ответ. Рассмотрим следующие данные:

Пользователи: Джон, Питер

METHOD  URL                      STATUS  RESPONSE
GET     /users                   200     [John, Peter]
GET     /users/john              200     John
GET     /users/kyle              404     Not found
GET     /users?name=kyle'        200     []
DELETE  /users/john              204     No Content

Немного предыстории:

  1. поиск возвращает массив, у него просто нет совпадений, но у него есть содержимое: пустой массив.

  2. 404, конечно, лучше всего известен по URL, которые не поддерживаются запрошенным сервером, но отсутствующий ресурс фактически тот же.
    Несмотря на то, что /users/:name совпадает с users/kyle, пользовательский Kyle не является доступным ресурсом, поэтому 404 по-прежнему применяется. Это не поисковый запрос, это прямая ссылка по динамическому URL, так что 404 это так.

Во всяком случае, мои два цента :)

  • 4
    Бросаю свой вес за этот стиль для REST API. Ни один клиент не должен запрашивать / users / kyle, если не было сказано, что ресурс будет существовать посредством вызова либо / users, либо / users? Name = kyle
  • 0
    @GaryBarker Но так как это «пользовательский ввод» в некотором роде, API все равно должен правильно его обрабатывать. Хотя вы должны запретить 404 в ВАШЕМ клиенте, вы не можете быть уверены, что он действительно был проверен в первую очередь. Разработка API должна выполняться без учета точной реализации вашего клиента, особенно с точки зрения проверенного пользовательского ввода.
Показать ещё 1 комментарий
18

В предыдущих проектах я использовал 404. Если нет пользователя 9, то объект не был найден. Поэтому 404 Not Found подходит.

Для объекта существует, но данных нет, 204 Нет содержимого. Я думаю, что в вашем случае объект не существует, хотя.

10

Согласно w3 post,

200 OK

Запрос преуспел. Информация, возвращаемая с ответом, зависит от метода, используемого в запросе

202 Принято

Запрос принят для обработки, но обработка не завершена.

204 Нет содержимого

Сервер выполнил запрос, но ему не нужно возвращать тело сущности и может захотеть вернуть обновленную метаинформацию.

400 Плохой запрос

Запрос не может быть понят сервером из-за неправильного синтаксиса. Клиент НЕ ДОЛЖЕН повторять запрос без изменений

401 Несанкционированный

Запрос требует аутентификации пользователя. Ответ ДОЛЖЕН включать поле заголовка WWW-Authenticate

404 Не найдено

Сервер не нашел ничего, что соответствовало бы Request-URI. Не указывается, является ли условие временным или постоянным.

  • 0
    Сообщение w3 о кодах состояния HTTP. HTTP не идентичен REST. REST в основном, но не обязательно, использует HTTP в качестве транспортного протокола. 404 - это код ошибки транспорта HTTP. Если REST будет таким же, как HTTP, не должен ли он называться HTTP?
9

Чтобы обобщить или упростить,

2xx: Необязательные данные: сформированный URI: критерии не являются частью URI: если критерии являются необязательными, которые могут быть указаны в @RequestBody, а @RequestParam должны приводить к 2xx. Пример: фильтр по имени/статусу

4xx: ожидаемые данные: неверно сформированный URI: критерии являются частью URI: если критерии обязательны, которые могут быть указаны только в @PathVariable, то это должно привести к 4xx. Пример: поиск по уникальному идентификатору.

Таким образом, для заданной ситуации: "Пользователи/9" будут 4xx (возможно, 404) Но для "users? Name = superman" должно быть 2xx (возможно, 204)

7

Есть два вопроса. Один в заголовке и один в примере. Я думаю, что это частично привело к количеству споров о том, какой ответ подходит.

В заголовке вопроса запрашиваются пустые данные. Пустые данные по-прежнему являются данными, но не совпадают с данными. Таким образом, это предполагает запрос набора результатов, т.е. Списка, возможно, от /users. Если список пуст, он по-прежнему является списком, поэтому наиболее подходящим является 204 (No Content). Вы только что попросили список пользователей и получили один, у него просто нет контента.

В представленном примере вместо этого запрашивается конкретный объект, пользователь, /users/9. Если пользователь # 9 не найден, объект пользователя не возвращается. Вы запросили определенный ресурс (объект пользователя) и не получили его, потому что он не был найден, поэтому подходит 404.

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

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

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

3

Что существующие ответы не уточняют, так это то, что имеет значение, используете ли вы параметры пути или параметры запроса.

  • В случае параметров пути параметр является частью пути ресурса. В случае /users/9 ответ должен быть 404 потому что этот ресурс не был найден. /users/9 - это ресурс, и результат унарный, или ошибки не существует. Это не монада.
  • В случае параметров запроса этот параметр не является частью пути к ресурсу. В случае /users?id=9, ответ должен быть 204 потому что ресурс /users был найден, но он не смог вернуть никаких данных. Ресурс /users существуют, и результат n-арный, он существует, даже если он пуст. Если id уникален, это монада.

Использовать ли параметры пути или параметры запроса, зависит от варианта использования. Я предпочитаю параметры пути для обязательных, нормативных или идентифицирующих аргументов и параметры запроса для необязательных, ненормативных или атрибутивных аргументов (таких как разбиение на страницы, локаль сопоставления и прочее). В API REST я бы использовал /users/9 не /users?id=9 особенно из-за возможного вложения, чтобы получить "дочерние записи", такие как /users/9/ssh-keys/0 чтобы получить первый открытый ключ ssh или /users/9/address/2 чтобы получить третий почтовый адрес.

Я предпочитаю использовать 404. Вот почему:

  • Вызовы унарных (1 результат) и n-арных (n результатов) методов не должны изменяться без уважительной причины. Мне нравится иметь одинаковые коды ответов, если это возможно. Число ожидаемых результатов, конечно, является разницей, скажем, вы ожидаете, что тело будет объектом (унарным) или массивом объектов (n-арным).
  • Для n-a я бы возвратил массив, и в случае отсутствия результатов я бы не возвратил никакого набора (без документа), я бы возвратил пустой набор (пустой документ, как пустой массив в JSON или пустой элемент в XML). То есть это еще 200 но с нулевыми записями. Нет никакой причины помещать эту информацию в провод, кроме как в теле.
  • 204 как void метод. Я бы не использовал его для GET, только для POST, PUT и DELETE. Я делаю исключение в случае GET где идентификаторы являются параметрами запроса, а не параметрами пути.
  • Отсутствие записи похоже на NoSuchElementException, ArrayIndexOutOfBoundsException или что-то в этом роде, вызванное тем, что клиент использует идентификатор, который не существует, поэтому это ошибка клиента.
  • С точки зрения кода, получение 204 означает дополнительную ветвь в коде, которой можно избежать. Это усложняет клиентский код, а в некоторых случаях также усложняет серверный код (в зависимости от того, используете ли вы монады сущностей/моделей или простые сущности/модели; и я настоятельно рекомендую держаться подальше от монад сущностей/моделей, это может привести к неприятным ошибкам, где, поскольку из монады вы думаете, что операция прошла успешно, и возвращайте 200 или 204, когда вы действительно должны были вернуть что-то еще).
  • Код клиента легче написать и понять, если 2xx означает, что сервер сделал то, что запросил клиент, а 4xx означает, что сервер не сделал то, что запросил клиент, и это ошибка клиента. Не предоставление клиенту записи о том, что клиент, запрошенный по идентификатору, является ошибкой клиента, потому что клиент запросил идентификатор, который не существует.

Последнее, но не менее важное: последовательность

  • GET/users/9
  • PUT/users/9 и DELETE/users/9

PUT/users/9 и DELETE/users/9 уже должны вернуть 204 в случае успешного обновления или удаления. Так что они должны вернуть в случае, если пользователь 9 не существует? Нет смысла представлять одну и ту же ситуацию в виде разных кодов состояния в зависимости от используемого метода HTTP.

Кроме того, не нормативная, а культурная причина: если 204 используется для GET/users/9 следующая вещь, которая произойдет в проекте, это то, что кто-то думает, что возвращение 204 хорошо для n-арных методов. И это усложняет клиентский код, потому что вместо того, чтобы просто проверять 2xx и затем декодировать тело, клиент теперь должен специально проверять 204 и в этом случае пропустить декодирование тела. Буд, что клиент делает вместо этого? Создать пустой массив? Тогда почему бы не иметь это на проводе? Если клиент создает пустой массив, 204 является формой глупого сжатия. Если клиент использует вместо этого null, открывается совершенно другая банка червей.

3

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

2

Я бы сказал, ни то, ни другое не подходит. Как уже было сказано - например, @anneb, я тоже думаю, что часть проблем возникает из-за использования HTTP-кода ответа для передачи статуса, относящегося к службе RESTful. Все, что служба REST может сказать о своей собственной обработке, должно передаваться с помощью специальных кодов REST.

1

Я бы сказал, что если HTTP-сервер найдет какой-либо сервис, который готов ответить на запрос, который он отправил, он не должен отвечать HTTP 404 - в конце концов, что-то было найдено сервером - если только это явно не указано сервис, который обрабатывает запрос.

Предположим на минуту следующий URL: http://example.com/service/return/test.

  • Случай А заключается в том, что сервер "просто ищет файл в файловой системе". Если его нет, 404 правильно. То же самое верно, если он запрашивает какой-либо сервис для доставки именно этого файла, и этот сервис сообщает, что ничего с таким именем не существует.
  • В случае B сервер не работает с "настоящими" файлами, но на самом деле запрос обрабатывается какой-то другой службой, например какой-то системой шаблонов. Здесь сервер не может предъявлять никаких претензий о существовании ресурса, поскольку он ничего не знает о нем (если только об этом не сообщит служба, обрабатывающая его).

Без какого-либо ответа от сервиса, явно требующего другого поведения, HTTP-сервер может сказать только 3 вещи:

  • 503 если служба, которая должна обрабатывать запрос, не работает или не отвечает;
  • 200 иначе как HTTP-сервер может фактически удовлетворить запрос - независимо от того, что служба скажет позже;
  • 400 или 404 чтобы указать, что такой службы нет (в отличие от "существует, но в автономном режиме") и ничего больше не найдено.

2

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

  • REST:EMPTY например, если его попросили найти что-нибудь. и это исследование вернулось пустым;
  • REST:NOT FOUND если его спросили специально для чего-либо. "ID-like" - будь то имя файла или ресурс по идентификатору или записи № 24 и т.д. - и этот конкретный ресурс не был найден (обычно один конкретный ресурс был запрошен и не найден);
  • REST:INVALID если какая-либо часть запроса, которую он отправил, не распознается службой.

(обратите внимание, что я поставил перед ними префикс "REST:", чтобы отметить тот факт, что хотя они могут иметь те же значения или формулировку, что и коды ответов HTTP, они полностью отличаются)

3

Давайте вернемся к указанному выше URL-адресу и изучим случай B, где служба указывает HTTP-серверу, что он не обрабатывает этот запрос сам, а передает его в SERVICE. HTTP выдает только то, что ему возвращает SERVICE, он ничего не знает о части return/test поскольку он обрабатывается SERVICE. Если эта служба работает, HTTP должен вернуть 200 поскольку он действительно нашел что-то для обработки запроса.

Статус, возвращаемый SERVICE (и который, как сказано выше, хотелось бы видеть в отдельном заголовке), зависит от того, какое действие фактически ожидается:

  • если return/test запрашивает определенный ресурс: если он существует, вернуть его со статусом REST:FOUND; если этот ресурс не существует, верните REST:NOT FOUND; это можно продлить, чтобы вернуть REST:GONE если мы знаем, что он когда-то существовал и не вернется, и REST:MOVED если мы знаем, что он перешел в Голливуд
  • если return/test считается операцией поиска или фильтрации: если набор результатов пуст, вернуть пустой набор в запрошенном типе и состояние REST:EMPTY; набор результатов в запрашиваемом типе и статусе REST:SUCCESS
  • если return/test не является операцией, распознаваемой SERVICE: верните REST:ERROR если она полностью неверна (например, опечатка типа retrun/test), или REST:NOT IMPLEMENTED если она запланирована на более позднее время.

4

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

  • Если возвращается HTTP 404, сервер говорит мне: "Я понятия не имею, о чем ты говоришь". Хотя REST-часть моего запроса может быть совершенно нормальной, я ищу par'Mach во всех неправильных местах.
  • С другой стороны, HTTP 200 и REST: ERR говорят мне, что я получил услугу, но сделал что-то не так в своем запросе к службе.
  • Из HTTP 200 и REST: EMPTY я знаю, что ничего плохого не сделал - правильный сервер, сервер нашел сервис, правильный запрос к сервису - но результат поиска пуст.

Резюме

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

Состояние запроса, обработанного службой, а не сервером HTTP, ДЕЙСТВИТЕЛЬНО НЕ ДОЛЖНО (RFC 6919) быть задано кодом ответа HTTP. Код HTTP СЛЕДУЕТ (RFC 2119) содержать только информацию, которую сервер HTTP может предоставить из своей собственной области: а именно, была ли найдена служба для обработки запроса или нет.

Вместо этого СЛЕДУЕТ использовать другой способ сообщить потребителю о состоянии запроса службе, которая фактически обрабатывает запрос. Мое предложение сделать это через специальный заголовок. В идеале как заголовок, так и его содержимое следуют стандарту, который облегчает потребителям работу с этими ответами.

0

Twitter использует 404 с пользовательским сообщением об ошибке типа "Данные не найдены".

Ссылка: https://developer.twitter.com/en/docs/basics/response-codes.html

  • 0
    Microsoft Azure также использует 404, но пользовательское сообщение об ошибке невозможно при ответе на запросы HEAD. docs.microsoft.com/en-us/rest/api/resources/resources/...
0

Кодировать содержимое ответа с общим перечислением, которое позволяет клиенту соответственно включать его и вилку. Я не уверен, как ваш клиент будет отличать разницу между "не найденными данными" 404 и "веб-ресурс не найден" 404? Вы не хотите, чтобы кто-то просматривал пользователя Z/9, и клиент удивился, как будто запрос был действительным, но не было возвращено данных.

-2

Почему бы не использовать 410? Он предполагает, что запрашиваемый ресурс больше не существует, и ожидается, что клиент никогда не сделает запрос для этого ресурса в вашем случае users/9.

Вы можете найти более подробную информацию о 410 здесь: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

  • 2
    «ожидается, что клиент никогда не сделает запрос» - и что если этот пользователь будет создан позже, ваш ответ предполагает, что вы можете гарантировать, что этот пользователь никогда не будет существовать, это очень смелое предположение и, вероятно, неправильно
  • 0
    Что сказала Холли. Чтобы добавить к ее пункту: даже в случае, если пользователь существовал и был удален, 410 неправильно, потому что, что, если пользователь будет удален? (Какой особый случай создается позже.)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню