Код статусу HTTP для "Ще обробляється"


47

Я будую API RESTful, який підтримує чергування тривалих завдань для можливої ​​обробки.

Типовим робочим процесом для цього API буде:

  1. Користувач заповнює форму
  2. Клієнт публікує дані в API
  3. API повертає 202 Прийнято
  4. Клієнт перенаправляє користувача до унікальної URL-адреси для цього запиту ( /results/{request_id})
  5. ~ зрештою ~
  6. Клієнт знову відвідує URL-адресу та бачить результати на цій сторінці.

Моя проблема полягає в кроці 6. Щоразу, коли користувач відвідує сторінку, я подаю запит у свій API ( GET /api/results/{request_id}). В ідеалі завдання вже буде завершено, і я поверну 200 ОК з результатами їх завдання.

Але користувачі наполегливі, і я сподіваюся, що багато переборливих оновлень, коли результат ще не закінчена обробка.

Який найкращий варіант для коду статусу, щоб вказати:

  • цей запит існує,
  • це ще не зроблено,
  • але це також не вийшло з ладу.

Я не очікую, що єдиний код передасть усе це, але мені б хотілося щось, що дозволяє мені передавати метадані, а не клієнту очікувати контенту.

Можливо, має сенс повернути 202, оскільки це не мало б тут іншого значення: це GETзапит, тому нічого, можливо, "не прийнято". Це був би розумний вибір?

Очевидною альтернативою всьому цьому - що функціонує, але перемагає одне призначення кодів статусу - було б завжди включати метадані:

200 OK

{
    status: "complete",
    data: {
        foo: "123"
    }
}

... або ...

200 OK

{
    status: "pending"
}

Потім на стороні клієнта, я б (зітхаю) switchна , response.data.statusщоб визначити , чи був завершений запит.

Це те, що я повинен робити? Або є краща альтернатива? Це просто мені так Web 1.0.


1
Чи не 1xx-коди зроблені саме для цієї мети?
Енді

@Andy я дивився на 102, але це для речей WebDAV. Крім цього, ні ... Вони в основному для транзитного зв'язку. Корисно для переходу на веб-розетки тощо.
Меттью Гоген

Про яку затримку ви говорите? 10 секунд? Або 6 годин? Якщо затримки короткі і, як правило, в межах одного відвідування браузера, ви можете робити довгі опитування або веб-розетки, а не періодичні опитування.
GrandmasterB

@GrandmasterB Це потенційно години. Я не несу відповідальності за саму обробку роботи, тому не маю по-справжньому хорошої оцінки, але це пройде деякий час. Інакше я б просто залишив перший POSTзапит відкритим. Основна проблема довгих опитувань або веб-розеток полягає в тому, що користувач може закрити браузер і повернутися назад. Я могла б відкрити їх знову (і це те, що я роблю), але здається, чистішим є єдиний API для виклику, перш ніж відкривати ці розетки, оскільки ця проблема не виникає.
Меттью Гоген

Відповіді:


48

Прийнято HTTP 202 (HTTP / 1.1)

Ви шукаєте HTTP 202 Acceptedстатус. Див. RFC 2616 :

Запит прийнято для обробки, але обробка не завершена.

Обробка HTTP 102 (WebDAV)

RFC 2518 пропонує використовувати HTTP 102 Processing:

Код стану 102 (Обробка) - проміжна відповідь, яка використовується для інформування клієнта про те, що сервер прийняв повний запит, але ще не виконав його.

але він має застереження:

Сервер ПОВИНЕН надіслати остаточну відповідь після завершення запиту.

Я не впевнений, як інтерпретувати останнє речення. Чи повинен сервер уникати нічого надсилання під час обробки та відповідати лише після його завершення? Або це лише примушує припинити відповідь лише тоді, коли обробка закінчується? Це може бути корисно, якщо ви хочете повідомити про прогрес. Надішліть HTTP 102 і обведіть байт відповіді за байтом (або рядком за рядком).

Наприклад, для тривалого, але лінійного процесу ви можете надіслати сто крапок, промиваючи після кожного символу. Якщо клієнтська сторона (наприклад, програма JavaScript) знає, що вона повинна очікувати рівно 100 символів, вона може співставити її з панеллю прогресу, яка буде показана користувачеві.

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

Проблеми з прогресивним промиванням

