“İşlem Yapıyor” için HTTP Durum Kodu


47

Son işlem için uzun süredir görevlerin sıraya alınmasını destekleyen bir RESTful API oluşturuyorum.

Bu API için tipik iş akışı şöyle olacaktır:

  1. Kullanıcı formu doldurur
  2. Müşteri API’ye veri gönderiyor
  3. API döndürür 202 Kabul edildi
  4. Müşteri, kullanıcıyı bu istek için benzersiz bir URL’ye yönlendirir ( /results/{request_id})
  5. ~ Sonunda ~
  6. Müşteri tekrar URL’yi ziyaret eder ve sonuçları o sayfadan görür.

Benim sorunum 6. adımda. Bir kullanıcı sayfayı her ziyaret ettiğinde, API’ma ( GET /api/results/{request_id}) isteğinde bulunuyorum . İdeal olarak, görev şimdiye kadar tamamlanacak ve görevlerinin sonuçlarına göre 200 OK döndüreceğim.

Ancak kullanıcılar saldırgandır ve sonuç henüz işleme koyulmadığında çok fazla aşırı yenilemeyi bekliyorum.

Bir durum kodu için şunu belirtmek için en iyi seçeneğim nedir:

  • bu istek var,
  • henüz bitmedi
  • ama aynı zamanda başarısız olmadı.

Tek bir kodun hepsini iletmesini beklemiyorum, ancak müşterinin içerik beklemesini sağlamak yerine meta verileri geçirmemi sağlayan bir şey istiyorum.

Bir 202 döndürmek mantıklı olabilir, çünkü burada başka bir anlamı olmayacaktı: bu bir GETistek, bu nedenle hiçbir şey “kabul edilmiyor”. Bu makul bir seçim olabilir mi?

Tüm bunlara bariz bir alternatif - hangi işlevler, ancak durum kodlarının bir amacını yendi - her zaman meta verileri eklemek olacaktır:

200 OK

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

...veya...

200 OK

{
    status: "pending"
}

Sonra istemci tarafı, ben (nefes) olur switchüzerinde response.data.statusisteğinin tamamlandığı olmadığını belirlemek için.

Yapmam gereken şey bu mu? Yoksa daha iyi bir alternatif mi var? Bu bana sadece Web 1.0 geliyor.


1
1xx kodları tam da bu amaç için yapılmış değil mi?
Andy

@Andy 102'ye bakıyordum, ama bu WebDAV işleri için. Bunun ötesinde, hayır ... Onlar çoğunlukla transit iletişim için. Web Prizlerine vb. Geçiş yapmak için kullanışlıdır.
Matthew Haugen

Ne tür bir gecikmeden bahsediyorsun? 10 saniye? Ya da 6 saat? Gecikmeler kısasa ve genellikle aynı tarayıcı ziyareti dahilindeyse, düzenli sorgulama yerine uzun sorgulama veya web soketleri yapabilirsiniz.
GrandmasterB

@GrandmasterB Potansiyel olarak saatlerdir. İşin işlenmesinden kendim sorumlu değilim, bu yüzden gerçekten iyi bir tahminde bulunmuyorum, ancak bir süre olacak. Aksi takdirde, ilk POSTisteği açık bırakırdım. Uzun sorgulama veya web soketleri ile ilgili ana sorun, kullanıcının tarayıcıyı kapatıp geri gelmesidir. Onları tekrar açabilirim (ve yaptığım şey bu), ancak bu soketleri açmadan önce arayacak tek bir API'nin olması daha net görünüyor, çünkü bu sorunun ortaya çıkması son derece önemlidir.
Matthew Haugen

Yanıtlar:


48

HTTP 202 Kabul Edildi (HTTP / 1.1)

HTTP 202 AcceptedDurum arıyorsun . RFC 2616'ya bakınız :

İstek işleme alındı, ancak işleme tamamlanmadı.

HTTP 102 İşleme (WebDAV)

RFC 2518 kullanmanızı önerir HTTP 102 Processing:

102 (İşleme) durum kodu, müşteriye sunucunun tüm isteği kabul ettiğini, ancak henüz tamamlamadığını bildirmek için kullanılan geçici bir cevaptır.

ama bir ihtar var:

