Код ответа REST HTTP для отсутствующего родительского ресурса

Мой вопрос: должен ли я возвращать код ответа HTTP 400 Bad Request, 404 Not Found или 410 Gone, если родительский ресурс или выше не существует или был удален?

Ищу некоторые рекомендации о том, как обрабатывать отсутствующие ссылки в дереве ресурсов RESTful, так как я читал довольно много, но не много личного опыта.

Скажем, у нас есть структура ресурсов ниже:

/users/{userId}/accounts/{accoundId}/logs/{logId}

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

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

/users/123/accounts/321/logs - POST
/users/123/accounts/321/logs/987 - GET
/users/123/accounts/321/logs/987 - PUT
/users/123/accounts/321/logs/987 - DELETE
/users/123/accounts/321/logs/987 - PATCH
/users/123/accounts/321/logs/987 - HEAD

Но этого ресурса нет или больше не существует:

/users/123/accounts/321

или этот ресурс не существует или больше не существует:

/users/123

Я мог бы возразить, что это неверный запрос 400, который согласно RFC7231:

6.5.1. ошибка 400, неверный запрос

Код состояния 400 (Bad Request) указывает на то, что сервер не может или не будет обрабатывать запрос из-за чего-то, что воспринимается как ошибка клиента (например, неверный синтаксис запроса, неверный кадр сообщения запроса или обманчивая маршрутизация запросов).

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

Я инстинктивно одолжил бы на 404 Not Found или 410 Gone, даже если бы это не было кешем, поскольку причиной сбоя был буквально отсутствующий/недоступный ресурс. Однако согласно спецификации RFC7231:

6.5.4. 404 Не Найдено

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

or

6.5.9. 410 ушел

Код состояния 410 (Gone) указывает на то, что доступ к целевому
ресурсу
больше недоступен на исходном сервере и что это
состояние, скорее всего, будет постоянным.

Казалось бы, они лгут этому инстинкту.

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


person It's an account    schedule 25.01.2018    source источник
comment
Непонятно, к чему вы ведете обсуждение 404 и 410 и выделение целевого ресурса. В вашем примере /logs/987 будет целевым ресурсом, и, насколько я понимаю, его больше не существует, поэтому 404 будет вполне уместно.   -  person ChrisRibe    schedule 25.01.2018
comment
Верно, я думаю, в случае действий с конкретным журналом, родитель которого был удален (тем самым неявно удалены подресурсы). Но в случае /logs — POST создается ресурс для коллекции журналов на конкретном ресурсе, который не существует/больше не существует.   -  person It's an account    schedule 25.01.2018
comment
Следовательно, коллекция журналов больше не существует, следовательно, 410 (или 404, если ресурс может появиться снова).   -  person ChrisRibe    schedule 25.01.2018


Ответы (3)


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

То есть

GET /users/{userId}/accounts/{accoundId}/logs/{logId}

запросы: текущее выбранное представление для целевого ресурса. См. RFC 7231; в частности, запрос не спрашивает о представлении любого из них:

/users/{userId}/accounts/{accoundId}/logs/
/users/{userId}/accounts/{accoundId}/
/users/{userId}
...

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

Тот факт, что представление /users/{userId}/accounts/{accoundId}/logs/{logId} недоступно, поскольку /users/{userId}/accounts/{accoundId} не существует, является деталем реализации.

404 обычно используется в качестве кода состояния для объявления что текущее представление целевого ресурса недоступно. Любое объяснение того, почему это так, обычно содержится в теле сообщения. Например, вы можете описать ее, используя детали проблемы.

Сервер не обязан отправлять представление ресурса с истекшим сроком действия только потому, что он завалялся (опять же, кеш — это деталь реализации).

410 — это почти одно и то же; это фактически совет, что клиент может пометить свои закладки как устаревшие.

400 — вполне разумный способ выставить лодку, если вы не можете найти код состояния, который лучше соответствует семантике сообщения в полезной нагрузке. Но я бы не стал использовать if в этом случае, когда 404 действительно соответствует вашим потребностям.

person VoiceOfUnreason    schedule 25.01.2018

НЕТ: исключите 400 Bad Request, так как все указанные вами запросы действительны.

НЕТ. Чтобы добавить остроты: я видел, что 409 Conflict возвращается в подобных случаях. Это кажется неуместным в вашей ситуации, поскольку вы ясно указали, что ресурс отсутствует.

ДА: 404 Not Found является наиболее подходящим ответом, если вашего ресурса не существует. Неважно, существовал он когда-либо или нет. 404 указывает на «извините, ресурс недоступен». Я бы добавил сообщение об ошибке, чтобы точно указать, какой ресурс отсутствует, чтобы ваши потребители API могли лучше справиться с ситуацией.

ВОЗМОЖНО. Ошибка 410 Gone — это частный случай ошибки 404. Она должна подниматься, если ресурс недоступен и он существовал до и он (практически) никогда больше не будет существовать. Так что неважно, как часто и когда вы пытаетесь получить ресурс, вы больше никогда его не получите, но в прошлом вы, возможно, могли его получить. То же самое и здесь: если вы выбрали 410, рассмотрите возможность добавления точного сообщения об ошибке.

В личной заметке:

  • Я никогда не видел практически полезной ситуации с 410 Gone, поэтому я избегаю этого и рекомендую своим командам не использовать его, если они не могут найти действительно вескую причину. Выглядит немного академично. В большинстве случаев пользователи API обрабатывают ошибки 404 и 410 одинаково. Они чаще всего не возражают/заботятся. Я могу видеть только редкие пограничные случаи, чтобы понять разницу.
  • DELETE часто не удаляет ресурс. Они деактивируются (делаются недоступными), но технически остаются доступными. Если есть шанс, что ресурс снова станет доступным, например, с помощью новой функции под названием «возврат всех недавно удаленных ресурсов», код 410 уже будет вводить в заблуждение.
  • В ответе 410 также указано, что этот ресурс действительно существовал в прошлом. С точки зрения безопасности данных вы могли бы утверждать, что информация должна храниться внутри и не должна подвергаться разглашению. В этих случаях 410 становится общим запретом для вашего API.
person Quality Catalyst    schedule 25.01.2018
comment
404 — Ресурс не найден и может появиться позже. 410 Ресурс удален и больше не появится. В первом случае клиенты обычно ничего не делают и могут попробовать позже. Во втором случае клиенты должны удалить упоминания этого ресурса на своей стороне. - person user2555515; 29.04.2021

410 удалено

Если /users/123/accounts/321/logs/987 больше не существует, наиболее подходящим ответом будет 410 Gone. Тот факт, что он был удален в результате удаления /users/123, не имеет отношения к запросу /users/123/accounts/321/logs/987.

400 Bad Request был бы более подходящим ответом для чего-то вроде запроса к /users/123/accounts/321/lergs/987.

person ChrisRibe    schedule 25.01.2018
comment
Что делает /users/123/accounts/321/lergs/987 плохим запросом? Это действительный запрос, даже если набор ресурсов lergs может не существовать. - person Quality Catalyst; 25.01.2018