Birden fazla eylem farklı durumlarla biterse hangi HTTP durum kodunu döndürürsünüz?


72

Kullanıcının, sunucudan bir HTTP isteğinde birden fazla eylem gerçekleştirmesini isteyebileceği bir API yapıyorum. Sonuç, işlem başına bir girişle birlikte bir JSON dizisi olarak döndürülür.

Bu eylemlerin her biri birbirinden bağımsız olarak başarısız veya başarılı olabilir. Örneğin, ilk eylem başarılı olabilir, ikinci eylemin girişi yetersiz biçimlendirilmiş ve doğrulanamayabilir ve üçüncü eylem beklenmeyen bir hataya neden olabilir.

İşlem başına bir talep olsaydı, sırasıyla 200, 422 ve 500 durum kodlarını döndürürdüm. Ancak şimdi sadece bir istek olduğunda hangi durum kodunu geri vermeliyim?

Bazı seçenekler:

  • Her zaman 200 döndür ve vücutta daha ayrıntılı bilgi ver.
  • Belki yukarıdaki kuralı ancak istekte birden fazla işlem olduğunda izleyebilirsin?
  • Tüm istekler başarılı olursa, belki 500 (ya da başka bir kod) başarılı olursa 200 döndürürsünüz?
  • İşlem başına yalnızca bir istek kullanın ve ek yükü kabul edin.
  • Tamamen farklı bir şey mi?

3
Sorunuz başka bir tane hakkında düşünmemi sağladı: programmers.stackexchange.com/questions/309147/…
AilurusFulgens

7
Biraz da ilgili: programmers.stackexchange.com/questions/305250/… (HTTP durum kodları ve uygulama kodları arasındaki ayrımla ilgili kabul edilen cevaba bakın)

4
Bu talepleri birlikte gruplayarak elde ettiğiniz avantaj nedir? İş mantığı hakkında mı, birden fazla kaynak üzerindeki bir işlem gibi mi, yoksa performans mı? Veya başka bir şey?
Luc Franken

5
Tamam, bu durumda, bu performansı diğer alanlarda da artırmanızı şiddetle tavsiye ederim. Bu karmaşıklığı iş katmanınıza uygulamadan önce iyimser kullanıcı arayüzü, gruplama isteme, önbellekleme vb. En çok nerede kaybettiğin konusunda net bir fikrin var mı?
Luc Franken

