Должен ли REST API возвращать 500 Internal Server Error, чтобы указать, что запрос ссылается на несуществующий объект?

39

Я работаю с REST API, который находится на сервере, который обрабатывает данные для множества устройств IoT.

Моя задача - выполнить запрос к серверу с помощью API для сбора конкретной информации о производительности указанных устройств.

В одном случае я получаю список доступных устройств и их соответствующие идентификаторы, а затем опрашиваю сервер для получения более подробной информации, используя эти идентификаторы (GUID).

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

errorMessage: "This ID does not exist",

Не обращайте внимания на тот факт, что сервер предоставил идентификатор для начала - это отдельная проблема для разработчика.

Должен ли REST API возвращать a, 500 Internal Server Errorчтобы сообщить, что запрос ссылается на несуществующий объект? На мой взгляд, коды ответов HTTP должны строго относиться к состоянию вызова REST, а не к внутренней механике API. Я ожидаю получить 200 OKответ с ошибкой и описанием, которые будут принадлежать данному API.


Мне приходит в голову, что есть потенциальная разница в ожидании в зависимости от того, как структурирован вызов REST.

Рассмотрим эти примеры:

  1. http://example.com/restapi/deviceinfo?id=123
  2. http://example.com/restapi/device/123/info

В первом случае идентификатор устройства передается как переменная GET. 404 или 500 будет означать, что path ( /restapi/deviceinfo) либо не найден, либо привел к ошибке сервера.

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

JYelton
источник
Это состояние, которое вы описываете, считается успешным или неудачным?
Роберт Харви
3
связанный (возможно, дубликат): я должен вернуть ответ 204 или 404, когда ресурс не найден?
комнат
@RobertHarvey Я ожидал, что API вернет некоторую информацию об устройстве. Сам запрос должен был быть успешным, но пропавший идентификатор устройства не должен вызывать сбой на уровне запроса.
Джелтон
18
ID не существует звучит как 404 для меня. Самая распространенная причина ошибок 500 - программисты, позволяющие всплыть внутреннему исключению, которое будет обрабатывать хост-сервер. Иногда бывают действительно исключительные случаи, когда это происходит, а иногда это просто ленивое программирование. Трудно сказать, что здесь происходит.
Берин Лорич
9
Внутренний сервер 500 означает «Это наша вина, мы что-то напутали», и вы никогда не должны стремиться вернуть этот статус пользователю. Его цель в основном состоит в том, чтобы указать на ошибку - ваш пользователь может сказать, что он получает 500, когда он делает определенный запрос, а затем вы можете войти и исправить его.
berry120

Ответы:

96

Я думаю, что ответ 404 является лучшим семантическим соответствием здесь, потому что ресурс, который вы пытались найти (как представлено URI, использованным для запроса), не был найден. Возврат полезной информации об ошибке в теле является разумным, но не обязательным.

Согласно RFC 2616 определение кода статуса 404:

10.4.5 404 Not Found
Сервер не нашел ничего, соответствующего Request-URI. Не указано, является ли состояние временным или постоянным. Код состояния 410 (Унесенные) СЛЕДУЕТ использовать, если сервер через некоторый внутренне конфигурируемый механизм знает, что старый ресурс постоянно недоступен и не имеет адреса пересылки. Этот код состояния обычно используется, когда сервер не хочет точно указывать, почему запрос был отклонен, или когда другой ответ не применим.

Энди Хант
источник
6
404 является только семантическим соответствием, если идентификатор, который не существует, соответствует ресурсу, который извлекается.
Роберт Харви
55
@ThomasOwens Запрос, не возвращающий результатов, вернул бы статус 200 и пустой массив результатов. 404 будет возвращено только при указании определенного идентификатора объекта, который на самом деле не существует.
Шон Бертон,
11
@ThomasOwens: с точки зрения вызывающего абонента, конечная точка не является /questions, это так /questions/368213. Эта конечная точка не существует в вашем сценарии. Подумайте об этом так: если вы выполняете GET для / foo / bar, а столбца нет, почему ответ должен быть другим, если / foo существует или нет?
Брайан Оукли
17
Да, это не ошибка сервера, поэтому мы не отправляем 5xx. Это ошибка клиента - клиент попросил что-то не иметь, поэтому мы отправляем 4xx, а именно 404.
bdsl
7
@ThomasOwens: How do you differentiate between a 404 meaning "the query returned no results" and a 404 meaning "the endpoint does not exist"?- 400 плохих запросов.
Роберт Харви
34

