REST API'mde PATCH veya PUT kullanmalı mıyım?


274

Geri kalan uç noktamı aşağıdaki senaryo için uygun yöntemle tasarlamak istiyorum.

Bir grup var. Her grubun bir statüsü vardır. Grup, yönetici tarafından etkinleştirilebilir veya devre dışı bırakılabilir.

Bitiş noktamı şu şekilde tasarlamalı mıyım?

PUT /groups/api/v1/groups/{group id}/status/activate

VEYA

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}

1
İkisi de iyi. Ancak JSON PATCH biçimi için RFC'ye bakın ( tools.ietf.org/html/rfc6902 ). PATCH, yük için bir tür diff / patch belgesi almayı bekliyor (ve raw JSON bunlardan biri değil).
Jørn Wildt

1
@ JørnWildt hayır, PUT korkunç bir seçim olurdu. Oraya ne koyuyorsun? PATCH tek mantıklı seçenektir. Bu durumda, soruda sunulan PATCH biçimini ve sadece PUT yöntemini kullanabilirsiniz; PUT örneği yanlıştır.
thecoshman

3
Bir istemcinin GUT ile değiştirebileceği ve değiştirebileceği bağımsız kaynaklar olarak bir veya daha fazla özelliği göstermenin yanlış bir yanı yoktur. Ancak, evet, URL, geçerli durumu okumak için "aktif" veya "etkin değil" veya GET alabileceğiniz / groups / api / v1 / groups / {grup kimliği} / durumu olmalıdır.
Jørn Wildt


4
" activate" yeterli RESTful yapımı değil. Muhtemelen status"aktif" veya "deaktif" olarak güncellemeye çalışıyorsunuz . bu durumda .../statusgövdede "etkin" veya "deaktif" dizeyle PATCH yapabilirsiniz . Veya bir boolean'ı güncellemeye çalışıyorsanız , vücuttaki boolean ile status.activePATCH yapabilirsiniz.../status/active
Augie Gardner

Yanıtlar:


328

Buradaki PATCHyöntem, mevcut bir kaynağı (grup kimliğini) güncellerken doğru seçimdir. PUTyalnızca bir kaynağı bütünüyle değiştiriyorsanız kullanılmalıdır .

Kısmi kaynak modifikasyonu hakkında daha fazla bilgi RFC 5789'da bulunabilir . Özellikle, PUTyöntem aşağıdaki gibi tarif edilir:

Köprü Metni Aktarım Protokolü'nü (HTTP) genişleten birçok uygulama, kısmi kaynak değişikliği yapmak için bir özellik gerektirir. Varolan HTTP PUT yöntemi yalnızca 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.


1
Adil olmak gerekirse, kaynağa 'etkinleştir' veya 'devre dışı bırak' dizesini koyabilirsiniz. Değiştirilecek tek şey var gibi göründüğü gibi, tamamen değiştirmek çok büyük bir anlaşma değil. Ve (önemsiz) daha küçük bir talebe izin verir.
thecoshman

35
RFC 5789'un hala teklif aşamasında olduğunu ve resmi olarak kabul edilmediğini ve şu anda 'irrata var' olarak işaretlendiğini belirtmek önemlidir. Bu 'en iyi uygulama' oldukça tartışmalıdır ve teknik olarak PATCH henüz HTTP standardının bir parçası değildir.
fishpen0

4
Birkaç yıl sonra sadece 2 sentim: durumun kendisini bir kaynak olarak düşünebilirsiniz ve eğer öyleyse, / status'a karşı PUT kullanmak teknik olarak o uç noktadaki durum kaynağının yerini alacaktır.
Jono Stewart

3
Ben "RFC" olmasına rağmen, dokümanlar karşı tartışmaya cesaret ediyorum. Dokümanlar, bir kaynağın yalnızca bir kısmını değiştirmek için PATCH kullanmanız gerektiğini belirtiyor, ancak PATCH yönteminin, idempotent olmayan bir yöntem olarak tanımlanması önemli bir şeyi atladı. Neden? Tüm kaynağın güncellenmesi / değiştirilmesi düşünülerek PUT yöntemi oluşturulduysa, amacı bir kaynağın parçasını güncellemekse neden PUT gibi idempotent bir yöntem olarak PATCH yöntemi oluşturulmadı? Bana göre, "a = 5" (PUT) ve "a = a + 5" (PATCH) gibi güncellemenin idempotensitesinde daha fazla bir fark görünüyor. Her ikisi de kaynağın tamamını güncelleyebilir.
Mladen B.

