Я создаю RESTful API, который поддерживает постановку в очередь длительных задач для возможной обработки.
Типичный рабочий процесс для этого API:
- Пользователь заполняет форму
- Клиент отправляет данные в API
- API возвращает 202 Принято
- Клиент перенаправляет пользователя на уникальный URL для этого запроса (
/results/{request_id}
) - ~ ~ В конце концов,
- Клиент снова заходит на URL и видит результаты на этой странице.
Моя проблема в шаге 6. Каждый раз, когда пользователь посещает страницу, я отправляю запрос в мой API ( GET /api/results/{request_id}
). В идеале, задача будет выполнена к настоящему времени, и я бы вернул 200 OK с результатами их задачи.
Но пользователи настойчивы, и я ожидаю много чрезмерных обновлений, когда результат еще не закончен.
Какой мой лучший вариант для кода состояния, чтобы указать, что:
- этот запрос существует,
- это еще не сделано,
- но это также не подвело.
Я не ожидаю, что один код сообщит обо всем этом, но я хотел бы, чтобы что-то позволяло мне передавать метаданные вместо того, чтобы клиент ожидал контент.
Возможно, имеет смысл вернуть 202, поскольку здесь это не имело бы никакого другого значения: это GET
запрос, так что ничто не может быть «принято». Это был бы разумный выбор?
Очевидная альтернатива всему этому - которая функционирует, но не справляется с одной целью кодов состояния - будет всегда включать метаданные:
200 OK
{
status: "complete",
data: {
foo: "123"
}
}
...или же...
200 OK
{
status: "pending"
}
Затем на стороне клиента, я бы (вздыхаю) switch
на , response.data.status
чтобы определить , был ли завершен запрос.
Это то, что я должен делать? Или есть лучшая альтернатива? Мне так кажется, что Web 1.0.
POST
запрос открытым. Основная проблема с длинными опросами или веб-сокетами заключается в том, что пользователь может закрыть браузер и вернуться обратно. Я мог бы открыть их снова в это время (и это то, что я делаю), но кажется более чистым иметь один API-интерфейс для вызова, прежде чем я открою эти сокеты, так как возникает крайний случай возникновения этой проблемы.Ответы:
HTTP 202 Принят (HTTP / 1.1)
Вы ищете
HTTP 202 Accepted
статус. См. RFC 2616 :Обработка HTTP 102 (WebDAV)
RFC 2518 предлагает использовать
HTTP 102 Processing
:но у этого есть предостережение:
Я не уверен, как интерпретировать последнее предложение. Должен ли сервер избегать отправки чего-либо во время обработки и отвечать только после завершения? Или это только заставляет завершить ответ только тогда, когда обработка прекращается? Это может быть полезно, если вы хотите сообщить о прогрессе. Отправьте HTTP 102 и очистите ответный байт за байтом (или построчно).
Например, для длинного, но линейного процесса, вы можете отправить сотню точек после каждого символа. Если клиентская сторона (например, приложение JavaScript) знает, что она должна ожидать ровно 100 символов, она может сопоставить ее с индикатором выполнения, который будет показан пользователю.
Другой пример касается процесса, который состоит из нескольких нелинейных этапов. После каждого шага вы можете сбросить сообщение журнала, которое в конечном итоге будет отображаться пользователю, чтобы конечный пользователь мог знать, как идет процесс.
Проблемы с прогрессивной промывкой
Обратите внимание, что хотя у этой техники есть свои достоинства, я бы ее не рекомендовал . Одна из причин заключается в том, что это заставляет соединение оставаться открытым, что может повредить с точки зрения доступности услуг и не очень хорошо масштабируется.
Лучшим подходом является ответить
HTTP 202 Accepted
и либо позволить пользователю вернуться к вам позже, чтобы определить, завершилась ли обработка (например, путем многократного вызова определенного URI, например,/process/result
который будет отвечать HTTP 404 Not Found или HTTP 409 Conflict до процесса). завершается и результат готов), или уведомите пользователя, когда обработка будет завершена, если вы сможете, например, перезвонить клиенту через службу очереди сообщений ( пример ) или WebSockets.Практический пример
Представьте себе веб-сервис, который конвертирует видео. Точка входа:
который берет видеофайл из HTTP-запроса и делает с ним магию. Давайте представим, что магия требует много ресурсов процессора, поэтому ее нельзя сделать в режиме реального времени во время передачи запроса. Это означает, что после передачи файла сервер ответит сообщением
HTTP 202 Accepted
JSON с некоторым содержанием, что означает: «Да, я получил ваше видео, и я над ним работаю; он будет готов где-то в будущем и будет доступен через ID 123. »У клиента есть возможность подписаться на очередь сообщений, чтобы получать уведомления об окончании обработки. После завершения клиент может загрузить обработанное видео, перейдя по ссылке:
что приводит к
HTTP 200
.Что произойдет, если клиент запросит этот URI до получения уведомления? Ну, сервер ответит,
HTTP 404
так как, действительно, видео еще не существует. Это может быть в настоящее время подготовлено. Это никогда не может быть запрошено. Это может существовать некоторое время в прошлом и быть удалено позже. Все, что имеет значение, - то, что получающееся видео не доступно.Теперь, что, если клиент заботится не только о конечном видео, но и о ходе (что было бы еще важнее, если бы не было службы очереди сообщений или какого-либо подобного механизма)?
В этом случае вы можете использовать другую конечную точку:
который привел бы ответ, подобный этому:
Повторное выполнение запроса покажет прогресс до тех пор, пока:
Крайне важно провести различие между этими тремя типами запросов:
POST /video/convert
ставит задачу в очередь Его следует вызывать только один раз: повторный вызов поставит в очередь дополнительную задачу.GET /video/download/123
касается результата операции: ресурсом является видео. Обработка, то есть то, что происходило под капотом для подготовки фактического результата до запроса и независимо от запроса, здесь не имеет значения. Его можно вызвать один или несколько раз.GET /video/status/123
касается обработки как таковой . Это ничего не ставит в очередь. Это не заботится о получающемся видео. Ресурс является самой обработкой. Его можно вызвать один или несколько раз.источник
GET
, хотя? Это, безусловно, правильный выбор для начальногоPOST
, поэтому я использую его. Но это кажется семантически подозрительным для того,GET
чтобы сказать «принято», когда он не принял ничего из этого конкретного запроса.POST
поднимаю задание на очередь, затем я получаюGET
результаты, возможно, после того, как клиент закрыл сеанс. 404 является то , что я рассмотрел , как хорошо, но это кажется неправильным, так как запрос будет найден, он просто не был завершен. Это указывало бы на то, что работа в очереди не была найдена, что является совсем другой ситуацией.GET
часть, не думайте о ней как о неполном запросе , а как о запросе на получение результата операции . Например, если я скажу вам преобразовать видео, и это займет у вас пять минут, запрос на преобразованное видео через две минуты должен привести к HTTP 404, потому что видео просто еще нет. С другой стороны, запрос на выполнение самой операции, вероятно, приведет к HTTP 200, содержащему количество преобразованных байтов, скорость и т. Д.Это правильный путь. Состояние, в котором находятся ресурсы в отношении доменного журнала (бизнес-логика), зависит от типа содержимого представления ресурса.
Здесь объединяются две концепции различий, которые на самом деле разные. Одним из них является состояние передачи состояния между клиентом и сервером ресурса, а другим - состояние самого ресурса в любом контексте, в котором бизнес-домен принимает различные состояния этого ресурса. Последнее не имеет ничего общего с кодами состояния HTTP.
Помните, что коды состояния HTTP соответствуют передаче состояния между клиентом и сервером рассматриваемого ресурса независимо от каких-либо подробностей этого ресурса. Когда вы
GET
ресурс, ваш клиент запрашивает у сервера представление ресурса в текущем состоянии, в котором он находится. Это может быть изображение птицы, это может быть документ Word, это может быть текущая внешняя температура. Протокол HTTP не волнует. Код состояния HTTP соответствует результату этого запроса. Передал лиPOST
клиент с сервера ресурс на сервер, где сервер дал ему URL-адрес, который клиент может просмотреть? Да? Тогда это201 Created
ответ.Ресурсом может быть бронирование авиабилетов, которое в настоящее время находится в состоянии «подлежит проверке». Или это может быть заказ на покупку продукта, который находится в «утвержденном» состоянии. Эти состояния зависят от домена, а не от протокола HTTP. Протокол HTTP имеет дело с передачей ресурсов между клиентом и сервером.
Суть REST и HTTP заключается в том, что протоколы не касаются деталей ресурсов. Это сделано специально, оно не касается проблем, связанных с доменом, поэтому его можно использовать, не зная ничего о проблемах, связанных с доменом. Вы не интерпретируете, что означают коды статуса HTTP в каждом отдельном контексте (система бронирования авиабилетов, система обработки изображений, система видеонаблюдения и т. Д.).
Специфичные для домена вещи предназначены для клиента и сервера, чтобы выяснить между собой на основе
Content Type
ресурса. Протокол HTTP не зависит от этого.Что касается того, как клиент узнает, что ресурс запроса изменил состояние, то опрос - это ваша лучшая ставка, поскольку он сохраняет контроль над клиентом и не предполагает непрерывного соединения. Особенно, если это будет потенциально часами, пока состояние не изменится. Даже если вы сказали, что, черт возьми, с REST, вы просто будете держать соединение открытым, держать его открытым в течение нескольких часов и предполагать, что ничего не пойдет не так, было бы плохой идеей. Что делать, если пользователь закрывает клиента или сеть отключается. Если гранулярность составляет часы, клиент может просто запрашивать состояние каждые несколько минут, пока Запрос не изменится с «ожидающий» на «выполненный».
Надеюсь, что это помогает уточнить вещи
источник
Я нашел предложения из этого блога разумными: ОТДЫХ и длительные работы .
Обобщить:
источник
Код состояния HTTP для ресурса, который еще не доступен, предлагает вернуть ответ на конфликт 409, а не ответ 404, в случае, если ресурс не существует, поскольку он находится в процессе генерации.
Из спецификации w3 :
Это немного неловко, поскольку код 409 «разрешен только в ситуациях, когда ожидается, что пользователь сможет разрешить конфликт и повторно отправить запрос». Я предлагаю, чтобы тело ответа содержало сообщение (возможно, в каком-то формате ответа, соответствующем остальной части вашего API), например: «Этот ресурс в настоящее время создается. Он был инициирован в [ВРЕМЯ] и, по оценкам, завершается в [ВРЕМЯ]. Пожалуйста, Попробуйте позже."
Обратите внимание, что я бы предложил подход 409 только в том случае, если весьма вероятно, что пользователь, запрашивающий ресурс, является также пользователем, инициировавшим генерацию этого ресурса. Пользователи, не участвующие в создании ресурса, сочтут ошибку 404 менее запутанной.
источник