4
... insanların bu durumlara doğru bakması için fazla umutlu olmayın. Çoğu program yalnızca en yaygın olanları denetler ve beklenmeyen bir durum kodu alırlarsa başarısız ya da hatalı davranırlar. (DefCon'da ayrıca, tarayıcının görmezden geldiği ve tarayıcıların bazen neden hata aldığını ve böylece web sitenizin bu bölümünü taramayı bıraktığını rasgele çıkış durumları göndererek sitenizi tarayıcılardan korumakla ilgili bir sunum yaptığını hatırlıyorum).
Bakuriu

Yanıtlar:


21

Kısa, doğrudan cevap

Talep, görev listesini yürütmekten bahsettiğinden (görevler burada bahsettiğimiz kaynaktır), o zaman görev grubu yürütmeye ileri götürülmüşse (yani, yürütme sonucundan bağımsız olarak), o zaman mantıklı olur. Cevap statüsü olacak 200 OK. Aksi takdirde, görev grubunun yürütülmesini engelleyebilecek bir sorun varsa, örneğin görev nesnelerinin doğrulanamaması veya bazı gerekli servisler mevcut değilse, yanıt durumu bu hatayı belirtmelidir. Geçmişte, görevlerin yerine getirilmesi başladığında, yapılacak görevlerin istek gövdesinde listelendiğini görmek, yürütme sonuçlarının cevap gövdesinde listelenmesini beklerdim.


Uzun, felsefi cevap

Bu ikilemi deneyimliyorsunuz çünkü HTTP'nin ne için tasarlandığından sapıyorsunuz. Kaynakları yönetmek için etkileşime girmiyorsunuz, bunun yerine, uzaktan yöntem çağırma aracı olarak kullanıyorsunuz (ki bu çok garip değil, ancak önceden tasarlanmış bir program olmadan zayıf çalışıyor).

Yukarıdakilerin söylenmesi ve bu cevabı uzun bir görüşlü rehber haline getirme cesareti olmadan, aşağıdakiler kaynak yönetimi yaklaşımına uygun bir URI şemasıdır:

  • /tasks
    • GET sayfalandırılmış tüm görevleri listeler
    • POST tek bir görev ekler
  • /tasks/task/[id]
    • GET tek bir görevin durum nesnesiyle yanıt verir
    • DELETE görevi iptal eder / siler
  • /tasks/groups
    • GET sayfalandırılmış tüm görev gruplarını listeler
    • POST bir grup görev ekler
  • /tasks/groups/group/[id]
    • GET görev grubunun durumuna cevap verir
    • DELETE görev grubunu iptal eder / siler

Bu yapı, onlarla ne yapılacağı hakkında değil, kaynaklar hakkında konuşuyor. Kaynaklarla yapılan başka bir hizmetin endişesidir.

Dikkat edilmesi gereken bir diğer önemli nokta ise, bir HTTP istek işleyicisinde çok uzun süre engellenmemesi tavsiye edilir. UI'ye benzer şekilde, bir HTTP arayüzünün yanıt vermesi gerekir - bir kaç büyüklük derecesinde daha yavaş olan bir zaman çizelgesinde (çünkü bu katman IO ile ilgilidir).

Kaynakları sıkı bir şekilde yöneten bir HTTP arayüzü tasarlama yolunda ilerlemek, bir düğme tıklatıldığında çalışmanın bir UI iş parçasından uzağa taşınması kadar muhtemeldir. HTTP sunucusunun, istek işleyicide yerine yürütmek yerine görevleri yürütmek için diğer hizmetlerle iletişim kurmasını gerektirir. Bu sığ bir uygulama değil, yön değişikliği.


Böyle bir URI planının nasıl kullanılacağına dair bazı örnekler

Tek bir görevi yerine getirmek ve ilerlemeyi izlemek:

  • POST /tasks yürütülmesi gereken görevle
    • GET /tasks/task/[id]completedMevcut durum / ilerlemeyi gösterirken, cevap nesnesi pozitif değere kadar

Tek bir görevi yerine getirmek ve tamamlanmasını beklemek:

  • POST /tasks yürütülmesi gereken görevle
    • GET /tasks/task/[id]?awaitCompletion=truecompletedpozitif olana kadar (muhtemelen zaman aşımına uğradı, bu yüzden bunun döngüsel olması gerekiyor)

Bir görev grubunun yürütülmesi ve ilerlemenin izlenmesi:

  • POST /tasks/groups Yürütülecek görevler grubu ile
    • GET /tasks/groups/group/[groupId]Cevap nesnesi completedözelliği değere sahip olana kadar bireysel görev durumunu gösterir (örneğin 5 üzerinden tamamlanan 3 görev)

Bir görev grubu için yürütme istemek ve tamamlanmasını beklemek:

  • POST /tasks/groups Yürütülecek görevler grubu ile
    • GET /tasks/groups/group/[groupId]?awaitCompletion=true Tamamlanmayı belirten sonuçla cevap verene kadar (büyük olasılıkla zaman aşımına uğramıştır, bu yüzden ilmek gerekir)

Anlamsal olarak neyin mantıklı olduğu hakkında konuşmanın buna yaklaşmanın doğru olduğunu düşünüyorum. Teşekkürler!
Anders,

2
Daha önce orada olmasaydı, bu cevabı önerecektim. Öyle imkansız tek HTTP isteğinde birden çok istekte bulunmaya. Öte yandan, "aşağıdaki işlemleri yap ve sonuçların ne olduğunu bana bildir" diyen tek bir HTTP isteği yapmak mükemmel bir şekilde mümkün . Ve burada olan da bu.
Martin Kochanski,

Bu cevabı en çok oy alandan uzak olsa bile kabul edeceğim. Diğer cevaplar da iyi olsa da, bunun HTTP anlambilimine neden olan tek şey olduğunu düşünüyorum.
Anders,

87

Benim oyum, bu görevleri ayrı taleplere bölmek olacaktır. Ancak, çok fazla gidiş-dönüş turu kaygı verici ise, 207 numaralı HTTP yanıt koduna rastladım - Çok Durumlu

Bu bağlantıdan kopyala / yapıştır:

Bir Çoklu Durum yanıtı, çoklu durum kodlarının uygun olabileceği durumlarda çoklu kaynaklar hakkında bilgi iletir. Varsayılan Çok Durumlu yanıt gövdesi, 'multistatus' kök öğeli bir metin / xml veya uygulama / xml HTTP öğesidir. Diğer öğeler, yöntem çağrısı sırasında oluşturulan 200, 300, 400 ve 500 serisi durum kodlarını içerir. 100 serisi durum kodu 'yanıt' XML öğesine kaydedilmemelidir.

'207' genel cevap durumu kodu olarak kullanılmasına rağmen, alıcının yöntem uygulamasının başarısı veya başarısızlığı hakkında daha fazla bilgi için multistatus yanıt gövdesinin içeriğine bakması gerekir. Yanıt, başarı, kısmi başarı ve başarısızlık durumlarında kullanılabilir.


22
207OP'nin istediği gibi görünüyor, ama gerçekten bu çoklu talep üzerine bir yaklaşıma sahip olmanın muhtemelen kötü bir fikir olduğunu vurgulamak istiyorum. Endişe verici performans ise, bulut tarzı yatay olarak ölçeklenebilir sistemler için mimarlık yapmalısınız (bu, HTTP tabanlı sistemlerin mükemmel olduğu bir şey)
David Grinberg,

44
@DavidGrinberg Daha fazla katılmıyorum. Bireysel eylemler ucuzsa, bir talebi yerine getirme yükü, eylemin kendisinden çok daha maliyetli olabilir. Öneriniz, bir veritabanında birden çok satırın güncellenmesinin her satır ayrı bir istek olarak gönderildiği için satır başına ayrı bir işlem kullanılarak yapıldığı senaryolara yol açabilir. Bu sadece korkunç derecede verimsiz değil, aynı zamanda gerekirse birden fazla satırı atomik olarak güncellemenin mümkün olmayacağı anlamına geliyor. Yatay ölçeklendirme önemlidir ancak verimli tasarımların yerine geçmez.
kasperd

4
İyi söylenmiş ve performans ve / veya atomiklik gibi iş gereksinimlerinin gerçeklerine karşı cahil insanlar tarafından yapılan REST API uygulamalarının tipik bir konusuna dikkat çekiyor. Bu nedenle, örneğin, OData REST şartnamesi, bir aramada çoklu işlemler için bir toplu iş formatına sahiptir - buna gerçek bir ihtiyaç vardır.
TomTom

8
@TomTom, OP gelmez atomicity istiyorum. Bir atomik işlemin sadece bir durumu olduğundan, tasarımı çok daha kolay bir şey olurdu. Ayrıca, HTTP belirtimi, HTTP / 2 çoklamasıyla (doğal olarak, HTTP / 2 desteği başka bir meseledir, ancak özellik buna izin verir) yoluyla toplu işlemlere izin verir.
Paul Draper,

2
@David Geçmişte bazı HPC sorunları üzerinde çalışmış olsaydım, deneyimime göre, tek bir bayt gönderme maliyeti bin tane göndermekle hemen hemen aynıdır (farklı transfer ortamları kesinlikle farklı bir ek yüke sahiptir, ancak bundan daha nadiren daha iyidir). Bu nedenle performans bir endişe ise, birden fazla isteğin gönderilmesinin nasıl bir yükü olmayacağını göremiyorum. Şimdi, aynı bağlantı üzerinden birden fazla isteği çoğaltabilseydiniz, bu sorun ortadan kalkacaktı, ama anladığım kadarıyla, bu yalnızca HTTP / 2 olan bir seçenek ve bunun için destek oldukça sınırlı. Yoksa bir şey mi kaçırıyorum?
Voo

24

Her ne kadar çoklu durum bir seçenek olsa da, bütün istekler başarılı olursa 200 (Her şey yolunda) ve aksi takdirde bir hata (500 ya da belki 207) döndürürdüm.

Standart durum genellikle 200 olmalı - her şey çalışıyor. Ve müşterilerin sadece bunu kontrol etmesi gerekir. Ve sadece hata durumunda, 500 (veya 207) döndürebilirsiniz. Bence 207, en az bir hata durumunda geçerli bir seçimdir, ancak paketin tamamını tek bir işlem olarak görürseniz, 500 de gönderebilirsiniz. - Müşteri hata mesajını her iki şekilde de yorumlamak isteyecektir.

Neden her zaman 207 göndermiyorsun? - Çünkü standart durumlar kolay ve standart olmalıdır. İstisnai durumlar olağanüstü olsa da. Bir müşteri, yalnızca istisnai bir durum garanti ederse, cevap kuruluşunu okumalı ve daha karmaşık kararlar vermelidir.


6
Tam olarak aynı fikirde değilim. 1. ve 3. alt istekler başarılı olursa, birleşik bir kaynak alırsınız ve birleştirilmiş cevabı yine de kontrol etmeniz gerekir. Düşünmen gereken bir dava daha var. Cevap = 200 ise veya cevap 1 = 200 ise, istek 1 başarılı oldu. Cevap = 200 ise veya cevap 2 = 200 ise, sadece 2 yanıtı test etmek yerine 2 başarılı ve daha fazlasını talep edin.
gnasher729

1
@ gnasher729 gerçekten uygulamaya bağlıdır. Tüm talepler başarılı olduğunda basitçe (herşey yolunda) ile bir sonraki adıma akacak olan kullanıcı odaklı bir eylem hayal ediyorum. - Eğer bir şeyler ters giderse (genel durum <= 200) o zaman ayrıntılı hataları görüntülemeniz ve iş akışını değiştirmeniz ve "handleMixedState" işlevinde olduğunuzdan ve "handleAllOk" işlevinde olmadığınız için yalnızca her alt istek için tek bir denetim yapmanız gerekir. .
Falco

Gerçekten ne anlama geldiğine bağlı. Örneğin, ticaret stratejilerini kontrol eden bir son noktam var. Bir çalıştırmada tanımlayıcı listesini "başlatabilirsiniz". Dönüş 200, işlemin (onları işleme koyma) başarılı olduğu anlamına gelir; ancak tümü başarıyla başlayamayabilir. Hangi sonuç, btw., Başlangıçta birkaç saniye sürebileceğinden hemen sonuçta (başlayacaktır) bile görülemez. Çoklu operasyon çağrısındaki anlambilim senaryoya göre değişir.
TomTom

Ayrıca genel bir Sorun (örneğin Veri Tabanı Aşağı) olması durumunda 500 gönderirim, böylece sunucu bireysel istekleri denemez, ancak genel bir hata verebilir. - Kullanıcı için 3 farklı sonuç olduğu için 1. hepsi Tamam, 2. genel Sorun, hiçbir şey çalışmıyor, 3. Bazı istekler başarısız oldu. -> Genellikle tamamen farklı bir program akışına neden olur.
Falco

1
Tamam, bir yaklaşım şöyle olur: 207 = her istek için kişisel durum. Başka bir şey: Geri gönderilen durum her istek için geçerlidir. 200, 401, ≥ 500 için mantıklı.
gnasher729

13

Bir seçenek her zaman 200 durum kodunu döndürmek ve ardından JSON belgenizin gövdesinde belirli hataları döndürmek olacaktır. Bu tam olarak bazı API'lerin tasarlanma şeklidir (her zaman bir durum kodu 200 döndürür ve hatayı gövdeye gönderir). Farklı yaklaşımlar hakkında daha fazla ayrıntı için, bkz. Http://archive.oreilly.com/pub/post/restful_error_handling.html


2
Bu durumda, her şeyin yolunda200 olduğunu belirtmek , istek almak ve geçerli olmak için kullanma fikrini seviyorum ve ardından bu istekte neler olduğu hakkında (yani işlemlerin sonucu) ayrıntılı bilgi vermek için JSON'u kullanın.
rickcnagy,

4

Neilsimp1'in doğru olduğunu düşünüyorum, ancak gönderilen 206 - Acceptedve sonra verileri işleyebilecek şekilde gönderilen verilerin yeniden tasarlanmasını tavsiye ederim . Belki de geri aramalarla.

Tek bir istekte birden fazla işlem göndermeye çalışmakla ilgili sorun, her bir işlemin kendi "statüsüne" sahip olması gerektiği gerçeğidir.

Bir CSV'yi içe aktarmaya bakmak (OP'nin ne hakkında olduğunu gerçekten bilmiyorum ama basit bir versiyonudur). CSV'yi POST yapın ve bir 206'yı geri alın. Ardından CSV içe aktarılabilir ve satır başı başına gösterilen bir URL'ye karşı bir GET (200) ile içe aktarma durumunu alabilirsiniz.