Я буду использовать ваши примеры.

http://example.com/restapi/deviceinfo?id=123

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

Если конечная точка предназначена для возврата одного результата , мой выбор был бы 404 NOT FOUND, потому что для меня правильный синтаксис для такого рода конечной точки является: http://example.com/restapi/deviceinfo/123. Я обычно использую параметр запроса только для фильтрации и когда моя конечная точка возвращает массив.

http://example.com/restapi/device/123/info

Я думаю, что этот вопрос уже был дан ответ здесь . POST или GET, лучший выбор кажется, 404 NOT FOUNDпотому что ресурс 123не был найден.

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

Dherik
источник
2
Как вы различаете несуществующий идентификатор и неверный URL?
Роберт Харви
2
@luizfzs, вы можете к этому также, я не вижу проблемы в этом. В некоторых API, которые я разработал, я предпочитаю использовать 204 в успешном PUT или DELETE ( как описано здесь ) без какого-либо тела в ответе.
Дерик
10
Почему вы должны различать несуществующий идентификатор и неверный URL на этом уровне? В любом случае, вы все еще делаете правильный запрос, что касается HTTP. Сервер просто ... ну ... не может найти ресурс, к которому вы обращаетесь. Неверный URL не является неправильным запросом; это правильно сформированный запрос на неправильную вещь .
cHao
6
@cHao Потому что, как разработчик, я хочу знать, происходит ли сбой моего приложения, потому что элемент не существует, или я случайно указал свое приложение на app.company.cxm / test /, а не app.company.cxm / tst /
Патрик М
7
@PatrickM: Как разработчик, вы должны знать, что ответы об ошибках также могут содержать текст сообщения. Вот где идет пояснительная информация об ошибке, если вы действительно этого хотите. Не нарушайте семантику только потому, что вы параноики по поводу толстых пальцев. На уровне HTTP, не имеет значения, испортили ли вы как /itms/1234или /items/12234.
Цао
27

HTTP 404 правильно, потому что сервер понимает, какой ресурс запрашивает клиент, но у него нет этого ресурса.

Тот факт, что вы работаете с «REST API», является ключевым. API должен вести себя так, как будто он выполняет REpresentational State Transfer, а не выполняет функцию. (Конечно, термин «REST» приобрел более широкое значение, но вы все равно можете использовать его буквальное значение для достижения хорошего эффекта.) Клиент запросил состояние ресурса, описываемое URL-адресом http://example.com/restapi/device/123/info. Строка запроса ( /deviceinfo?id=123) не изменит ситуацию. Сервер знает, что вы просите передать состояние устройства 123, но он не распознает это как известный ресурс. Отсюда HTTP 404.

Другие возможные ответы, обсуждаемые здесь, также имеют определенные значения:

  • HTTP 200- У нас есть состояние для вас; это в теле ответа.
  • HTTP 204- У нас есть состояние для вас; это пусто
  • HTTP 400- Мы не можем сказать, о каком ресурсе вы спрашиваете. Исправьте ваш URL.
  • HTTP 500- Мы неисправны. Не твоя вина.

См. RFC 2616 Sec. 10 соответственно.

Трэвис Уилсон
источник
Я согласен с вашим мнением об этом. Если бы сервер возвращал 404, это имело бы больше смысла, чем то, что он делает (500). Спасибо.
Джелтон
11

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

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


