REST API - Tek istekte Toplu Oluşturma veya Güncelleme [kapalı]


94

Orada iki kaynaklardır varsayalım Binderve Docbu dernek ilişkisi anlam Docve Binderkendi başlarına ayakta. Docait olabilir veya olmayabilir Binderve Binderboş olabilir.

Bir kullanıcının TEK BİR İSTEKTE , aşağıdaki gibi birDoc s koleksiyonu göndermesine izin veren bir REST API tasarlamak istersem :

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

Ve içindeki her doküman için docs,

  • Eğer varsa, doconu atayınBinder
  • Eğer docyoksa, oluşturun ve sonra atamak

Bunun nasıl uygulanması gerektiği konusunda gerçekten kafam karıştı:

  • Hangi HTTP yöntemi kullanılmalı?
  • Hangi yanıt kodu iade edilmelidir?
  • Bu REST için bile uygun mu?
  • URI nasıl görünür? /binders/docs?
  • Toplu istek işleme, ya birkaç öğe bir hata verir, ancak diğeri geçerse. Hangi yanıt kodu iade edilmelidir? Toplu işlem atomik olmalı mı?

Yanıtlar:


59

Genellikle bunun için tasarladıkları için, bunu işlemek için bir POST veya PATCH yöntemi kullanabileceğinizi düşünüyorum.

  • Bir POSTyöntem kullanmak , genellikle liste kaynağında kullanıldığında bir öğe eklemek için kullanılır, ancak bu yöntem için birkaç eylemi de destekleyebilirsiniz. Bu yanıta bakın: REST Kaynak Koleksiyonu Nasıl Güncellenir . Giriş için farklı gösterim biçimlerini de destekleyebilirsiniz (bir diziye veya tek bir öğeye karşılık geliyorlarsa).

    Bu durumda, güncellemeyi açıklamak için formatınızı tanımlamanıza gerek yoktur.

  • PATCHKarşılık gelen istekler kısmi güncellemeye karşılık geldiğinden, bir yöntemin kullanılması da uygundur. RFC5789'a göre ( http://tools.ietf.org/html/rfc5789 ):

    Köprü Metni Aktarım Protokolünü (HTTP) genişleten birkaç uygulama, kısmi kaynak değişikliği yapmak için bir özellik gerektirir. Mevcut HTTP PUT yöntemi yalnızca bir belgenin tamamen değiştirilmesine izin verir. Bu teklif, mevcut bir HTTP kaynağını değiştirmek için yeni bir HTTP yöntemi olan PATCH ekler.

    Bu durumda, kısmi güncellemeyi açıklamak için formatınızı tanımlamanız gerekir.

Bence bu durumda POSTve PATCHher öğe için yapılacak işlemi gerçekten açıklamanıza gerek olmadığı için oldukça benzer. Gönderilecek temsilin formatına bağlı olduğunu söyleyebilirim.

Durum PUTbiraz daha az net. Aslında, bir yöntem kullanırken PUT, tüm listeyi sağlamalısınız. Nitekim, talepte sağlanan temsil, liste kaynağı olanın yerine geçecektir.

Kaynak yollarıyla ilgili iki seçeneğiniz olabilir.

  • Belge listesi için kaynak yolunu kullanma

Bu durumda, talepte sağladığınız temsilde belge bağlantısını açıkça bir bağlayıcı ile sağlamanız gerekir.

İşte bunun için örnek bir yol /docs.

Bu tür bir yaklaşımın içeriği yöntem için olabilir POST:

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • Bağlayıcı öğesinin alt kaynak yolunu kullanma

Ek olarak, dokümanlar ve bağlayıcılar arasındaki bağlantıyı açıklamak için alt yollardan yararlanmayı da düşünebilirsiniz. Bir doküman ve bir ciltçi arasındaki ilişkiye ilişkin ipuçlarının artık istek içeriğinde belirtilmesi gerekmez.

İşte bunun için örnek bir yol /binder/{binderId}/docs. Bu durumda, bir yöntemle doküman listesi göndermek POSTveya yoksa dokümanı oluşturduktan sonra PATCHtanımlayıcıya binderIdsahip bağlayıcıya dokümanlar ekleyecektir .

Bu tür bir yaklaşımın içeriği yöntem için olabilir POST:

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