POST /imports/ -> 206
GET  /imports/1 -> 200
GET  /imports/1/errors -> 200 -> Has a list of errors

Bu aynı model birçok parti işlemine uygulanabilir.

POST /operations/ -> 206
GET  /operations/1 -> 200
GET  /operations/1/errors -> 200 - > Has a list of errors.

POST'u işleyen kodun yalnızca işlem verisi formatının geçerli olduğunu doğrulaması gerekir. Ardından, bazı daha sonraki zamanlarda işlemler gerçekleştirilebilir. Bir arka zemin işçisinde, böylece daha kolay ölçeklenebilirsiniz. Ardından istediğiniz zaman işlemlerin durumunu kontrol edebilirsiniz. Bir işlemin ne zaman tamamlandığını bilme ihtiyacını gidermek için yoklama veya geri aramalar veya akışlar veya herhangi bir şey kullanabilirsiniz.


2

Zaten burada birçok iyi cevap var, ancak bir yönü eksik:

Müşterilerinizin beklediği sözleşme nedir?

HTTP dönüş kodları en azından bir başarı / başarısızlık ayrımını göstermeli ve bu nedenle "yoksul adamın istisnaları" rolünü üstlenmelidir. Ardından 200 "sözleşme tamamen yerine getirildi" anlamına geliyor ve 4xx veya 5xx yerine getirilemediğini gösteriyor.

