Kode Status HTTP untuk “Still Processing”

47

Saya sedang membangun API tenang yang mendukung antrian tugas yang sudah berjalan lama untuk penanganan akhirnya.

Alur kerja khas untuk API ini adalah:

  1. Pengguna mengisi formulir
  2. Klien memposting data ke API
  3. Pengembalian API 202 Diterima
  4. Klien mengalihkan pengguna ke URL unik untuk permintaan itu ( /results/{request_id})
  5. ~ akhirnya ~
  6. Klien mengunjungi URL lagi, dan melihat hasilnya di halaman itu.

Masalah saya ada pada langkah 6. Setiap kali pengguna mengunjungi halaman, saya mengajukan permintaan ke API saya ( GET /api/results/{request_id}). Idealnya, tugas sudah selesai sekarang, dan saya akan mengembalikan 200 OK dengan hasil tugas mereka.

Tetapi pengguna memaksa, dan saya berharap banyak refresh bersemangat, ketika hasilnya belum selesai diproses.

Apa pilihan terbaik saya untuk kode status untuk menunjukkan bahwa:

  • permintaan ini ada,
  • itu belum selesai,
  • tetapi juga tidak gagal.

Saya tidak mengharapkan satu kode untuk mengkomunikasikan semua itu, tetapi saya menginginkan sesuatu yang memungkinkan saya melewati metadata alih-alih meminta klien mengharapkan konten.

Masuk akal untuk mengembalikan 202, karena itu tidak akan memiliki arti lain di sini: itu adalah GETpermintaan, jadi tidak ada yang mungkin "diterima." Apakah itu pilihan yang masuk akal?

Alternatif yang jelas untuk semua ini - yang berfungsi, tetapi mengalahkan satu tujuan kode status - akan selalu menyertakan metadata:

200 OK

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

...atau...

200 OK

{
    status: "pending"
}

Kemudian sisi klien, saya akan (menghela napas) switchpada response.data.statusuntuk menentukan apakah permintaan tersebut selesai.

Apakah ini yang harus saya lakukan? Atau ada alternatif yang lebih baik? Ini terasa begitu Web 1.0 bagi saya.

Matthew Haugen
sumber
1
Bukankah kode 1xx dibuat persis untuk tujuan itu?
Andy
@Andy saya melihat 102, tapi itu untuk hal-hal WebDAV. Selain itu, tidak ... Mereka sebagian besar untuk komunikasi dalam perjalanan. Berguna dalam beralih ke Soket Web dan semacamnya.
Matthew Haugen
Apa jenis keterlambatan yang kamu bicarakan? 10 detik? Atau 6 jam? Jika penundaan pendek dan umumnya dalam kunjungan browser yang sama, Anda mungkin melakukan polling panjang atau soket web daripada polling berkala.
GrandmasterB
@ GrandmasterB Ini jam, berpotensi. Saya tidak bertanggung jawab atas pemrosesan pekerjaan itu sendiri, jadi saya tidak memiliki perkiraan yang benar-benar bagus, tetapi itu akan memakan waktu. Kalau tidak, saya akan membiarkan POSTpermintaan pertama terbuka. Masalah utama dengan polling panjang atau soket web adalah bahwa pengguna mungkin menutup browser dan kembali. Saya dapat membukanya lagi pada waktu itu (dan itulah yang saya lakukan), tetapi tampaknya lebih bersih untuk memiliki satu API untuk dipanggil sebelum saya membuka soket itu, karena ini merupakan kasus tepi untuk masalah yang muncul.
Matthew Haugen

Jawaban:

48

HTTP 202 Diterima (HTTP / 1.1)

Anda mencari HTTP 202 Acceptedstatus. Lihat RFC 2616 :

Permintaan telah diterima untuk diproses, tetapi pemrosesan belum selesai.

Pemrosesan HTTP 102 (WebDAV)

RFC 2518 menyarankan menggunakan HTTP 102 Processing:

Kode status 102 (Pemrosesan) adalah respons sementara yang digunakan untuk memberi tahu klien bahwa server telah menerima permintaan lengkap, tetapi belum menyelesaikannya.

tetapi memiliki peringatan:

Server HARUS mengirim respons akhir setelah permintaan selesai.