Yanıtla ilgili olarak, yanıt düzeyini ve döndürülecek hataları tanımlamak size kalmıştır. İki seviye görüyorum: durum seviyesi (genel seviye) ve yük seviyesi (daha ince seviye). İsteğinize karşılık gelen tüm eklerin / güncellemelerin atomik olup olmayacağını belirlemek de size bağlıdır.

  • Atomik

Bu durumda, HTTP durumundan yararlanabilirsiniz. Her şey yolunda giderse, bir statü alırsınız 200. Değilse 400, sağlanan verilerin doğru olmaması gibi başka bir durum (örneğin bağlayıcı kimliği geçerli değil) veya başka bir şey.

  • Atomik olmayan

Bu durumda, bir durum 200döndürülür ve ne yapıldığını ve sonunda hataların nerede oluştuğunu açıklamak yanıt temsiline bağlıdır. ElasticSearch, toplu güncelleme için REST API'sinde bir uç noktaya sahiptir. Bu size bu düzeyde bazı fikirler verebilir: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .

  • Eşzamansız

Ayrıca, sağlanan verileri işlemek için zaman uyumsuz bir işlem de uygulayabilirsiniz. Bu durumda, HTTP durum geri dönüşleri olacaktır 202. Müşterinin ne olacağını görmek için ek bir kaynak çekmesi gerekir.

Bitirmeden önce, OData belirtiminin varlıklar arasındaki ilişkilerle ilgili sorunu gezinme bağlantıları adı verilen özellik ile ele aldığını da fark etmek isterim . Belki şuna bir bakabilir misin ;-)

Aşağıdaki bağlantı da size yardımcı olabilir: https://templth.wordpress.com/2014/12/15/designing-a-web-api/ .

Umarım sana yardımcı olur, Thierry


Soruyu takip ettim. İç içe geçmiş bir alt kaynak olmadan düz yolları seçtim. Tüm dokümanları GET /docsalmak için belirli bir cilt içindeki tüm dokümanları arar ve alırım GET /docs?binder_id=x. Kaynakların bir alt kümesi ben çağırır silmek için DELETE /docs?binder_id=xya da ben demeliyim DELETE /docsbir ile {"binder_id": x}isteği gövdesine? Hiç PATCH /docs?binder_id=xtoplu güncelleme için kullanır mıydınız yoksa sadece PATCH /docsve çiftleri geçirir miydiniz ?
Andy Fusniak

35

Muhtemelen POST veya PATCH kullanmanız gerekecek, çünkü birden fazla kaynağı güncelleyen ve oluşturan tek bir isteğin idempotent olma ihtimali düşüktür.

Yapmak PATCH /docskesinlikle geçerli bir seçenektir. Standart yama formatlarını kendi senaryonuz için zor bulabilirsiniz. Bundan emin değilim.

200 kullanabilirsiniz. Ayrıca 207 - Çoklu Durum'u da kullanabilirsiniz.

Bu, RESTful bir şekilde yapılabilir. Bence anahtar, güncellenecek / oluşturulacak bir dizi belgeyi kabul edecek şekilde tasarlanmış bir kaynağa sahip olmaktır.

PATCH yöntemini kullanırsanız, operasyonunuzun atomik olması gerektiğini düşünürdüm. yani 207 durum kodunu kullanmaz ve ardından yanıt birimindeki başarıları ve başarısızlıkları rapor etmezdim. POST işlemini kullanırsanız, 207 yaklaşımı uygulanabilir. Hangi işlemlerin başarılı ve hangilerinin başarısız olduğunu bildirmek için kendi müdahale organınızı tasarlamanız gerekecektir. Standartlaştırılmış olanın farkında değilim.


Çok teşekkür ederim. Derken This can be done in a RESTful wayGüncelleme ve Oluşturmanın ayrı ayrı yapılması gerektiğini mi söylüyorsunuz?
Sam R.

1
@norbertpy Bir kaynak üzerinde bir tür yazma işlemi gerçekleştirmek, diğer kaynakların güncellenmesine ve tek bir istekten oluşturulmasına neden olabilir. REST'in bununla bir sorunu yok. Cümle seçimim, bazı çerçevelerin HTTP isteklerini çok parçalı belgelere serileştirerek ve ardından serileştirilmiş HTTP isteklerini toplu olarak göndererek toplu işlemler gerçekleştirmesiydi. Bu yaklaşımın kaynak tanımlama REST kısıtlamasını ihlal ettiğini düşünüyorum.
Darrel Miller