Doğal olarak, çoklu işlem talebinizin sözleşmesinin "tüm görevlerimi yap" olmasını beklerdim ve bunlardan biri başarısız olursa, o zaman istek (tamamen) başarılı değildi. Tipik olarak, bir müşteri olarak 200'ü "her şey yolunda" olarak anlarım ve 400 ve 500 ailesinden gelen kodlar beni (kısmi) bir başarısızlığın sonuçları hakkında düşünmeye zorlar. Bu nedenle, "yapılan tüm işler" için 200 kullanın ve kısmi bir başarısızlık durumunda 500 artı açıklayıcı bir cevap verin.

Farklı bir varsayımsal sözleşme "tüm eylemleri yapmayı dene" olabilir. O zaman, eylemlerin bir kısmı başarısız olursa, sözleşme ile tamamen uyumludur. Böylece her zaman 200 artı bir bireysel görev için başarı / başarısızlık bilgisini bulacağınız bir sonuç belgesini döndürürsünüz.

Peki, takip etmek istediğiniz sözleşme nedir? Her ikisi de geçerlidir, ancak birincisi (yalnızca her şeyin yapılması durumunda 200) benim için daha sezgiseldir ve tipik yazılım modelleriyle uyumludur. Hizmetin tüm görevleri tamamladığı vakaların (umarım) çoğunluğu için, müşterinin bu durumu tespit etmesi kolaydır.

