RESTful API представляет отсутствие вещи

18

Представьте себе API, чтобы определить, выбрал ли человек свое духовное животное. У них может быть только ноль или одно духовное животное.

В настоящее время:

/person/{id}/selectedSpiritAnimal

когда они выбрали животное возвращает http 200 и {selectedAnimal:mole}

но когда у них нет выбора, возвращается http 404.

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

Кроме того, как бизнес, а именно Sprit-Animal-Hampers-R-us, мы хотим знать, когда у кого-то нет выбора, поэтому мы можем подсказать им.

Какой лучший ответ здесь:

HTTP 200 и {selectedAnimal:null}

или даже более явно

HTTP 200 и {selectedAnimal:null, spiritAnimalSelected: false}

Или лучше вернуть 404? Так как this image has not yet been uploadedпри просмотре изображения онлайн было бы 404. this person has not selected a spirit animalможет быть 404


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

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

Так что это не «бизнес-логика», а скорее обстоятельство, при котором отсутствие вещи имеет значение (возможно, многие мои коллеги утверждают, что 404 по-прежнему верен), но я не уверен, как сопоставить это с спекуляция


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

Дело в том, что для меня это решается тем, что в спецификации говорится, что 4xx - это ошибка клиента . В этом случае клиенту было сказано ожидать ответа от URL выбранного selectedSpiritAnimal, поэтому он не допустил ошибку.

Консенсус среди моих коллег в том, что это признак плохого дизайна API

Вероятно, было бы лучше, если бы мы просто запросили / person / {id}, и это возвращает набор отношений ссылки для человека ... тогда, если вам не дают ссылку / selectedSpiritAnimal (когда у человека нет выбора), но вы в любом случае, называть это 404 имеет смысл. Или что вы реализуете частичные ответы и позволяете / person / {id} возвращать более полный документ, если клиент не запрашивает подмножество данных

Пол д'Амбра
источник
5
Вы не объяснили, почему вы считаете, что возвращать 404 является проблемой. С точки зрения домена вы не знаете, получили ли вы 404 или 200, абстрагированные от уровня клиента.
Винсент
14
или может лучше 204 вернуть без контента?
Пол
6
Я полагаю, что мое беспокойство по поводу 404 - устранение неоднозначности изменения конфигурации, которое вводит
Paul D'Ambra
2
@ PaulD'Ambra Для чего бы это ни стоило, я бы не стал использовать никакой контент. Я не видел HTTP-кодов, обрабатываемых таким образом во внешнем интерфейсе в Javascript со времен XmlHttpRequest. Но это конечно верно.
Брэндон Арнольд

Ответы:

24

Коды HTTP 4xx, вероятно, не являются правильным выбором для этого сценария. Вы утверждаете, что наличие животных с нулевым духом является действительным состоянием, и маршрут API person/{id}/selectedSpiritAnimalбудет учитывать, есть ли у человека idтакой или нет.

Ответы HTTP 4xx зарезервированы для ситуации, когда клиент сделал что-то неправильное в запросе (см . Архив оригинальной спецификации w3 ). Но клиент делает правильный запрос, независимо от того, idесть у человека духовное животное или нет .

Поэтому я склоняюсь ко вторым решениям, используя правильно отформатированное тело JSON в ответе и код HTTP 2xx.

Теперь, если вы получаете такой запрос, и оказывается, что человек idне существует, код 4xx имеет больше смысла.