Я бы рассматривал оба ваших примера ( http://example.com/restapi/deviceinfo?id=123и http://example.com/restapi/device/123/info) одинаково - 123это параметр. В обоих случаях это разные способы структурирования запроса на получение информации об устройстве для устройства с идентификатором 123.

Сначала я бы рассмотрел авторизацию и аутентификацию. Если у пользователя нет соответствующих разрешений, я бы вернул 403 или 401 в зависимости от ситуации. Хотя он называется «Не авторизован», я понимаю, что 401 больше относится к аутентификации, а 403 - к неавторизованному или отказу в доступе. Я бы не стал слишком требователен, если бы вы просто хотели использовать 403 для всех ошибок аутентификации и авторизации.

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

Если бы все параметры были действительны, я бы начал обрабатывать запрос. Если система или какая-либо зависимость (база данных, сторонняя служба, другая внутренняя служба) недоступна, я бы вернул код 5xx - 503 будет определенным, но 500 также будет приемлемым. В любом случае я бы вернул тело с дополнительной информацией. Учтите, что если внешняя зависимость сообщает об истечении времени ожидания запроса 408, я бы съел его и вернул клиенту 500, что позволило бы клиенту получить 408, только если время ожидания запроса к моей системе истекло. Если система сможет выполнить запрос, я верну 200 и соответствующий орган.

204 может быть полезен в некоторых случаях, но он не позволяет отправить тело ответа. Особенно в настройке API, отправка тела ответа с информацией, которая может быть передана в механизм ведения журнала или отчетности, в большинстве случаев кажется правильным решением.

Единственный раз, когда 404 будет возвращен, это если сервер не имеет /deviceinfoконечной точки или /device/:id/infoконечной точки.

Я бы не рассматривал идентификатор, который не найден, таким же, как ресурс, который не найден. Ресурс - это информация об определенном устройстве (в вашем примере). Возврат 404 будет означать, что ресурс (информация об устройстве) не существует. 200 с соответствующим телом означает, что система действительно может предоставить информацию об устройстве. Может быть или не быть устройство с указанным идентификатором.

Томас Оуэнс
источник
1
@RobertHarvey Да. Там не было никаких ошибок. Тот факт, что сервер сгенерировал GUID и отправил его клиенту, не означает, что другая операция удалила его. Запрос / ответ был успешным. Команда или запрос, представленный запросом, не вернули данные. Для меня это не провал. Это может быть исключением, но я не уверен, что это достойно 5xx.
Томас Оуэнс
2
Если бы я разрабатывал этот API, к которому я обращаюсь, я бы вернул 200 с телом, которое затем содержит что-то с эффектом device: 123; status: not found;. Тогда я знаю, что запрос сработал, и что API сообщает мне полезную информацию.
Джелтон
7
@Blrfl Это подтверждается (хотя и не «доказано») формулировками многих веб-сайтов по 500 страниц, обычно что-то вроде «Мы ошиблись и будем расследовать». На мой взгляд, нормально функционирующий сервер никогда не посылает 500, за исключением, возможно, в случае космических лучей.
Мбриг
6
Http не имеет понятия «конечной точки», нет соответствующего различия между переменной и остальной частью URL, и, следовательно, ни один не делает остальных. У вас может быть одна система маршрутизации, которая соответствует регулярному выражению и направляет тысячи URL-адресов на одну функцию контроллера, но это деталь реализации, а не фундаментальная часть API. Ответ 200 на получение - только для случая, когда запрашиваемый ресурс извлекается. В этом случае клиент хочет представление ресурса, который не существует, поэтому 404 является правильным ответом.
BDSL
8
Правильно функционирующая система никогда не отправляет ответ 500. Правильно функционирующий сервер может отправить 500, потому что, если он является частью более крупной системы, и он обнаружил внутреннюю ошибку в этой более крупной системе, например, база данных не работает или другой, зависящий от веб-службы, возвращает бессмысленные результаты.
BDSL
5

Ошибка HTTP серии 500 указывает на неисправность сервера. Помимо 501 Not Implementedи 505 HTTP Version Not Supported, использование этих кодов ошибок подразумевает, что повторная попытка запроса в более позднее время может быть успешной (хотя это только 503 Service Unavailableявно указано). В идеале сервер никогда не должен генерировать один из этих кодов, хотя невозможность написать безошибочное программное обеспечение и обеспечить сервер бесконечными ресурсами означает, что они вам понадобятся время от времени.

Для результата «объект не существует» вы, вероятно, должны вернуть либо 404 Not Found(когда запрос предназначен для объекта по имени), либо 200 Successс пустым телом результата (при поиске объекта по атрибутам). 204 No Contentвыглядит заманчиво, но я бы использовал его только для ситуаций, когда отсутствие тела ответа является ожидаемым результатом.

отметка
источник
1

Как клиент вашего API, когда я делаю один из этих вызовов:

  1. http://example.com/restapi/deviceinfo?id=123
  2. http://example.com/restapi/device/123/info

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

Для API REST я считаю коды состояния 400 и 500 чем-то вроде исключений. Вы используете их, чтобы указать, когда вы не можете вернуть «нормальный» ответ на полученный вами запрос, поэтому клиенту нужно будет сделать что-то исключительное, а не обработать информацию, которую он ожидал получить.

Это означает, что в качестве потребителя API я могу использовать какую-то функцию check-rest-call, которая получает ответ или выдает исключение. Замечательно; моя обычная логика может быть прямым кодом, и я могу организовать обработку ошибок так же, как в локальном коде. Неожиданные 404 будут проявляться как исключения «ресурс не найден», и мне вообще ничего не нужно делать, а не как ошибки «отсутствующего атрибута», когда я позже обработаю их, { errorMessage: "Device 123 not found" }как если бы это был DeviceInfoобъект.

Если причина того, что конечная точка http://example.com/restapi/deviceinfo будет найдена, и это только , id=123что это не так , и так вернуться 200 с сообщением об ошибке в теле, то вы создаете точно такой же вид проблем интерфейса как функции C , которые могут возвращать либо исправить результат или код ошибки, или методы, которые указывают на проблемы путем произвольного возвратаnull, Пользователю вашего интерфейса гораздо приятнее, когда ошибки выводятся через «отдельный» канал от регулярных возвратов. Это применимо и здесь, даже если ответы HTTP 200, 404 и 500 являются одним и тем же каналом с точки зрения низкого уровня. Они стандартизированы и их легко отличить друг от друга, поэтому моя клиентская среда REST может легко включить эти статусы, чтобы превратить их в правильные структуры на моем языке; чтобы сделать то же самое со слоем JSON (где вы всегда говорите 200 и даете мне либо DeviceInfoсообщение об ошибке, либо сообщение об ошибке), мне нужно вложить некоторые знания о схемах JSON, которые вы используете.

Поэтому используйте только 200, когда вы можете вернуть допустимое значение ожидаемого типа (поэтому http://example.com/restapi/search-devices?colour=blueможно возвращать 200 с пустым массивом, если нет синих устройств; пустой массив является допустимым массивом, а разумный ответ на запрос "I хотел бы детали всех синих устройств "). Если вы не можете, используйте наиболее подходящий код состояния, отличный от 200. Несмотря на то, что «устройство 123 не существует» является правильным ответом на «предоставить мне данные устройства 123» и не является ошибкой для сервера , это исключение для ожидания клиента того, что он получит ответ DeviceInfoи должен не сообщайте как нормальный ответ «вот что вы просили».

Бен
источник
К сожалению, я также являюсь клиентом этого API и не могу его изменить. Это отличная сводка того, как это должно работать.
Джелтон
0

Вы можете использовать ошибку 422 Unprocessable Entity, чтобы отличить ее от 404 not found . 422 означает, что сервер понимает запрос, но не может дать правильный ответ. Я использую этот код в похожих ситуациях.

Finex
источник
4
Эта статья описывает использование 422 более подробно. Я не думаю, что этот код ошибки действительно применим здесь.
Роберт Харви
Спасибо, очень полезно! Мне придется углубить эту тему!
FiNeX
0

Ошибка 500 обычно указывает, что запрос потерпел крах программы на стороне сервера; в корпоративной среде эти ошибки рассматриваются как яйцо на лице, и их избегают.

Ошибка 4xx должна сигнализировать программисту, что конечная точка API (ресурс) не существует. Следовательно, как только соответствующая конечная точка достигнута, любая обработка ошибок с этого момента является обязанностью программиста API, которая должна выполняться корректно, то есть с сообщением об ошибке ответа 200.

postronnim
источник
1
Итак, вы говорите: «Не используйте 500, потому что вы не разбиваете сервер?»
Роберт Харви
0

Хотя w3 отмечает, что 404 используется, когда никакой другой ответ не применим , разве это не подходит для ответа 204 (без контента)? Запрос был действителен с точки зрения обработки, и сервер обработал запрос и дал результат. Это успех, который опирается на ответы 2xx. Контента для этого конкретного запроса не было, поэтому 204 сообщает пользователю, что с его запросом все в порядке, но там ничего нет.

Вы также можете сделать слабый случай, что 409 (конфликт) является подходящим ответом. Хотя 409 чаще всего используется для POST, он говорит

Запрос не может быть выполнен из-за конфликта с текущим состоянием ресурса. Этот код разрешен только в ситуациях, когда ожидается, что пользователь сможет разрешить конфликт и повторно отправить запрос. Тело ответа ДОЛЖНО содержать достаточно информации, чтобы пользователь мог распознать источник конфликта. В идеале, объект ответа должен включать в себя достаточно информации, чтобы пользователь или пользовательский агент мог решить проблему; однако это может быть невозможно и не требуется.

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

moneyt
источник
3
Нет, ответ 204 на запрос get будет иметь смысл только тогда, когда запрошенный ресурс будет найден, и его представление будет точно нулевым символом.
BDSL
0

Другие ответы покрывают это, но я просто хотел указать, что ошибка 500-й серии означает «Я испортил». В этом случае «I» означает API, поэтому необработанная ошибка сервера или подобное. Ошибка 400-й серии означает «вы все испортили» - вызывающий API отправил что-то неверное.

Марси
источник
-4

Другие пользователи предоставили правильные ответы на вопрос, что делать, если вы хотите пройтись по книге.

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

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

Существует другой подход: полностью избегать RESTfulness и использовать его просто как протокол связи и ничего больше. Это означает, что единственными ответами, которые необходимо вернуть, являются «HTTP 200 OK» и «HTTP 500 Internal Server Error», поскольку в отношении протокола связи любая попытка установить связь может иметь только два результата: либо запрос был успешным доставлено на сервер или нет.

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

Итак, суть в том, что я бы порекомендовал вернуть «HTTP 200 OK», и в полезной нагрузке ответа («тело содержимого ответа» на языке HTTP) есть прикладной код ошибки, который говорит «ID not found» или что-то еще.

Майк Накис
источник
Судя по отрицательным голосам, наверное, я обидел чувства некоторых людей. Или, может быть, какая-то другая часть их тела. C -: =
Майк Накис
Они должны действительно прочитать это: blogs.dropbox.com/developers/2015/04/…
Майк Накис,
1
Не больно здесь. Ты просто не прав. Даже статья, на которую вы ссылаетесь, не упоминает об использовании кодов состояния настолько явно ошибочно, что ошибки и успех выглядят одинаково. В нем говорится: «Говоря о вкусе, важно помнить, что разработка API не сводится исключительно к практическим последствиям для клиентского и серверного программного обеспечения. Аудитория для API - это разработчик, который собирается его использовать. По принципу наименьшего удивительно, «разработчикам будет легче изучать и понимать API, если он будет следовать тем же соглашениям, что и другие API, с которыми они знакомы».
Цао
@cHao, так что, я полагаю, часть, в которой говорится, что «Dropbox в настоящее время использует около 10 различных кодов состояния (8 ошибок и 2 для успеха), но мы планируем уменьшить это число в будущей версии API», ничего не значило для ты.
Майк Накис
Это не значит, что вы получаете от этого. Если они знают, что делают, то в крайнем случае они сократят это до одного успеха и четырех кодов ошибок (400, 401, 404 и 500), потому что они, очевидно, заботятся о соглашениях.
Цао