Saya tidak yakin bagaimana menafsirkan kalimat terakhir. Haruskah server menghindari pengiriman apa pun selama pemrosesan, dan hanya merespons setelah selesai? Atau itu hanya memaksa untuk mengakhiri respons hanya ketika pemrosesan berakhir? Ini bisa bermanfaat jika Anda ingin melaporkan kemajuan. Kirim HTTP 102 dan siram respons byte demi byte (atau baris demi baris).

Misalnya, untuk proses yang panjang tapi linier, Anda dapat mengirim seratus titik, membilas setiap karakter. Jika pihak klien (seperti aplikasi JavaScript) tahu bahwa ia harus mengharapkan tepat 100 karakter, itu dapat mencocokkannya dengan bilah kemajuan untuk ditampilkan kepada pengguna.

Contoh lain menyangkut proses yang terdiri dari beberapa langkah non-linear. Setelah setiap langkah, Anda dapat membuka pesan log yang pada akhirnya akan ditampilkan kepada pengguna, sehingga pengguna akhir dapat mengetahui bagaimana prosesnya.

Masalah dengan pembilasan progresif

Perhatikan bahwa meskipun teknik ini memiliki kelebihan, saya tidak akan merekomendasikannya . Salah satu alasannya adalah bahwa hal itu memaksa koneksi untuk tetap terbuka, yang bisa merugikan dalam hal ketersediaan layanan dan tidak skala dengan baik.

Pendekatan yang lebih baik adalah merespons dengan HTTP 202 Accepteddan membiarkan pengguna untuk kembali kepada Anda nanti untuk menentukan apakah pemrosesan berakhir (misalnya dengan memanggil berulang kali URI yang diberikan seperti /process/resultyang akan merespons dengan HTTP 404 Tidak Ditemukan atau Konflik HTTP 409 hingga proses selesai dan hasilnya siap), atau beri tahu pengguna ketika pemrosesan selesai jika Anda dapat memanggil klien kembali misalnya melalui layanan antrian pesan ( misalnya ) atau WebSockets.

Contoh praktis

Bayangkan sebuah layanan web yang mengkonversi video. Titik masuknya adalah:

POST /video/convert

yang mengambil file video dari permintaan HTTP dan melakukan sihir dengannya. Mari kita bayangkan bahwa sihir itu intensif CPU, jadi itu tidak dapat dilakukan secara real-time selama transfer permintaan. Ini berarti bahwa setelah file ditransfer, server akan merespons dengan HTTP 202 Acceptedbeberapa konten JSON, yang berarti “Ya, saya mendapatkan video Anda, dan saya sedang mengerjakannya; itu akan siap di suatu tempat di masa depan dan akan tersedia melalui ID 123. "

Klien memiliki kemungkinan untuk berlangganan antrian pesan untuk diberi tahu ketika pemrosesan selesai. Setelah selesai, klien dapat mengunduh video yang diproses dengan masuk ke:

GET /video/download/123

yang mengarah ke HTTP 200.

Apa yang terjadi jika klien menanyakan URI ini sebelum menerima pemberitahuan? Nah, server akan merespons HTTP 404karena, memang, videonya belum ada. Saat ini mungkin disiapkan. Itu mungkin tidak pernah diminta. Mungkin ada beberapa waktu di masa lalu dan dihapus kemudian. Yang penting adalah bahwa video yang dihasilkan tidak tersedia.

Sekarang, bagaimana jika klien tidak hanya peduli tentang video akhir, tetapi juga tentang kemajuan (yang akan menjadi lebih penting jika tidak ada layanan antrian pesan atau mekanisme serupa)?

Dalam hal ini, Anda dapat menggunakan titik akhir lain:

GET /video/status/123

yang akan menghasilkan respons yang mirip dengan ini:

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

Melakukan permintaan berulang kali akan menunjukkan kemajuan sampai:

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