19

PUT ing

PUT /binders/{id}/docs Tek bir belgeyi oluşturun veya güncelleyin ve bir ciltleyiciyle ilişkilendirin

Örneğin:

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

YAMA ing

PATCH /docs Yoksa belgeler oluşturun ve bunları bağlayıcılarla ilişkilendirin

Örneğin:

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

Daha sonra ek bilgiler ekleyeceğim, ancak bu arada isterseniz RFC 5789 , RFC 6902 ve William Durand's Please'e bir göz atın . Aptal bir blog girişi gibi yama yapmayın .


2
Bazen müşterinin toplu işleme ihtiyacı vardır ve kaynağın orada olup olmadığı umrunda değildir. Soruda söylediğim gibi, müşteri bir demet gönderip docsonları ilişkilendirmek istiyor binders. Müşteri, mevcut değilse bağlayıcı oluşturmak ve varsa ilişkilendirmeyi yapmak ister. TEK BİR TOPLU talepte.
Sam R.

12

Çalıştığım bir projede bu sorunu 'Toplu' talepler dediğimiz bir şeyi uygulayarak çözdük. /batchAşağıdaki formatta json kabul ettiğimiz bir yol tanımladık :

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

Yanıt, 207 (Çoklu Durum) durum koduna sahiptir ve aşağıdaki gibi görünür:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

Bu yapıya başlıklar için destek de ekleyebilirsiniz. Bir toplu işteki istekler arasında kullanılacak değişkenler olan yararlı olduğu kanıtlanmış bir şey uyguladık, yani bir istekten diğerine girdi olarak yanıt kullanabiliriz.

Facebook ve Google'ın benzer uygulamaları vardır:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

Aynı çağrı ile bir kaynak oluşturmak veya güncellemek istediğinizde, duruma bağlı olarak POST veya PUT kullanırdım. Belge zaten mevcutsa, tüm belgenin şu şekilde olmasını ister misiniz:

  1. Gönderdiğiniz belge ile değiştirildi mi (yani talepte eksik özellikler kaldırılacak ve zaten mevcut olanların üzerine yazılacak)?
  2. Gönderdiğiniz belgeyle birleştirilmiş mi (yani, istekteki eksik özellikler kaldırılmayacak ve zaten mevcut mülklerin üzerine yazılacak)?

Alternatif 1'deki davranışı istiyorsanız, bir POST kullanmalısınız ve alternatif 2'deki davranışı istiyorsanız, PUT kullanmalısınız.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

İnsanların zaten önerdiği gibi, PATCH için de gidebilirsin, ancak API'leri basit tutmayı ve gerekmiyorsa ekstra fiiller kullanmamayı tercih ederim.


5
Kanıt Kanıtı ve Google ve Facebook bağlantıları için bu yanıtı beğenin. Ancak POST veya PUT ile ilgili son bölüme katılmıyorum. Bu cevapta bahsedilen 2 durumda, birincisi PUT ve ikincisi PATCH olmalıdır.
RayLuo

@RayLuo, POST ve PUT'a ek olarak neden PATCH'a ihtiyacımız olduğunu açıklayabilir misiniz?
David Berg

2
Çünkü PATCH bunun için icat edildi. Bu tanımı okuyabilir ve PUT ve PATCH'in 2 madde işaretinizle nasıl eşleştiğini görebilirsiniz.
RayLuo

@DavidBerg, Görünüşe göre Google, toplu istekleri işlemek için başka bir yaklaşımı tercih etti, yani her bir alt isteğin başlığını ve gövdesini bir ana isteğin karşılık gelen kısmına benzer bir sınırla ayırmak --batch_xxxx. Google ve Facebook çözümleri arasında bazı önemli farklılıklar var mı? Ek olarak, "bir istekten gelen yanıtı diğerine girdi olarak kullanmak" hakkında, kulağa çok ilginç geliyor, daha fazla ayrıntı paylaşır mısınız? veya ne tür bir senaryo kullanılmalıdır?
Yang
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.