Брэндон Арнольд
источник
17
«Ответы HTTP 4xx зарезервированы для ситуации, когда клиент сделал что-то неправильное в запросе». Делая авторитетные заявления о спецификации, пожалуйста, ссылайтесь на реальную HTTP-спецификацию, а не (а) основанную на ней платформу или (б) случайную запись в блоге.
Эрик Стейн
5
@EricStein Мне было немного лень об этом; спасибо за критику. Обновлено.
Брэндон Арнольд
1
Я чувствую, что ссылка на RFC здесь противоречива. С одной стороны, 4xx говорит cases in which the client seems to have erred, а с другой - 404 The server has not found anything matching the Request-URI. Вы можете запросить что-то, что не существует ошибки клиента. Я понимаю, что человек не существует против подпора не существует, но это может быть указано в ответном сообщении. 204 также представляется уместным, поскольку сущность существует, но не имеет содержимого для подчиненного свойства.
shortstuffsushi
1
Клиент сделал что-то не так; он запрашивал выбранного духовного животного для пользователя, когда он не существует. Это именно то, что означает 404 ... вы просили что-то, чего там нет.
Энди
5
Когда мой браузер отправляет запрос на softwareengineering.stackexchange.com/questions/unicorn , это действительный запрос, и все же я получаю 404 (просто не возникает вопроса с идентификатором «единорог»).
user253751
24

Позвольте мне представить вам модель зрелости Ричардсона .

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

Ваш человек должен жить под URI, /person/{id}а животное должно жить под /spiritanimal/{id}. Человек должен указать, что у него есть духовное животное, используя ссылку на животное.

Представим себе человека по имени Боб, у которого есть идентификатор 123 и духовное животное Единорог.

GET /person/123

вернется;

{
  "name": "Bob",
  "links": [
    {
      "rel": "spiritanimal",
      "uri": "/spiritanimals/789"
    }
  ]
}

Теперь любой, кто читает человека 123, будет знать, что у него есть духовное животное, и у него есть URI, где он может получить больше информации о нем.

GET /spitiranimal/789

может вернуться

{
  "type": "Unicorn"
}

Теперь давайте представим человека по имени Фред, у которого id 456 и у которого нет духовного животного.

GET /person/456

вернется;

{
  "name": "Fred",
  "links": [
  ]
}

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

Qwerky
источник
1
Я просто добавил к вопросу, что, учитывая возможность изменения API, некоторый уровень HATEOAS, вероятно, будет правильным решением. Это отличный пример этого
Пол
1

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

Соответствующее решение - вернуть http 200 и {"selectedAnimal": null}

У вас должен быть отдельный веб-метод, /person/{id}/hasSelectedSpiritAnimalкоторый возвращается {"isSpiritAnimalSelected": false}. За кулисами он может или не может делать одни и те же вызовы методов, просто возвращая false, если ноль, но это решать, а не код потребления.

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

TheCatWhisperer
источник
1
Не могли бы вы объяснить, почему вы считаете это подходящим решением? Я не могу иметь никакого смысла возвращать данные, когда нет данных, которые должны быть возвращены. Я согласен с вами, что 404 не имеет смысла, так как с URL-адресом все в порядке, поэтому 204, кажется, имеет для меня наибольшее значение.
Винсент
2
@ VincentSavard С кодами состояния работать сложнее, чем с ответами json, и они больше подходят для технических вопросов, чем для передачи информации о домене.
TheCatWhisperer
2
204: Нет содержимого с пустым телом. Это все понятно и говорит само за себя. Не надо спорить дальше. В общем, когда нет четкого шаблона проектирования, SE должен выбрать его, согнуть в соответствии со своими потребностями и предоставить документацию. Возвращение тела с ответом 204 не является следствием.
Formixian
2
@formixian ... Если бы вы создавали api bjson / tcp вместо json / http, что бы вы вернули в этом случае? Полагаясь на http-коды, мне кажется, что излишне привязывать результаты API к его http-реализации.
TheCatWhisperer
2
@ VincentSavard When you said "the spirit animal is not currently on file", it seemed quite similar to me to "there is no content"Нет контента означает, что нет контента . т.е. он возвращает "". Эти двое совсем не похожи. «Духовное животное в настоящее время отсутствует в архиве» сильно отличается от «».
Шейн
1

То, что представляет ваша конечная точка, не просто животное; это животное или его отсутствие. Это значение лучше всего представляется через Optional/ Maybe/ Nullable/ и т. Д.