Sangat penting untuk membuat perbedaan antara ketiga jenis permintaan:

  • POST /video/convertmengantri tugas. Ini harus dipanggil hanya sekali: memanggilnya lagi akan mengantri tugas tambahan.
  • GET /video/download/123menyangkut hasil operasi: sumber daya adalah video. Pemrosesan — itulah yang terjadi di bawah tenda untuk menyiapkan hasil aktual sebelum permintaan dan secara independen terhadap permintaan — tidak relevan di sini. Itu dapat dipanggil sekali atau beberapa kali.
  • GET /video/status/123menyangkut pemrosesan per se . Itu tidak mengantri apa pun. Itu tidak peduli dengan video yang dihasilkan. Sumber daya adalah proses itu sendiri. Itu dapat dipanggil sekali atau beberapa kali.
Arseni Mourzenko
sumber
1
Apakah 202 masuk akal sebagai respons terhadap GET? Itu tentu pilihan yang tepat untuk inisial POST, itulah sebabnya saya menggunakannya. Tetapi tampaknya sangat mencurigakan bagi yang GETmengatakan "diterima" ketika tidak menerima apa pun dari permintaan khusus itu.
Matthew Haugen
2
@MainMa Seperti yang saya katakan, saya POSTnaik pekerjaan yang harus antri, lalu saya GEThasilnya, berpotensi setelah klien menutup sesi. Sebuah 404 adalah sesuatu yang saya sudah dipertimbangkan juga, tetapi tampaknya salah, karena permintaan yang ditemukan, itu hanya belum selesai. Itu akan menunjukkan kepada saya bahwa pekerjaan yang antri tidak ditemukan, yang merupakan situasi yang sangat berbeda.
Matthew Haugen
1
@MatthewHaugen: ketika Anda melakukan GETbagian itu, jangan menganggapnya sebagai permintaan yang tidak lengkap , tetapi sebagai permintaan untuk mendapatkan hasil operasi . Misalnya, jika saya memberi tahu Anda untuk mengonversi video dan Anda perlu waktu lima menit untuk melakukannya, meminta video yang dikonversi dua menit kemudian akan menghasilkan HTTP 404, karena video tersebut belum ada di sana. Meminta kemajuan operasi itu sendiri, di sisi lain, mungkin akan menghasilkan HTTP 200 yang berisi jumlah byte yang dikonversi, kecepatan, dll.
Arseni Mourzenko
5
Kode Status HTTP untuk Sumber Daya belum tersedia menyarankan untuk mengembalikan respons konflik 409 ("Permintaan tidak dapat diselesaikan karena konflik dengan kondisi sumber daya saat ini."), Daripada respons 404, dalam hal sumber daya tidak tidak ada karena sedang dibuat.
Brian
1
@ Brian Komentar Anda akan membuat jawaban yang masuk akal untuk pertanyaan ini. Meskipun saya kemudian akan menjawab dengan "[t] kodenya hanya diperbolehkan dalam situasi di mana diharapkan pengguna mungkin dapat menyelesaikan konflik dan mengirim kembali permintaan," yang tidak sepenuhnya benar dalam kasus saya, tetapi tampaknya kurang salah daripada "tidak ditemukan." Sebagian diriku condong ke arah 409 dengan sundulan Retry-After disematkan. Satu-satunya masalah adalah bahwa rasanya aneh untuk mengembalikan 409 untuk GET, tetapi saya bisa hidup dengan keanehan itu - itu tidak mungkin didefinisikan sebaliknya di masa depan.
Matthew Haugen
5

Alternatif yang jelas untuk semua ini - yang berfungsi, tetapi mengalahkan satu tujuan kode status - akan selalu menyertakan metadata:

Ini cara yang tepat untuk pergi. Status sumber daya dalam kaitannya dengan log khusus domain (alias logika bisnis) adalah masalah untuk tipe konten representasi sumber daya.

Ada dua konsep perbedaan yang disatukan di sini yang sebenarnya berbeda. Salah satunya adalah status transfer negara antara klien dan server sumber daya, dan yang lainnya adalah keadaan sumber daya itu sendiri dalam konteks apa pun yang dilakukan domain bisnis terhadap berbagai status sumber daya itu. Yang terakhir tidak ada hubungannya dengan kode status HTTP.