179

R REST kaynak açılımı

(Bu doğru değildir, çünkü Temsili anlamına gelir, ancak Kaynakların REST'teki önemini hatırlamak iyi bir hile).

Hakkında PUT /groups/api/v1/groups/{group id}/status/activate: Eğer edilir değil bir "etkinleştirmek" güncelleme. "Aktive" bir şey değil, bir fiildir. Fiiller asla iyi bir kaynak değildir. Temel kural: eylem, bir fiil, URL'de bulunuyorsa, büyük olasılıkla RESTful değildir .

Onun yerine ne yapıyorsun? Ya bir Gruptaki bir aktivasyonu "ekler", "kaldırır" ya da "güncellersiniz" ya da isterseniz: bir Gruptaki "durum" kaynağını değiştirmek. Şahsen ben "aktivasyonları" kullanırım çünkü onlar "statü" kavramından daha az belirsizdirler: bir durum yaratmak belirsizdir, bir aktivasyon yaratmak değildir.

  • POST /groups/{group id}/activation Bir etkinleştirme oluşturur (veya oluşturulmasını ister).
  • PATCH /groups/{group id}/activationMevcut bir aktivasyonun bazı ayrıntılarını günceller. Bir grupta yalnızca bir etkinleştirme olduğundan, hangi etkinleştirme kaynağına başvurduğumuzu biliriz.
  • PUT /groups/{group id}/activationEski aktivasyonu ekler veya değiştirir. Bir grupta yalnızca bir etkinleştirme olduğundan, hangi etkinleştirme kaynağına başvurduğumuzu biliriz.
  • DELETE /groups/{group id}/activation Etkinleştirmeyi iptal eder veya kaldırır.

Bu model, bir Grubun "etkinleştirilmesi" nin ödemeler, gönderilen postalar vb. Sadece POST ve PATCH bu gibi yan etkilere sahip olabilir. Örneğin, bir aktivasyonun silinmesinin kullanıcıları posta yoluyla bildirmesi gerektiğinde DELETE doğru seçim değildir; bu durumda muhtemelen istediğiniz bir deaktivasyon kaynak oluşturmak : POST /groups/{group_id}/deactivation.

Bu yönergeleri takip etmek iyi bir fikirdir, çünkü bu standart sözleşme müşterileriniz ve müşteri ile sizin aranızdaki tüm proxy'ler ve katmanlar için yeniden denemenin ne zaman güvenli olduğunu ve ne zaman yapılmadığını bilir. İstemcinin kesintili wifi ile bir yerde olduğunu ve kullanıcısının "devre dışı bırak" ı tıkladığını ve bu da aşağıdakileri tetiklediğini varsayalım DELETE: Bu başarısız olursa, müşteri 404, 200 veya işleyebileceği başka bir şey alana kadar yeniden deneyebilir. Ancak bir tetikleme yaparsa POST to deactivationyeniden denememeyi bilir: POST bunu ima eder.
Artık herhangi bir müşterinin, HTTP kütüphanesinin arka uca çağrıyı yeniden denemeye devam etmesi nedeniyle "grubunuz devre dışı bırakıldı" 42 e-postası göndermeye karşı koruyacak bir sözleşmesi var.

Tek bir özelliği güncelleme: PATCH kullanın

PATCH /groups/{group id}

Bir özelliği güncellemek istediğinizde. Örneğin, "durum" Google Grupları'nda ayarlanabilen bir özellik olabilir. "Durum" gibi bir özellik, genellikle değerlerin beyaz listesiyle sınırlanmak için iyi bir adaydır. Örnekler bazı tanımlanmamış JSON şemasını kullanır:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Yan etkileri olmadan kaynağı değiştirmek PUT kullanın.

PUT /groups/{group id}

Grubun tamamını değiştirmek isterseniz. Bu, sunucunun aslında yeni bir grup oluşturduğu ve eskisini attığı anlamına gelmez, örneğin kimlikler aynı kalabilir. Ama müşteriler için, PUT budur olabilir müşteri o sunucunun verdiği yanıta bağlı olarak tamamen yeni bir öğe, alır üstlenmesi gerektiğini: demek.

İstemci, bir PUTistek durumunda, yeni bir öğe oluşturmak için gereken tüm verilere sahip olarak her zaman kaynağın tamamını göndermelidir: genellikle POST oluşturma ile aynı veriler gerekir.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

Çok önemli bir gereklilik PUTidempotenttir: bir Grubu güncellerken (veya bir aktivasyonu değiştirirken) yan etkilere ihtiyacınız varsa, kullanmalısınız PATCH. Bu nedenle, güncelleme, örneğin bir posta göndermekle sonuçlandığında, kullanmayın PUT.


3
Bu benim için çok bilgilendiriciydi. "Bu örüntü, bir grubun" aktivasyonunun "yan etkileri olduğunda faydalıdır" - Bu örüntü, özellikle OP başlangıç ​​uç noktalarının aksine, eylemlerin yan etkileri olduğunda, ne kadar faydalıdır
Abdul

1
@Abdul, desen birçok nedenden dolayı yararlıdır, ancak wrt yan etkileri, bir müşterinin, bir eylemin ne gibi etkileri olduğu çok açık olmalıdır. Diyelim ki, bir iOS uygulaması adres defterinin tamamını "kişiler" olarak göndermeye karar verdiğinde, Kişinin oluşturma, güncelleme, silme vb. Yan etkilerinin son derece net olması gerekir. Örneğin tüm temasların toplu olarak gönderilmesini önlemek için.
16:59, berkes

1
RESTfull'da PUT aynı zamanda Varlıkları da değiştirebilir - Örneğin, paralel bir isteğin başarısız olmasına neden olabileceği PrimaryKey Kimliği. (örneğin tüm varlığın güncellenmesi bazı satırları silmeli ve yeni satırlar eklemeli, bu nedenle yeni varlıklar oluşturmalıdır) PATCH hiçbir zaman bunu yapamaz, diğer "uygulamaları" etkilemeden sınırsız sayıda PATCH isteğine izin verir
Piotr Kula

1
Çok yardımcı bir cevap. Teşekkürler! Ayrıca, Luke'un cevabında olduğu gibi, PUT / PATCH arasındaki farkın sadece / kısmi güncelleme olmadığını, aynı zamanda farklı olan idempotency olduğunu belirten bir yorum da ekleyeceğim. Bu bir hata değildi, kasıtlı bir karardı ve bence HTTP yönteminin kullanımına karar verirken pek çok insan bunu dikkate almıyor.
Mladen B.

1
@richremer hizmetleri, modeller gibi, iç soyutlamalardır. Tıpkı REST uç noktaları -veya-ORM modelleri veya hatta veritabanı tabloları arasında 1-1 bir ilişki gerektirmesi gibi zayıf bir soyutlama olduğu gibi, Hizmetlerin ortaya çıkarılması da zayıf bir soyutlamadır. Dışarıdan, API'nız alan modellerini iletmelidir. Bunları dahili olarak nasıl uyguladığınız API ile ilgili değildir. API'nizi değiştirmek zorunda kalmadan bir ActivationService'ten CQRS tabanlı bir aktivasyon akışına geçiş yapmakta özgür olmalısınız.
Berkes

12

'Grup' kaynağınızın birçok özelliği olduğu için PATCH kullanmanızı öneririm, ancak bu durumda yalnızca etkinleştirme alanını güncelliyorsunuz (kısmi değişiklik)

RFC5789'a göre ( https://tools.ietf.org/html/rfc5789 )

Varolan HTTP PUT yöntemi yalnızca 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.

Ayrıca, daha ayrıntılı olarak,

PUT ve PATCH istekleri arasındaki fark, sunucunun ekteki varlığı
İstek-URI tarafından tanımlanan kaynağı değiştirmek için işleme biçiminde yansıtılır . Bir PUT isteğinde, ekteki varlık
kaynak sunucuda depolanan kaynağın değiştirilmiş bir sürümü olarak kabul edilir ve istemci depolanan sürümün
değiştirilmesini ister. Ancak PATCH ile ekteki varlık,
kaynak sunucuda bulunan bir kaynağın yeni bir sürüm üretmek için nasıl değiştirilmesi gerektiğini açıklayan bir dizi talimat içerir . PATCH yöntemi, İstek URI'sı tarafından tanımlanan kaynağı etkiler ve
ayrıca diğer kaynaklar üzerinde yan etkileri olabilir; yani yeni kaynaklar
bir
PATCH uygulamasıyla oluşturulabilir veya mevcut olanlar değiştirilebilir .

PATCH, [RFC2616], Bölüm 9.1 tarafından tanımlanan şekilde ne güvenli ne de idempotenttir.

Müşterilerin PUT yerine PATCH'i ne zaman kullanacaklarını seçmeleri gerekir. Örneğin
, düzeltme eki belge boyutu
bir PUT'da kullanılacak yeni kaynak verilerinin boyutundan büyükse
, PATCH yerine PUT kullanılması mantıklı olabilir . POST ile karşılaştırma daha da zordur, çünkü POST çok çeşitli şekillerde kullanılır
ve sunucu seçerse PUT ve PATCH benzeri işlemleri kapsayabilir. İşlem
, İstek URI'sı tarafından tanımlanan kaynağı öngörülebilir bir şekilde değiştirmezse, PATCH
veya PUT yerine POST dikkate alınmalıdır .

PATCH için yanıt kodu:

204 yanıt kodu, yanıt bir ileti gövdesi taşımadığından (200 kodla bir yanıtın sahip olacağı) kullanılır. Diğer başarı kodlarının da kullanılabileceğini unutmayın.

ayrıca bkz. thttp: //restcookbook.com/HTTP%20Methods/patch/

Uyarı: PATCH uygulayan bir API atomik olarak yama yapmalıdır. Bir GET tarafından talep edildiğinde kaynakların yarı yamalanması mümkün OLMAMALIDIR.


7

REST mimari stilini kullanarak bir API tasarlamak istediğinizden, hangi kavramların kaynak olarak gösterilebilecek kadar önemli olduğuna karar vermek için kullanım durumlarınızı düşünmeniz gerekir. Bir grubun durumunu alt kaynak olarak göstermeye karar verirseniz, gruba aşağıdaki URI'yi verebilir ve hem GET hem de PUT yöntemleri için destek uygulayabilirsiniz:

/groups/api/groups/{group id}/status

Modifikasyon için PATCH üzerine bu yaklaşımın dezavantajı, bir grubun birden fazla özelliğinde atomik ve işlemsel olarak değişiklik yapamayacağınızdır. İşlem değişiklikleri önemliyse PATCH kullanın.

Durumu bir grubun alt kaynağı olarak göstermeye karar verirseniz, grubun temsilindeki bir bağlantı olmalıdır. Örneğin, aracı 123 grubunu alıp XML'yi kabul ederse, yanıt gövdesi şunları içerebilir:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

REST mimari stilinin uygulama durumu koşulunun motoru olarak hiper ortamı yerine getirmek için bir köprü gereklidir .


0

Genellikle activate/ deactivatealt-kaynak (ile bir Linkbaşlık ile bağlantılı) gibi biraz daha basit bir şey tercih ediyorum rel=service.

POST /groups/api/v1/groups/{group id}/activate

veya

POST /groups/api/v1/groups/{group id}/deactivate

Tüketici için bu arayüz son derece basittir ve sizi "aktivasyonları" bireysel kaynaklar olarak kavramsallaştırmaktan alıkoymadan REST ilkelerini takip eder.


0

Bu davranışı uygulamak için olası bir seçenek

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

Birisi ihtiyaç dışı bırakmak eğer Açıkçası, PUTsahip olacaktırDeactivated JSON'da durumu .

Kitle aktivasyonu / devre dışı bırakılması PATCHgerektiğinde oyuna adım atabilir (tam grup için değil, groupskaynak için:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

Genel olarak bu, @Andrew Dobrowolski'nin öne sürdüğü gibi bir fikirdir, ancak kesin gerçekleştirmede küçük değişiklikler vardır.

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.