Зауважте, що хоча ця методика має свої достоїнства, я б не рекомендував її . Однією з причин є те, що воно змушує зв’язок залишатися відкритим, що може зашкодити з точки зору доступності послуги та не змінювати масштаби.

Кращим підходом є відповідь HTTP 202 Acceptedі або дозволити користувачеві повернутися до вас пізніше, щоб визначити, чи закінчилася обробка (наприклад, повторним викликом даного URI, такого, /process/resultякий би відповів HTTP 404 Not Found або HTTP 409 Conflict до процесу завершується, і результат готовий), або повідомляйте користувача, коли обробка завершена, якщо ви зможете зателефонувати клієнту, наприклад, через службу черги повідомлень ( приклад ) або WebSockets.

Практичний приклад

Уявіть веб-сервіс, який конвертує відео. Точка входу:

POST /video/convert

який бере відео-файл із HTTP-запиту і виконує з ним деяку магію. Давайте уявимо, що магія є інтенсивною процесором, тому її неможливо зробити в режимі реального часу під час передачі запиту. Це означає, що щойно файл буде передано, сервер відповість на нього HTTP 202 Acceptedз деяким вмістом JSON, тобто "Так, я отримав ваше відео, і я працюю над ним; він буде готовий десь у майбутньому та буде доступний через ідентифікатор 123. "

Клієнт має можливість підписатися на чергу повідомлень, про яку буде повідомлено, коли обробка закінчиться. Після її завершення клієнт може завантажити оброблене відео, перейшовши до:

GET /video/download/123

що призводить до ан HTTP 200.

Що станеться, якщо клієнт запитує цей URI, перш ніж отримувати сповіщення? Ну, сервер відповість, HTTP 404оскільки, справді, відео ще не існує. Наразі вона може бути підготовлена. Це може ніколи не просити. Він може існувати певний час в минулому і бути видалений пізніше. Важливо лише те, що отримане відео недоступне.

Тепер, що робити, якщо клієнт дбає не лише про остаточне відео, а й про прогрес (що було б ще важливіше, якщо немає служби черги повідомлень чи будь-якого подібного механізму)?

У цьому випадку ви можете використовувати іншу кінцеву точку:

GET /video/status/123

що призведе до подібної відповіді:

