Я создаю API, где пользователь может попросить сервер выполнить несколько действий в одном HTTP-запросе. Результат возвращается в виде массива JSON с одной записью на действие.
Каждое из этих действий может быть неудачным или успешным независимо друг от друга. Например, первое действие может быть выполнено успешно, входные данные для второго действия могут быть плохо отформатированы и не могут быть проверены, а третье действие может вызвать непредвиденную ошибку.
Если бы был один запрос на действие, я бы вернул коды состояния 200, 422 и 500 соответственно. Но теперь, когда есть только один запрос, какой код статуса я должен вернуть?
Некоторые варианты:
- Всегда возвращайте 200, и дайте более подробную информацию в теле.
- Может быть, следовать приведенному выше правилу, только если в запросе более одного действия?
- Может быть, вернуть 200, если все запросы выполнены успешно, в противном случае 500 (или какой-то другой код)?
- Просто используйте один запрос на действие и принимайте дополнительные издержки.
- Что-то совершенно другое?
Ответы:
Короткий прямой ответ
Поскольку запрос говорит о выполнении списка задач (задачи - это ресурс, о котором мы здесь говорим), то если группа задач была перенесена на выполнение (то есть независимо от результата выполнения), то это было бы разумно что статус ответа будет
200 OK
. В противном случае, если возникла проблема, которая помешала бы выполнению группы задач, например, не удалось проверить объекты задачи , или какая-либо необходимая служба, например, недоступна, то статус ответа должен обозначать эту ошибку. В прошлом, когда начинается выполнение задач, учитывая, что задачи для выполнения перечислены в теле запроса, я бы ожидал, что результаты выполнения будут перечислены в теле ответа.Длинный философский ответ
Вы столкнулись с этой дилеммой, потому что вы отклоняетесь от того, для чего был разработан HTTP. Вы не взаимодействуете с ним для управления ресурсами, скорее вы используете его как средство удаленного вызова метода (что не очень странно, но плохо работает без заранее заданной схемы).
С учетом вышесказанного и без смелости превратить этот ответ в подробное руководство, ниже приводится схема URI, которая соответствует подходу управления ресурсами:
/tasks
GET
перечисляет все задачи, разбитые на страницыPOST
добавляет одну задачу/tasks/task/[id]
GET
отвечает объектом состояния одной задачиDELETE
отменяет / удаляет задачу/tasks/groups
GET
перечисляет все группы задач, разбитые на страницыPOST
добавляет группу задач/tasks/groups/group/[id]
GET
отвечает с состоянием целевой группыDELETE
отменяет / удаляет группу задачЭта структура говорит о ресурсах, а не о том, что с ними делать. То, что делается с ресурсами, является заботой другой службы.
Еще один важный момент: желательно, чтобы блок обработчика HTTP-запросов не блокировался слишком долго. Как и в случае с пользовательским интерфейсом, интерфейс HTTP должен быть отзывчивым - в масштабе времени, который на несколько порядков медленнее (потому что этот уровень имеет дело с IO).
Переход к разработке HTTP-интерфейса, который строго управляет ресурсами, вероятно, столь же труден, как и перемещение кнопки из потока пользовательского интерфейса при нажатии кнопки. Требуется, чтобы HTTP-сервер связывался с другими службами для выполнения задач, а не их выполнения в обработчике запросов. Это не поверхностная реализация, это изменение направления.
Некоторые примеры того, как будет использоваться такая схема URI
Выполнение одной задачи и отслеживание прогресса:
POST /tasks
с задачей выполнитьGET /tasks/task/[id]
пока объект ответа не будетcompleted
иметь положительного значения при отображении текущего состояния / прогрессаВыполнение одной задачи и ожидание ее завершения:
POST /tasks
с задачей выполнитьGET /tasks/task/[id]?awaitCompletion=true
пока неcompleted
имеет положительного значения (вероятно, имеет тайм-аут, поэтому это должно быть зациклено)Выполнение группы задач и отслеживание прогресса:
POST /tasks/groups
с группой задач для выполненияGET /tasks/groups/group/[groupId]
покаcompleted
свойство объекта ответа не будет иметь значение, показывая статус отдельной задачи (например, 3 задачи выполнены из 5)Запрос выполнения для группы задач и ожидание его завершения:
POST /tasks/groups
с группой задач для выполненияGET /tasks/groups/group/[groupId]?awaitCompletion=true
пока не ответит с результатом, который обозначает завершение (вероятно, имеет тайм-аут, поэтому должен быть зациклен)источник
Мой голос состоял бы в том, чтобы разделить эти задачи на отдельные запросы. Однако, если слишком много поездок туда и обратно, я столкнулся с HTTP-кодом ответа 207 - Multi-Status
Скопируйте / вставьте из этой ссылки:
источник
207
кажется, что ОП хочет, но я действительно хочу подчеркнуть, что, вероятно, плохая идея иметь такой подход с несколькими запросами в одном. Если проблема заключается в производительности, то вы должны разрабатывать горизонтальные масштабируемые системы в облачном стиле (в этом превосходны системы на основе HTTP)Несмотря на то, что мульти-статус является опцией, я бы вернул 200 (все хорошо), если все запросы были выполнены успешно, и ошибку (500 или, может быть, 207) в противном случае.
Стандартный случай обычно должен быть 200 - все работает. И клиенты должны только проверить это. И только в случае ошибки вы можете вернуть 500 (или 207). Я думаю, что 207 - правильный выбор в случае, по крайней мере, одной ошибки, но если вы видите весь пакет как одну транзакцию, вы также можете отправить 500. - Клиент захочет интерпретировать сообщение об ошибке в любом случае.
Почему не всегда отправлять 207? - Потому что стандартные случаи должны быть простыми и стандартными. Хотя исключительные случаи могут быть исключительными. Клиент должен только прочитать тело ответа и принять дальнейшие сложные решения, если этого требует исключительная ситуация.
источник
Один из вариантов - всегда возвращать код состояния 200, а затем возвращать конкретные ошибки в теле документа JSON. Именно так разработаны некоторые API (они всегда возвращают код состояния 200 и отправляют сообщение об ошибке в теле). Для получения дополнительной информации о различных подходах см. Http://archive.oreilly.com/pub/post/restful_error_handling.html.
источник
200
чтобы указать, что все хорошо, запрос получен и был действителен , а затем использовать JSON, чтобы предоставить подробности о том, что произошло в этом запросе (то есть, результат транзакций).Я думаю, что neilsimp1 верен, но я бы порекомендовал изменить структуру отправляемых данных таким образом, чтобы вы могли отправить их
206 - Accepted
и обработать позже. Возможно с обратными вызовами.Проблема с попыткой отправить несколько действий в одном запросе заключается именно в том, что каждое действие должно иметь свой собственный «статус»
Глядя на импорт CSV (я не знаю, что такое OP, но это простая версия). ПОСТАВЬТЕ CSV и верните 206. Затем позже CSV можно будет импортировать, и вы можете получить статус импорта с помощью GET (200) для URL, который отображается для каждой строки ошибок.
Этот же шаблон может быть применен ко многим групповым операциям
Код, который обрабатывает POST, должен только проверить, что формат данных операций является допустимым. Затем в более позднее время операции могут быть выполнены. Например, на заднем плане, так что вы можете легче масштабировать. Затем вы можете проверить статус операций, когда захотите. Вы можете использовать опросы или обратные вызовы, или потоки или что-то еще, чтобы удовлетворить потребность знать, когда набор операций завершен.
источник
Здесь уже много хороших ответов, но отсутствует один аспект:
Какой контракт ожидают ваши клиенты?
Коды возврата HTTP должны указывать, по крайней мере, различие успеха / неудачи и, таким образом, играть роль «исключений для бедняков». Тогда 200 означает «контракт полностью выполнен», а 4xx или 5xx означают невыполнение.
Наивно, я бы ожидал, что контракт вашего запроса с несколькими действиями будет «выполнять все мои задачи», и если одно из них не будет выполнено, то запрос не будет (полностью) успешным. Как правило, как клиент я понимаю 200 как «все в порядке», а коды из семейства 400 и 500 заставляют меня задуматься о последствиях (частичного) сбоя. Таким образом, используйте 200 для «всех выполненных задач» и 500 плюс описательный ответ в случае частичного сбоя.
Другой гипотетический контракт может быть «попытаться выполнить все действия». Тогда это полностью соответствует контракту, если (некоторые из) действий не удаются. Таким образом, вы всегда будете возвращать 200 плюс документ с результатами, где вы найдете информацию об успехе / неудаче для отдельных задач.
Итак, каким контрактом вы хотите следовать? Оба действительны, но первый (200 только в том случае, если все было сделано) для меня более интуитивен и лучше соответствует типичным шаблонам программного обеспечения. И для (надеюсь) большинства случаев, когда служба выполняла все задачи, клиенту легко обнаружить этот случай.
Последний важный аспект: как вы сообщаете своим клиентам о своем контрактном решении? Например, в Java я бы использовал имена методов, такие как "doAll ()" или "tryToDoAll ()". В HTTP вы можете соответствующим образом называть URL-адреса конечных точек, надеясь, что ваши клиентские разработчики увидят, прочитают и поймут названия (я бы не стал на это ставить). Еще одна причина, чтобы выбрать контракт наименее неожиданным.
источник
Ответ:
Код состояния описывает состояние одной операции. Следовательно, имеет смысл иметь одну операцию на запрос.
Несколько независимых операций нарушают принцип, на котором основаны модель запроса-ответа и коды состояния. Вы боретесь с природой.
HTTP / 1.1 и HTTP / 2 значительно снизили количество HTTP-запросов. Я полагаю, что очень мало ситуаций, когда рекомендуется группировать независимые запросы.
Это сказало,
(1) Вы можете сделать несколько модификаций с помощью запроса PATCH ( RFC 5789 ). Однако для этого необходимо, чтобы изменения не были независимыми; они применяются атомарно (все или ничего).
(2) Другие указали на код 207 Multi-Status. Однако это определено только для WebDAV ( RFC 4918 ), расширения HTTP.
207 XML-ответ WebDAV будет таким же странным, как утка в не-WebDAV API. Не делай этого.
источник
Если вам действительно нужно иметь несколько действий в одном запросе, почему бы не обернуть все действия в транзакции в бэкэнд? Таким образом, они либо все преуспевают, либо все проваливаются
Как клиент, использующий API, я могу иметь дело с полным успехом или неудачей при вызове API. С частичным успехом трудно иметь дело, так как мне пришлось бы обрабатывать все возможные результирующие состояния.
источник