Ingat kode status HTTP sesuai dengan transfer negara antara klien dan server sumber daya yang ditangani, secara independen ke setiap detail sumber daya itu. Ketika Anda GETsumber daya klien Anda meminta server untuk representasi dari sumber daya dalam keadaan saat ini. Itu bisa menjadi gambar burung, itu bisa menjadi dokumen Word, itu bisa menjadi temp luar saat ini. Protokol HTTP tidak peduli. Kode status HTTP sesuai dengan hasil permintaan itu. Apakah POSTdari klien ke server mentransfer sumber daya ke server, di mana server kemudian memberikan URL yang dapat dilihat klien? Iya? Maka itu adalah 201 Createdrespons.

Sumber daya bisa berupa pemesanan maskapai yang saat ini dalam kondisi 'untuk ditinjau'. Atau bisa juga pesanan pembelian produk yang dalam kondisi 'disetujui'. Status tersebut adalah spesifik domain dan bukan tentang protokol HTTP. Protokol HTTP berkaitan dengan transfer sumber daya antara klien dan server.

Maksud dari REST dan HTTP adalah bahwa protokol tidak memperhatikan dirinya sendiri dengan rincian sumber daya. Ini sengaja, tidak berkaitan dengan masalah khusus domain sehingga dapat digunakan tanpa harus tahu apa-apa tentang masalah khusus domain. Anda tidak menafsirkan ulang arti kode status HTTP dalam setiap konteks yang berbeda (sistem pemesanan maskapai, sistem pemrosesan bayangkan, sistem keamanan video, dll.).

Hal-hal khusus domain adalah untuk klien dan server untuk mencari tahu di antara mereka sendiri berdasarkan pada Content Typesumber daya. Protokol HTTP agnostik untuk ini.

Adapun cara klien mengetahui bahwa sumber daya Permintaan telah berubah status, pemungutan suara adalah taruhan terbaik Anda karena tetap mengendalikan di klien dan tidak menganggap koneksi tidak terputus. Terutama jika itu akan berpotensi berjam-jam sampai negara berubah. Bahkan jika Anda mengatakan persetan dengan REST Anda hanya akan menjaga koneksi tetap terbuka, menjaganya tetap terbuka selama berjam-jam dan dengan asumsi tidak ada yang salah akan menjadi ide yang buruk. Bagaimana jika pengguna menutup klien atau jaringan padam. Jika rinciannya adalah jam, klien hanya dapat meminta status setiap beberapa menit sampai Permintaan berubah dari 'menunggu' menjadi 'selesai'.

Harapan itu membantu memperjelas hal-hal

Cormac Mulhall
sumber
"Apakah POST dari klien ke server mentransfer sumber daya ke server, di mana server kemudian memberikannya URL yang dapat dilihat klien? Ya? Maka itu adalah respons 201 Created." 202 Diterima juga dapat diterima sebagai tanggapan terhadap hal ini jika server tidak dapat segera bertindak untuk memproses sumber daya, yang dilakukan OP.
Andy
1
Masalahnya server bertindak segera. Itu menciptakan sumber daya dengan URL segera. Hanya saja keadaan sumber daya "Tertunda" (atau sesuatu). Itu adalah status domain bisnis. Sejauh menyangkut Protokol HTTP, server bertindak segera setelah menciptakan sumber daya dan memberi klien URL sumber daya tersebut. Anda dapat DAPATKAN sumber daya itu. Permintaan POST itu sendiri tidak tertunda. Inilah yang saya maksud dengan memisahkan dua domain konseptual yang berbeda. Jika klien mengirimkan api dan lupa permintaan POST tidak ditindaklanjuti selama berjam-jam maka 202 akan berlaku.
Cormac Mulhall
Tidak ada yang peduli jika url ada tetapi Anda tidak bisa mendapatkan data yang diwakili sumber daya karena masih diproses. Mungkin juga TIDAK membuat url sampai dapat digunakan untuk mendapatkan video.
Andy
Sumber daya dibuat, hanya saja dalam kondisi "tertunda". Itu sendiri adalah data yang relevan. Pada titik tertentu di masa depan server dapat mengubah status sumber daya menjadi "selesai" (atau "gagal") tetapi itu adalah konsep yang berbeda dengan tugas khusus domain HTTP "buat sumber daya". Tertunda dapat menjadi keadaan yang benar-benar valid untuk sumber daya "Permintaan", dan klien jelas ingin tahu server telah menciptakan sumber daya dalam keadaan itu karena ia beralih dari meminta server untuk membuat sumber daya untuk mengetahui polling untuk mengetahuinya jika keadaan berubah.
Cormac Mulhall
4