HTTP 200
{
    "id": 123,
    "status": "queued",
    "priority": 2,
    "progress-percent": 0,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Виконання запиту знову і знову покаже прогрес, поки він не стане:

HTTP 200
{
    "id": 123,
    "status": "done",
    "progress-percent": 100,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Важливо змінити ці три типи запитів:

  • POST /video/convertчерги завдання. Його слід викликати лише один раз: зателефонувавши ще раз, це поставило б чергу в додаткове завдання.
  • GET /video/download/123стосується результату операції: ресурсом є відео. Обробка - ось що сталося під кришкою для підготовки фактичного результату до запиту та незалежно від запиту - тут не має значення. Його можна викликати один або кілька разів.
  • GET /video/status/123стосується обробки як такої . Він нічого не чергає. Це не хвилює отримане відео. Ресурс є найбільш обробкою. Його можна викликати один або кілька разів.

1
Чи є сенс 202 у відповіді на a GET, однак? Це, безумовно, правильний вибір для початкового POST, саме тому я його використовую. Але це здається семантично підозрілим для того, GETщоб сказати "прийнятим", коли він не прийняв нічого з цього конкретного запиту.
Меттью Гоген

2
@MainMa Як я вже говорив, я працюю над чергою POST, тоді я GETрезультати, можливо, після того, як клієнт закрив сесію. 404 є те , що я розглянув , як добре, але це здається неправильним, тому що запит буде знайдений, він просто не був завершений. Це вказувало б на те, що робота в черзі не знайдена, що є зовсім іншою ситуацією.
Меттью Гоген

1
@MatthewHaugen: коли ви виконуєте GETдеталь, не думайте про це як неповний запит , а як прохання отримати результат операції . Наприклад, якщо я скажу вам перетворити відео, і на це знадобиться п'ять хвилин, запит на перетворене відео через дві хвилини має призвести до HTTP 404, тому що відео просто ще немає. З іншого боку, запит на хід самої операції, ймовірно, призведе до отримання HTTP 200, який містить кількість перетворених байтів, швидкість тощо.
Арсеній Муренко,

5
Код статусу HTTP для ресурсу ще не доступний, пропонує повернути відповідь на конфлікт 409 ("Запит не вдалося виконати через конфлікт із поточним станом ресурсу."), А не відповідь 404, якщо ресурс не відповідає не існувати, тому що це в середині породження.
Брайан

1
@Brian Ваш коментар дасть розумну відповідь на це питання. Хоча я б тоді відповів "[t] його код дозволений лише в тих ситуаціях, коли очікується, що користувач зможе вирішити конфлікт і повторно подати запит", що в моєму випадку не зовсім вірно, але це здається менш помилковий, ніж "не знайдено". Частина мене схиляється до 409 із закріпленим заголовком Retry-After. Єдине питання полягає в тому, що здається дивним повернути 409 для GET, але я можу жити з цим дивацтвом - навряд чи це буде визначено інакше в майбутньому.
Метью Хауген

5

Очевидною альтернативою всьому цьому - що функціонує, але перемагає одне призначення кодів статусу - було б завжди включати метадані:

Це правильний шлях. Стан ресурсів, що стосується журналу, визначеного для домену (він же логіка бізнесу), є предметом змістового типу представлення ресурсу.

Тут пов'язані два поняття різниці, які насправді різні. Один - це стан передачі стану між клієнтом і сервером ресурсу, а другий - стан самого ресурсу в будь-якому контексті, що ділова сфера приймає різні стани цього ресурсу. Останнє не має нічого спільного з кодами статусу HTTP.

Пам'ятайте, що коди статусу HTTP відповідають передачі стану між клієнтом та сервером ресурсу, яким обробляється, незалежно від будь-яких деталей цього ресурсу. Коли ви GETкористуєтесь ресурсом, ваш клієнт просить сервер представити ресурс у поточному стані, в якому він знаходиться. Це може бути зображення птаха, це може бути документ Word, це може бути поточний зовнішній темп. Протокол HTTP не хвилює. Код статусу HTTP відповідає результату цього запиту. Чи POSTпереніс клієнт на сервер ресурс на сервер, де сервер надав йому URL-адресу, яку може переглядати клієнт? Так? Тоді це 201 Createdвідповідь.

Ресурсом може бути бронювання авіакомпанії, яка наразі перебуває у стані "для перевірки". Або це може бути замовлення на придбання товару, яке знаходиться у "затвердженому" стані. Ці стани залежать від домену, а не для протоколу HTTP. Протокол HTTP займається передачею ресурсів між клієнтом і сервером.

Сенс REST і HTTP полягає в тому, що протоколи не стосуються себе деталями ресурсів. Це навмисно, воно не стосується конкретних проблем домену, щоб ним можна було користуватися, не знаючи нічого про конкретні проблеми домену. Ви не переосмислюєте, що означають коди статусу HTTP у кожному різному контексті (система бронювання авіакомпаній, система обробки уявлених даних, система відеоспостереження тощо).

Інформація, що стосується домену, призначена для того, щоб клієнт і сервер розібралися між собою на основі Content Typeресурсу. Протокол HTTP є агресивним до цього.

Щодо того, як клієнт з'ясовує, що ресурс запиту змінився, опитування - це найкраща ставка, оскільки він зберігає контроль у клієнта і не передбачає безперервного зв'язку. Особливо, якщо це буде потенційно години до зміни стану. Навіть якщо ви сказали на пекло з REST, ви просто збираєтесь тримати зв’язок відкритим, тримати його відкритим годинами і припускаючи, що нічого не піде не так, було б поганою ідеєю. Що робити, якщо користувач закриває клієнта або вимикається мережа. Якщо деталізація становить години, клієнт може просто запитувати стан кожні кілька хвилин, поки Запит не зміниться з "очікуваний" на "виконаний".

Сподіваюсь, що допомагає з’ясувати речі


"Чи передавав POST від клієнта на сервер ресурс на сервер, де сервер надав йому URL-адресу, яку може переглядати клієнт? Так? Тоді це 201 відповідь." 202 Прийнято також є прийнятною як відповідь на це, якщо сервер не може негайно діяти для обробки ресурсу, що і робить ОП.
Енді

1
Справа в тому, що сервер діє негайно. Він створює ресурс з URL-адресою негайно. Просто стан ресурсу є "в очікуванні" (або щось таке). Це стан домену бізнесу. Що стосується протоколу HTTP, то сервер діяв, як тільки він створив ресурс і дав клієнтові URL-адресу ресурсу. Ви можете отримати цей ресурс. Сам запит POST не очікується. Це те, що я маю на увазі, зберігаючи два різних концептуальні області. Якщо клієнт надсилав пожежу та забував запит POST, на який не діяли години, 202 буде застосовано.
Кормак Малхолл

Нікого не хвилює, чи існує URL-адреса, але ви не можете отримати дані, які представляє ресурс, оскільки вони все ще обробляються. Можливо, також НЕ створювати URL, поки його не можна використовувати для отримання відео.
Енді

Ресурс створений, він просто знаходиться в стані "в очікуванні". Це самі по собі відповідні дані. В якийсь момент в майбутньому сервер може змінити стан ресурсів на "завершений" (або "невдалий"), але це інша концепція для конкретного завдання домену HTTP "створити ресурс". Очікування може бути абсолютно допустимим станом для ресурсу "Запит", і клієнт, очевидно, хоче знати, що сервер створив ресурс у такому стані, оскільки він переходить від прохання сервера створити ресурс, щоб знати, опитуючи його, щоб дізнатися якщо держава змінилася.
Cormac Mulhall

4

Я вважаю, що пропозиції цього блогу є розумними: REST та тривалі роботи .

Узагальнити:

  1. Сервер повертає код "202 Accepted" із заголовком "Location", встановленим URI, щоб клієнт перевіряв стан, наприклад "/ queue / 12345".
  2. Поки обробка не закінчується, сервер відповідає на запити стану за допомогою "200 ОК" та деяких даних відповідей, що показують стан завдання.
  3. Після завершення обробки сервер відповідає на запити стану за допомогою "303 див. Інші" та "місцеположення", що містять URI, до кінцевого результату.

2

Код статусу HTTP для ресурсу ще не доступний, пропонує повернути відповідь на конфлікт 409, а не відповідь 404, якщо ресурс не існує, оскільки він знаходиться в середині створення.

З специфікації w3 :

10.4.10 409 Конфлікт

Запит не вдалося виконати через конфлікт із поточним станом ресурсу. Цей код дозволений лише в тих випадках, коли очікується, що користувач зможе вирішити конфлікт і подати повторно запит. Орган відповіді ДОЛЖЕН би включити достатньо

інформація для користувача, щоб визнати джерело конфлікту. В ідеалі суб'єкт відповіді повинен містити достатню кількість інформації для користувача або агента користувача, щоб вирішити проблему; однак це може бути неможливим і не потрібно.

Конфлікти, найімовірніше, виникають у відповідь на запит PUT. Наприклад, якщо використовувалася версія, а сутність PUT включала зміни до ресурсу, які суперечать тим, які були зроблені попереднім запитом (сторонньої сторони), сервер може використовувати відповідь 409, щоб вказати, що не може виконати запит. . У цьому випадку суб'єкт відповіді, ймовірно, міститиме список відмінностей між двома версіями у форматі, визначеному відповіддю Content-Type.

Це трохи незручно, оскільки код 409 "дозволений лише у випадках, коли очікується, що користувач зможе вирішити конфлікт та повторно подати запит". Я пропоную тілу відповідей містити повідомлення (можливо, у якомусь форматі відповідей, що відповідає решті вашого API), наприклад: "Цей ресурс наразі створюється. Він був ініційований у [TIME] і, як очікується, заповниться о [TIME]. Будь ласка спробуйте ще раз пізніше."

Зауважте, що я б запропонував підхід 409 лише тоді, коли велика ймовірність, що користувач, який запитує ресурс, також є користувачем, який ініціював генерацію цього ресурсу. Користувачі, не залучені до створення ресурсу, виявлять помилку 404 менш заплутаною.


Начебто розтягнення, для чого насправді призначений 409, що відповідає ухваленню.
Енді

@Andy: Це правда, але так це є і будь-яка інша альтернатива. Напр., 202 насправді мається на увазі відповідь на запит, який ініціював обробку, а не запит, який запитував результати обробки. Дійсно, найбільш відповідна специфікація відповіді - 404, оскільки ресурс не знайдено (тому що він ще не існував). Ніщо не заважає API надавати відповідні дані api в межах 404 відповіді. Зауважте, відповіді 4xx / 5xx, як правило, дратують споживання; деякі мови створюють виняток, а не просто надають інший код статусу.
Брайан

2
Ні, особливо останні кілька абзаців відповіді MainMa. Окремі кінцеві точки, щоб перевірити стан запиту та отримати саме відео. Статус не є таким самим ресурсом, як відео, і він повинен бути адресований самостійно.
Енді
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.