Sunucu, istek tamamlandıktan sonra son bir yanıt göndermelidir.

Son cümleyi nasıl yorumlayacağımdan emin değilim. Sunucu, işlem sırasında herhangi bir şey göndermekten kaçınmalı ve yalnızca tamamlandıktan sonra yanıt vermeli mi? Ya da sadece zorlar sona yalnızca işlem sonlandırıldığında yanıtı? İlerlemeyi bildirmek istiyorsanız bu yararlı olabilir. HTTP 102 gönderin ve yanıtı baytla (veya satır satır) temizleyin.

Örneğin, uzun ama doğrusal bir işlem için, her karakterden sonra sifon çekerek yüz nokta gönderebilirsiniz. İstemci tarafı (bir JavaScript uygulaması gibi) tam olarak 100 karakter beklemesi gerektiğini biliyorsa, kullanıcıya göstermek için bir ilerleme çubuğu ile eşleşebilir.

Başka bir örnek, doğrusal olmayan birkaç basamaktan oluşan bir işlemle ilgilidir. Her adımdan sonra, sonunda kullanıcıya gösterilecek olan bir günlük mesajını aktarabilirsiniz, böylece son kullanıcı işlemin nasıl gittiğini bilebilir.

Aşamalı yıkama ile ilgili sorunlar

Bu tekniğin yararları varken, bunu tavsiye etmemeyi unutmayın . Bunun sebeplerinden biri, bağlantının açık kalmaya zorlamasıdır; bu hizmet kullanılabilirliği açısından zarar verebilir ve iyi ölçeklenemez.