Son önemli bir husus: Sözleşme kararınızı müşterilerinize nasıl iletirsiniz? Örneğin, Java'da "doAll ()" veya "tryToDoAll ()" gibi yöntem adları kullanırdım. HTTP'de, istemci geliştiricilerin adlarını görmesini, okumasını ve anlamasını umarak (buna bahse girmem) umuduyla, bitiş noktası URL'lerini uygun şekilde adlandırabilirsiniz. En az sürpriz sözleşmesini seçmek için bir neden daha.


0

Cevap:

İşlem başına yalnızca bir istek kullanın ve ek yükü kabul edin.

Bir durum kodu, bir işlemin durumunu açıklar. Bu nedenle, istek başına bir işlem yapılması mantıklıdır.

Birden fazla bağımsız işlem, istek-yanıt modelinin ve durum kodlarının dayandığı prensibi bozar. Doğayla savaşıyorsun.

HTTP / 1.1 ve HTTP / 2, HTTP isteklerinin ek yükünü çok daha düşük hale getirdi. Bağımsız taleplerin gruplandırılmasının önerilebileceği çok az durum olduğunu tahmin ediyorum.


Bahsedilen,

(1) PATCH isteğinde ( RFC 5789 ) birden fazla değişiklik yapabilirsiniz . Ancak, bu değişikliklerin bağımsız olmamalarını gerektirir; atomik olarak uygulanırlar (hepsi ya da hiçbiri).