Saya menemukan saran dari blog ini masuk akal: REST dan pekerjaan jangka panjang .

Untuk meringkas:

  1. Server mengembalikan kode "202 Diterima" dengan tajuk "Lokasi" diatur ke URI untuk klien untuk memeriksa status, mis. "/ Antrian / 12345".
  2. Hingga pemrosesan selesai, server merespons permintaan status dengan "200 OK" dan beberapa data respons menampilkan status pekerjaan.
  3. Setelah pemrosesan selesai, server merespons permintaan status dengan "303 See Other" dan "Location" yang berisi URI ke hasil akhir.
Xiangming Hu
sumber
2

Kode Status HTTP untuk Sumber Daya belum tersedia menyarankan untuk mengembalikan respons konflik 409, daripada respons 404, dalam hal sumber daya tidak ada karena sedang dibuat.

Dari spesifikasi w3 :

10.4.10 409 Konflik

Permintaan tidak dapat diselesaikan karena konflik dengan kondisi sumber daya saat ini. Kode ini hanya diperbolehkan dalam situasi di mana diharapkan pengguna dapat menyelesaikan konflik dan mengirimkan kembali permintaan. Badan respons HARUS menyertakan cukup

informasi bagi pengguna untuk mengenali sumber konflik. Idealnya, entitas respons akan mencakup informasi yang cukup bagi pengguna atau agen pengguna untuk memperbaiki masalah; Namun, itu mungkin tidak mungkin dan tidak diperlukan.

Konflik kemungkinan besar terjadi sebagai respons terhadap permintaan PUT. Misalnya, jika versi sedang digunakan dan entitas yang PUT menyertakan perubahan ke sumber daya yang bertentangan dengan yang dibuat oleh permintaan sebelumnya (pihak ketiga), server mungkin menggunakan respons 409 untuk menunjukkan bahwa ia tidak dapat menyelesaikan permintaan . Dalam hal ini, entitas respons kemungkinan akan berisi daftar perbedaan antara dua versi dalam format yang ditentukan oleh Tipe Konten respons.

Ini agak canggung, karena kode 409 "hanya diperbolehkan dalam situasi di mana diharapkan pengguna dapat menyelesaikan konflik dan mengirim kembali permintaan." Saya sarankan badan respons menyertakan pesan (mungkin dalam beberapa format respons yang cocok dengan API Anda yang lain) seperti, "Sumber daya ini sedang dibuat. Ini dimulai pada [WAKTU] dan diperkirakan selesai pada [WAKTU]. Silakan coba lagi nanti."

Perhatikan bahwa saya hanya akan menyarankan pendekatan 409 jika sangat mungkin bahwa pengguna yang meminta sumber daya juga adalah pengguna yang memprakarsai generasi sumber daya itu. Pengguna yang tidak terlibat dengan pembuatan sumber daya akan menemukan kesalahan 404 kurang membingungkan.

Brian
sumber
Sepertinya peregangan untuk apa 409 sebenarnya dimaksudkan, yang merupakan tanggapan terhadap put.
Andy
@Andy: Benar, tapi begitu juga setiap alternatif lain. Misalnya, 202 benar-benar dimaksudkan sebagai respons terhadap permintaan yang memulai pemrosesan, bukan permintaan yang meminta hasil pemrosesan. Sungguh, respons yang paling sesuai dengan spesifikasi adalah 404, karena sumber daya itu tidak ditemukan (karena belum ada). Tidak ada yang menghentikan API dari menyediakan data api yang relevan dalam respons 404. Ingat, respons 4xx / 5xx cenderung mengganggu untuk dikonsumsi; beberapa bahasa akan memunculkan pengecualian daripada hanya memberikan kode status yang berbeda.
Brian
2
Tidak, terutama beberapa paragraf terakhir dari jawaban MainMa. Pisahkan titik akhir untuk memeriksa status permintaan dan untuk mendapatkan video itu sendiri. Statusnya bukan sumber yang sama dengan video dan harus dialamatkan sendiri.
Andy