Daha iyi bir yaklaşım, HTTP 202 Acceptedişlemin bitip bitmediğini (örneğin , sürece kadar /process/resultHTTP 404 Bulunamadı veya HTTP 409 Uyuşmazlığı ile yanıt verecek olan belirli bir URI'yi çağırarak) kullanarak yanıt vermek ve kullanıcının size daha sonra geri dönmesini sağlamaktır. bittiğinde ve sonuç hazırdır) veya müşteriyi örneğin bir mesaj kuyruğu servisi ( örneğin ) veya WebSockets üzerinden geri arayabiliyorsanız, işlem tamamlandığında kullanıcıyı bilgilendirin .

Pratik örnek

Videoları dönüştüren bir web hizmeti hayal edin. Giriş noktası:

POST /video/convert

HTTP isteğinden bir video dosyası alır ve onunla birlikte biraz sihir yapar. Büyünün CPU-yoğun olduğunu hayal edelim, bu nedenle isteğin aktarılması sırasında gerçek zamanlı olarak yapılamaz. Bu, bir kez dosya transfer edildiğinde, sunucunun bir HTTP 202 AcceptedJSON içeriği ile cevap vereceği anlamına gelir, “Evet, videonuzu aldım ve üzerinde çalışıyorum; Gelecekte bir yerde hazır olacak ve ID 123 aracılığıyla erişilebilir olacak. ”

Müşterinin işlem bittiğinde bildirilmesi gereken bir mesaj kuyruğuna abone olma olasılığı vardır. İşlem tamamlandığında, müşteri işlenen videoyu aşağıdakilere giderek indirebilir:

GET /video/download/123

hangi bir yol açar HTTP 200.

Müşteri bildirimi almadan önce bu URI'yi sorgularsa ne olur? Eh, sunucu ile cevap verecektir HTTP 404, aslında, video henüz yok. Şu anda hazırlanabilir. Asla talep edilmeyebilir. Geçmişte bir süre olabilir ve daha sonra kaldırılabilir. Tek önemli olan elde edilen videonun mevcut olmaması.

Şimdi, müşteri yalnızca son videoya değil, ilerlemeye de önem veriyorsa (mesaj sırası hizmeti ya da benzeri bir mekanizma yoksa bu daha da önemli olurdu)?

Bu durumda, başka bir bitiş noktası kullanabilirsiniz:

GET /video/status/123

buna benzer bir cevap ortaya çıkacaktır:

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

İsteği tekrar tekrar yapmak, şu tarihe kadar olan ilerlemeyi gösterecektir:

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

Bu üç tür istek arasında bir fark yaratmak çok önemlidir:

  • POST /video/convertbir görevi sıraya koyar. Sadece bir kez çağrılmalıdır: tekrar çağırmak ek bir görevi sıraya koyar.
  • GET /video/download/123operasyonun sonucuyla ilgilidir : kaynak videodur. İşlem - fiili sonucu talepten önce ve talebe bağımsız olarak hazırlamak için davlumbazın altında gerçekleşen olay bu değil. Bir veya birkaç kez çağrılabilir.
  • GET /video/status/123başlı başına işleme ile ilgilidir . Hiçbir şeyi sıraya sokmaz. Elde edilen videoyu umursamıyor. Kaynak işleme kendisidir. Bir veya birkaç kez çağrılabilir.

1
Bir 202 GET, buna karşılık bir anlam ifade ediyor mu? Bu kesinlikle başlangıç ​​için doğru seçim POST, bu yüzden kullanıyorum. Ancak GET, bu belirli bir talepten hiçbir şey kabul etmediğinde “kabul edilmiş” demesi anlamsal olarak şüpheli görünüyor .
Matthew Haugen

2
@MainMa Dediğim gibi, POSTsıraya alınacak işi başlattım , ardından GETmüşteri oturumu kapattıktan sonra sonuçları alıyorum . 404 Ben de kabul ettiğim bir şey, ama istek beri, yanlış geliyor edilir bulundu, sadece tamamlanmadı. Bu bana sıradaki işin bulunmadığını ve bunun da çok farklı bir durum olduğunu gösteriyor.
Matthew Haugen

1
@MatthewHaugen: Parçayı yaptığınızda, eksik bir istek olarak değil , işlemin sonucunu almak için bir istek GETolarak düşünün . Bir video dönüştürmek için size ve ne için talep bunu yapmak için beş dakika sürer Örneğin, dönüştürülmüş bir videoda iki dakika sonra gereken video henüz sadece yok çünkü HTTP 404 sonuçlanabilir. Operasyonun ilerlemesini istemek, diğer yandan, muhtemelen dönüştürülen bayt sayısını, hızı vb. İçeren bir HTTP 200 ile sonuçlanacaktır.
Arseni Mourzenko

5
Kaynak için henüz mevcut olmayan HTTP Durum Kodu , bir kaynağın yapmaması durumunda bir 404 yanıtı yerine 409 çakışma yanıtı ("istek, kaynağın geçerli durumuyla çakışması nedeniyle tamamlanamadı.") Gönderilmesini önerir. yok çünkü üretilmenin tam ortasında.
Brian

1
@Brian Yorumunuz bu soruya makul bir cevap verecektir. Daha sonra "[t] koduna yalnızca kullanıcının çakışmayı çözmesi ve isteği yeniden göndermesi beklenebileceği durumlarda izin verilsin" yanıtını vermeme rağmen, benim durumumda kesinlikle doğru değil, ancak bu görünüyor "bulunamadı" dan daha az yanlış. Bir parçam 409'a yaslanmış, bir Retry-After başlığı sabitlenmiş. Tek sorun, bir GET için bir 409'u iade etmenin garip göründüğü, ancak bu gariplikle yaşayabileceğim - ileride başka türlü tanımlanma olasılığı düşük.
Matthew Haugen

5

Tüm bunlara bariz bir alternatif - hangi işlevler, ancak durum kodlarının bir amacını yendi - her zaman meta verileri eklemek olacaktır:

Bu gitmek için doğru yoldur. Bir kaynağın, alana özgü log (yani iş mantığı) ile ilgili olduğu durum, kaynağın temsili için bir tür meseledir.

Burada birbirinden farklı olan iki farklı kavram var. Birincisi, bir kaynağın müşterisi ile sunucusu arasındaki durum aktarımının durumu, diğeri ise iş alanının o bağlamda olduğu bağlamda hangi bağlamda yer aldığına bağlı olarak kaynağın kendisidir. İkincisi, HTTP durum kodlarıyla ilgisi yoktur.

HTTP durum kodlarının, söz konusu kaynağın tüm ayrıntılarından bağımsız olarak, ele alınan kaynağın istemcisi ile sunucusu arasındaki durum aktarımına karşılık geldiğini unutmayın. Bir GETkaynağınız, müşterinizden sunucuya içinde bulunduğu mevcut durumda bir kaynağın temsili olmasını ister. Bu bir kuşun resmi olabilir, bir Word belgesi olabilir, mevcut dış sıcaklık olabilir. HTTP protokolü umursamıyor. HTTP durum kodu, bu isteğin sonucuna karşılık gelir. Did POSTSunucu, daha sonra müşteri görüntüleyebilmek bunu bir URL verdi nereye istemciden sunucuya sunucusuna bir kaynak aktarmak? Evet? O zaman bu bir 201 Createdcevaptır.

Kaynak, şu anda 'incelenecek' durumda olan bir havayolu rezervasyonu olabilir. Veya “onaylanmış” durumda olan ürün satınalma siparişi olabilir. Bu durumlar etki alanına özgüdür ve HTTP protokolünün konusu değildir. HTTP protokolü, istemci ve sunucu arasındaki kaynakların aktarılması ile ilgilidir.

REST ve HTTP'nin amacı, protokollerin, kaynakların ayrıntılarıyla ilgilenmediğidir. Bu bilerek, alana özgü konularla ilgilenmiyor, böylece alana özgü konular hakkında hiçbir şey bilmek zorunda kalmadan kullanılabiliyor. HTTP durum kodlarının her bir bağlamda ne anlama geldiğini yeniden yorumlamıyorsunuz (havayolu rezervasyon sistemi, hayal işleme sistemi, video güvenlik sistemi vb.).

Etki alanına özgü şey, istemcinin ve sunucunun Content Typekaynağa göre kendi aralarında bir çözüm bulması içindir. HTTP protokolü buna agnostiktir.

Müşterinin İstek kaynağının durumunu değiştirdiğini nasıl belirlediğine gelince, sorgulama, müşteride kontrolü elinde tuttuğu ve kırılmamış bağlantıyı almadığı için en iyi seçimdir. Özellikle, devlet değişene kadar potansiyel olarak saatler olacaksa. REST ile cehenneme gitmiş olsanız bile, bağlantıyı açık tutacaksınız, saatlerce açık tutmak ve hiçbir şeyin yanlış gitmeyeceğini varsaymak kötü bir fikir olacaktır. Kullanıcı müşteriyi kapatırsa veya ağ kapanırsa. Taneciklilik saat ise, müşteri isteğini 'beklemeden' den 'bitinceye kadar' birkaç dakikada bir devletten talep edebilir.

İşlerin netleşmesine yardımcı olan umut


"POST, istemciden sunucuya bir kaynak sunucuya aktardı mı, sunucunun daha sonra istemcinin görüntüleyebileceği bir URL verdi mi? Evet mi? 202 Kabul edilirse, sunucu kaynak işlemesi için derhal hareket edemiyorsa, OP'nin yaptığı şey de buna cevap olarak kabul edilebilir.
Andy

1
Sorun şu ki, sunucu hemen hareket ediyor. Hemen bir URL ile kaynak oluşturur. Bu sadece kaynağın durumu "Beklemede" (veya başka bir şey). Bu bir işletme etki alanı devletidir. HTTP Protokolü ile ilgili olarak, sunucu kaynağı yarattığı anda harekete geçti ve müşteriye kaynağın URL'sini verdi. Bu kaynağı alabilirsin. POST isteğinin kendisi beklemede değil. İki farklı kavramsal alanı ayrı tutarak demek istediğim bu. Müşteri bir yangın gönderiyorsa ve saatlerce uygulanmayan POST isteğini unutursa, 202 geçerli olacaktır.
Cormac Mulhall

URL'nin var olup olmadığını kimse umursamıyor ancak kaynağın temsil ettiği verileri alamıyorsunuz çünkü hala işleniyor. Videoyu almak için kullanılabilecek kadar URL oluşturmayabilir.
Andy,

Kaynak yaratıldı, sadece "beklemede" durumunda. Bu kendi içinde ilgili veridir. Gelecekte bir noktada sunucu, kaynak durumunu "tamamlandı" (veya "başarısız") olarak değiştirebilir, ancak bu, "kaynak yarat" ın HTTP etki alanına özgü görevi için farklı bir kavramdır. Beklemede olmak, bir "İstek" kaynağının olması için tamamen geçerli bir durum olabilir ve müşteri açık bir şekilde, sunucunun kaynağı bulmak istediğini bilmesini ister. eğer durum değişmişse.
Cormac Mulhall

4

Bu blogdaki önerileri makul buldum: REST ve uzun süredir devam eden işler .

Özetlemek:

  1. Sunucu, "202 Kabul Edildi" kodunu "Konum" başlığı, müşterinin durumu kontrol etmesi için bir URI'ye ayarlı olarak döndürür, örneğin "/ queue / 12345".
  2. İşlem tamamlanana kadar, sunucu "200 OK" durum sorgularına ve iş durumunu gösteren bazı cevap verisine cevap verir.
  3. İşlem bittikten sonra, sunucu nihai sonucu URI içeren "303 See Other" ve "Konum" ile durum sorgularına yanıt verir.

2

Kaynak için henüz mevcut olmayan HTTP Durum Kodu, bir kaynak bulunmadığı için mevcut olmadığından 404 yanıt yerine 409 çakışma yanıtı döndürdüğünü gösterir.

Gönderen w3 spec :

10.4.10 409 Uyuşmazlık

İstek, kaynağın mevcut durumu ile bir çakışma nedeniyle tamamlanamadı. Bu koda yalnızca kullanıcının anlaşmazlığı çözmesi ve isteği yeniden göndermesi beklenen durumlarda izin verilir. Tepki gövdesi yeterince içermelidir

Kullanıcının çatışma kaynağını tanıması için bilgi. İdeal olarak, yanıt varlığı, sorunu çözmek için kullanıcı veya kullanıcı aracısı için yeterli bilgiyi içerecektir; Ancak, bu mümkün olmayabilir ve gerekli değildir.

PUT isteğine yanıt olarak çakışmaların ortaya çıkması daha olasıdır. Örneğin, sürüm kullanılıyorsa ve PUT olan varlık daha önceki (üçüncü taraf) bir istekte bulunanlarla çakışan bir kaynaktan yapılan değişiklikleri içeriyorsa, sunucu isteği tamamlayamayacağını belirtmek için 409 yanıtını kullanabilir . Bu durumda, cevap varlığı muhtemelen cevap İçerik Tipi tarafından tanımlanan formattaki iki versiyon arasındaki farkların bir listesini içerecektir.

Bu biraz gariptir, çünkü 409 koduna "yalnızca kullanıcının anlaşmazlığı çözmesi ve isteği yeniden göndermesi beklenen durumlarda izin verilir". Yanıt organının, "Bu kaynak şu anda üretiliyor. [TIME] 'da başlatıldığı ve [TIME]' da tamamlandığı tahmin ediliyor. daha sonra tekrar deneyin."

Yalnızca 409 yaklaşımı önereceğimi ve kaynak talep eden kullanıcının aynı zamanda bu kaynağın oluşturulmasını başlatan kullanıcı olması ihtimalinin yüksek olduğunu düşündüğümü unutmayın. Kaynağın üretilmesiyle ilgili olmayan kullanıcılar, 404 hatalarını daha az kafa karıştırıcı bulacaklardır.


409'un gerçekte ne anlama geldiği bir gerginlik gibi görünüyor, ki bu bir cevaba cevap olarak.
Andy

@Andy: Doğru, ama diğer her alternatif. Örneğin, 202 işlemin sonuçlarını talep eden talebe değil, işlemi başlatan talebe bir cevap anlamına gelir . Gerçekte, en spesifik uyumlu cevap, kaynak bulunamadığından 404'tür (çünkü henüz mevcut değildi). API'nın 404 yanıtı içinde ilgili api verilerini sağlamasını engelleyen hiçbir şey yoktur. Unutmayın, 4xx / 5xx cevapları tüketmek can sıkıcıdır; bazı diller, yalnızca farklı bir durum kodu sağlamak yerine istisna atar.
Brian

2
Hayır, özellikle MainMa'nın cevabının son birkaç paragrafı. İsteğin durumunu kontrol etmek ve videonun kendisini almak için ayrı bitiş noktaları. Durum, video ile aynı kaynak değildir ve kendi başına adreslenebilir olmalıdır.
Andy,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.