Таким образом, допустимые значения (как в 200 ОК) могут быть:

  • {'animal': <some animal>, 'selected': true}
  • {'animal': null, 'selected': false}

Я мог бы представить, что этот DELETEметод при применении к конечной точке может снова установить 'selected'значение false, то есть сбросить выбранное животное.

Вы можете, конечно, опустить 'selected'ключ здесь, это показано только для ясности; Строка против nullдостаточно для различия.

9000
источник
0

Вы должны использовать 404.

Код состояния 404 (не найден) указывает, что исходный сервер не нашел текущего представления для целевого ресурса

RFC7231

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 404 Not Found
Content-Type: text/plain
Date: Mon, 25 Sep 2017 00:00:00 GMT

this person has not selected a spirit animal

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

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

РЕДАКТИРОВАТЬ: Предположим, вы добавили функцию, где пользователи могли выбирать, делиться ли они своими духовными животными, а кто-то не делился ею. Вы бы вернули 200 ОК nullили 200 ОК "Unauthorized? Или вы бы использовали стандартный 401 Unauthorized / 403 Forbidden? Это похоже на прямую аналогию с тем, чтобы не выбирать никого в первую очередь.


В качестве альтернативы, если вы хотите использовать 200 OK + JSON, вы должны вернуться null.

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

"mole"

HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 25 Sep 2017 00:00:00 GMT

null

Держите вещи в чистоте. Создайте больше упаковки только в случае необходимости.

Пол Дрэйпер
источник
2
Данные в найдены, хотя. Просто бывает, что данные есть null(не имеет значения). Это отличается от того, что клиент запрашивает что-то, для чего не существует слота для хранения значения. Вы не предлагаете бросать AttributeErrorв Python, когда значение оказывается None; это сделало бы интерфейс непрактичным и излишне сложным для работы. Более прагматично: многие клиентские API-интерфейсы HTTP могут преобразовать 404 в стандартный механизм обработки ошибок языка (код ошибки, исключение, тип ошибки), что означает, что вам придется подавлять постороннюю ошибку.
jpmc26
@Paul Draper Пролистав чуть-чуть в спецификации, вы увидите, что он делает общее заявление о HTTP 4xx: «Класс кода состояния 4xx предназначен для случаев, когда клиент, похоже, допустил ошибку». Операция ясно заявила, что отсутствие животного-духа является допустимым состоянием, за которое должен отвечать API. tools.ietf.org/html/rfc7231#section-6.5
Брэндон Арнольд,
Как тогда вы различаете указание на отсутствие запрошенного ресурса (т.е. не выбрано животное) и клиента, запрашивающего неверный путь (т. /person/{id}/selectedSpritAnimalЕ. Наблюдаете опечатку Sprit).
Сампатрисрис
1
Независимо от намерения, 404 строго означает, что ресурс не был найден. Если selectedSpiritAnimal является ресурсом, и ни один из них не существует, то нет ничего, чтобы получить и 404 является подходящим. Однако, если клиент хочет знать, было ли выбрано духовное животное, тогда сервер должен предоставить другой ресурс, /person/{id}/isSpiritAnimalSelectedкоторый сообщит клиенту, был ли он выбран. Мне кажется, это тот же классический пример тестирования, существует ли файл перед его чтением. Просто прочитайте файл и обработайте ошибку ENOENT, если она будет выдана.
аааааа
1
@PaulDraper Дело не в том, существует ли ресурс. Дело в том, что коды HTTP 4xx предназначены для случаев, когда вызывающая сторона использует API неправильно. Оператор утверждает, что /person/{id}/selectedSpiritAnimalпредполагается использовать, даже если духовное животное не существует. Это означает, что HTTP 4xx неверен, когда используется в таком случае. Op также правильно утверждает, что это может быть запахом плохого дизайна API.
Брэндон Арнольд