RESTful API - це відсутність речі


18

Уявіть собі API, щоб визначити, чи вибрала людина свою духовну тварину. У них можуть бути лише нульові або одні спиртні тварини.

В даний час:

/person/{id}/selectedSpiritAnimal

коли вони вибрали, тварина повертає http 200 і {selectedAnimal:mole}

але коли вони не мають вибору, він повертає http 404.

Це робить мою спиртну тварину нещасною, оскільки ми представляємо дійсну проблему домену - ще не вибравши спиртну тварину - як помилку HTTP.

Крім того, як бізнес - Erm 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-адресиSpiritAnimal, тому він не помилився.

Консенсус серед моїх колег полягає в тому, що це симптом поганого дизайну API

Напевно, було б краще, щоб ми просто просили / person / {id}, і це повертає набір зв’язків для особи ... тоді, якщо вам не надано / selectedSpiritAnimal (якщо людина не має вибору), але ви називати це все одно, то 404 має сенс. Або що ви реалізуєте часткові відповіді і нехай / person / {id} повертає більш повний документ, якщо клієнт не вимагає підмножини даних


5
Ви не пояснили, чому ви вважаєте, що це питання повернення 404. З точки зору домену, ви не знаєте, отримали ви 404 або 200, це абстрагується на рівні клієнта.
Вінсент Савард

14
а може, краще повернути 204 без вмісту?
Пол Д'Амбра

6
Я думаю, що я переживаю 404 - це роз'яснення зміни конфігурації, яка вводить неправильний шаблон URL-адреси від людини, яка не має духовної тварини
Пол Д'Амбра

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, а не (a) рамки, побудованої поверх неї, або (b) випадкової публікації в блозі.
Ерік Штейн

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 (просто не виникає питання з ідентифікатором "єдиноріг").
користувач253751

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"
}

Тепер давайте уявимо собі людину на ім’я Фред, у якої ідентифікатор 456 і жодна духовна тварина.

GET /person/456

повернувся б;

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

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


1
Просто додав до запитання, що, враховуючи можливість зміни api, певний рівень HATEOAS, ймовірно, буде правильним рішенням. Це прекрасний приклад тому
Пол Д'Амбра

1

Це відповідна URL-адреса для отримання спиртних тварин; тому помилка 404 недоцільна. 404 - це представлення технічної проблеми, а не логічної проблеми.

Відповідне рішення - повернути http 200 і {"selectedAnimal": null}

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

Краще уникати поєднання роздільних запитів в одному веб-методі без вагомих причин для цього, навіть якщо запити тісно пов'язані.


1
Чи можете ви пояснити, чому ви вважаєте, що це відповідне рішення? Я не можу мати сенсу повертати дані, коли немає даних, які потрібно повернути. Я погоджуюся з вами, що 404 не має сенсу, оскільки URL-адресою добре, тому 204, здається, має для мене найбільш сенс.
Вінсент Савард

2
@ Коди статусу @VincentSavard складніше працювати, ніж відповіді json, і більше підходять для технічних питань, а не для передачі інформації про домен.
TheCatWhisperer

2
204: Немає вмісту з порожнім тілом. Це все зрозуміло і зрозуміло. Не потрібно більше сперечатися. Загалом, коли немає чіткого шаблону проектування, ІП повинен вибрати один, зігнути його до своєї потреби та надати документацію. Повернення тіла з відповіддю 204 не є наслідком.
formixian

2
@formixian ... Якби ви створили apjs 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'ключ, він відображається лише для наочності; рядок vs nullдосить для розрізнення.


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 є спосіб представити неіснуюче, і саме це я б використав.

EDIT: Припустимо, ви додали функцію, де користувачі могли вибрати, чи слід ділитися своїми духовими тваринами, а хтось не поділився нею. Ви повернете 200 ОК null, або 200 ОК "Unauthorized? Або ви використовуєте стандарт 401 Несанкціонований / 403 Заборонений? Це здається безпосередньо аналогічним тому, що вибирати не в першу чергу.


Якщо ви хочете використати 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 клієнтських клієнтів можуть перетворити 404 у стандартний механізм обробки помилок (код помилки, виняток, тип помилки), тобто вам доведеться придушити сторонні помилки.
jpmc26

@Paul Draper Прокрутіть трохи деталі вгорі, ви побачите, що вона робить ковдру заяву про HTTP 4xx: "Клас коду статусу 4xx призначений для випадків, коли клієнт, здається, помилився". Оператор чітко заявив, що відсутність духової тварини є дійсним станом, для якого API повинен враховувати. tools.ietf.org/html/rfc7231#section-6.5
Брендон Арнольд,

Тоді як ви розрізняєте вказівку на відсутність запитуваного ресурсу (тобто не вибрано тварину) та клієнта, який запитує недійсний шлях (тобто /person/{id}/selectedSpritAnimalдотримуйтесь помилки введення тексту Sprit).
sampathsris

1
Незалежно від наміру, 404 строго означає, що ресурс не був знайдений. Якщо вибранийSpiritAnimal є ресурсом, а жоден не існує, то GET нічого не має, а 404 підходить. Однак якщо клієнт хоче знати, чи було обрано духовну тварину, то сервер повинен викрити інший ресурс, /person/{id}/isSpiritAnimalSelectedякий повідомив би клієнта, чи був обраний той. Мені здається той самий класичний приклад тестування того, чи існує файл до його читання. Просто прочитайте файл і обробіть помилку ENOENT у разі його викидання.
aaaaaa

1
@PaulDraper Справа не в тому, існує чи ні ресурс. Справа в тому, що коди HTTP 4xx призначені для того, коли абонент використовує API неправильно. Оп заявляє, що /person/{id}/selectedSpiritAnimalпризначений для використання, навіть якщо духової тварини не існує. Це означає, що HTTP 4xx є неправильним, коли використовується в такому випадку. Op також правильно стверджує, що це може бути запахом поганого дизайну API.
Брендон Арнольд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.