(2) Diğerleri, 207 Çoklu Durum kodunu belirtmiştir. Ancak, bu yalnızca HTTP'nin bir uzantısı olan WebDAV ( RFC 4918 ) için tanımlanmıştır .

207 (Çok Durumlu) durum kodu, birden fazla bağımsız işlem için durum sağlar (daha fazla bilgi için Bölüm 13'e bakın).

...

Bir Çoklu Durum yanıtı, çoklu durum kodlarının uygun olabileceği durumlarda çoklu kaynaklar hakkında bilgi iletir. 'Multistatus' root [XML] öğesi, her biri ayrı bir kaynak hakkında bilgi içeren, herhangi bir sırada sıfır veya daha fazla 'yanıt' öğesini tutar.

Bir 207 WebDAV XML yanıtı, WebDAV dışı bir API'daki bir ördek kadar tuhaf olacaktır. Bunu yapma


1
Esasen @Anders'ın bir XY problemi olduğunu iddia ediyorsun . Haklı olabilirsiniz, ancak ne yazık ki, bu sorduğu soruyu yanıtlamadığınız anlamına gelir (çoklu işlem isteği için hangi durum kodunu kullanırsınız).
Azuaron,

2
@Azuaron, çocukları dövmek için en iyi kayış hangisidir? Bence "Yok" izin verilen bir cevap. Ayrıca, Andres onun fikir listesine birden fazla istek ekledi. Bu seçeneği gönülden destekledim.
Paul Draper,

Bir şekilde onu listelediğini kaçırdım. Bu durumda, aptalca bir soru olduğunu söylüyorum Sayın Yargıç!
Azuaron,

1
@Azuaron Kesinlikle bunun geçerli bir cevap olduğunu düşünüyorum. Eğer her şeyi yanlış yapıyorum, birinin söylemesini istiyorum ve bana en uçurumdan nasıl en iyi şekilde yararlanılacağı konusunda talimat vermemeyi istiyorum.
Anders,

1
İçerik Tipi üstbilgisi uygun şekilde ayarlandığı ve müşterinin istediği ile eşleştiği sürece, 207 yanıtında JSON göndermesini yasaklayan hiçbir şey yoktur (üstbilgiyi kabul et).
dolmen

0

Tek bir istekte gerçekten birden fazla işlem yapmanız gerekiyorsa, neden bir işlemi tüm işlemleri arka uçta kaydırmıyorsunuz? Bu şekilde hepsi başarılı ya da başarısız olur.

API kullanan bir müşteri olarak, bir API çağrısında başarı veya başarısızlıkla baş edebilirim. Kısmi başarının üstesinden gelmek zor, çünkü olası tüm bu durumları ele almak zorunda kalacağım.


2
Talebin atomik olması gerekiyorsa, bu soruyu göndermeyeceğini tahmin ediyorum.
Andy,

@Andy Belki, ama böyle bir tasarımın bütün sonuçlarını düşündüğünü varsayamazsınız.
Dean

İstek atomik olmamalıdır - örneğin # 2 başarısız olursa, # 1 tarafından yapılan değişiklikler devam etmelidir. Yani her şeyi tek bir işlemle sarmalamak bir seçenek değil.
Anders,
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.