RESTFul: durum değiştiren eylemler


59

RESTfull API oluşturmayı planlıyorum ama kafamda bazı problemler yaratan bazı mimari sorular var. Müşterilere arka uç iş mantığı eklemek, iş mantığının hızla değişebildiği durumlarda birden fazla istemci platformunu güncellemenin zor olduğu için kaçınmak istiyorum.

Diyelim ki makalemiz bir kaynak olarak var (api / makale), yayınlama, yayından kaldırma, etkinleştirme veya devre dışı bırakma gibi eylemleri nasıl uygulamalıyız, ancak mümkün olduğunca basit tutmaya çalışalım?

1) api / article / {id} / {action} 'i kullanmalı mıyız, çünkü uzak konumlara zorlamak ya da çoklu özellik değiştirmek gibi birçok arka uç mantığı olabilir. Muhtemelen buradaki en zor şey, tüm makale verilerini güncelleme için API'ye geri göndermemiz gerektiği ve çok kullanıcılı bir çalışmanın gerçekleştirilemeyeceğidir. Örneğin, editör 5 saniye daha eski veriler gönderebilir ve diğer bazı gazetecilerin sadece 2 saniye önce yaptığı düzeltmenin üzerine yazabilir ve bir makaleyi yayınlayanların içeriği güncellemeyle hiçbir şekilde bağlantısı olmadığı için bunu müşterilere açıklayamamın bir yolu yoktur.

2) Yeni kaynak oluşturmak da bir seçenek olabilir, api / article- {action} / id, ancak sonra döndürülen kaynak article- {action} değil, bunun uygun olup olmadığından emin olmadığım bir makale olacaktır. Ayrıca sunucu tarafı kodunda makale sınıfı her iki kaynaktan da gerçek bir işlem yapıyor ve bunun RESTfull düşüncesine aykırı olup olmadığından emin değilim.

Herhangi bir öneri bekliyoruz ..


'İşlemlerin' bir RESTful URI'sinin parçası olması tamamen yasaldır - eğer yapılacak bir eylem / algoritmayı belirtirlerse. Neyin var api/article?action=publish? Sorgu parametreleri, kaynağın durumunun bahsettiğiniz “algoritmaya” (veya eyleme) bağlı olduğu durumlar için tasarlanmıştır. Örn api/articles?sort=asc: geçerli
doktora

1
Ben kontrol önermek bu daha da size ilham verebilir değer artışı, daha RESTful çözümü.
Benjol

Api / article? Action = publish ile karşılaştığım sorunlardan biri, RESTfull uygulamasında sadece şunu yapmayı tercih ederken, RESTfull uygulamasında yayınlamak için TÜM makale verilerini göndermem gerekiyor: api / article / 4545 / publish / ek bir şey olmadan
Miro Svrtan

2
Bu konuda mükemmel kaynak ve daha fazlası: REST API Tasarımı - Kaynak Modelleme
Izhaki

@PhD: protokolde tamamen yasal olmasına rağmen, URI'ler genellikle fiiller yerine isimler olmalıdır. URI'da bir fiil sahibi olmak genellikle kötü REST tasarımının bir işaretidir.
Yalan Ryan

Yanıtlar:


49

Burada açıklanan uygulamaları yararlı buluyorum :

CRUD operasyonlarının dünyasına uymayan eylemlere ne dersiniz?

İşlerin bulanıklaştığı yer burasıdır. Birkaç yaklaşım var:

  1. Bir kaynak alanı gibi görünecek eylemi yeniden yapılandırın. Bu işlem işlem parametreleri almazsa çalışır. Örneğin, bir etkinleştirme eylemi bir boole activatedalanına eşleştirilebilir ve PATCH aracılığıyla kaynağa güncellenebilir.
  2. RESTful ilkeleri olan bir alt kaynak gibi görün. Örneğin, Github en API sağlayan bir özü yıldız ile PUT /gists/:id/starve ekleme veya kaldırma ile DELETE /gists/:id/star.
  3. Bazen, hareketi mantıklı bir RESTful yapıyla eşleştirmenin hiçbir yolu yoktur. Örneğin, çok kaynaklı bir arama, belirli bir kaynağın son noktasına uygulanacak bir anlam ifade etmiyor. Bu durumda, /searchbir kaynak olmamasına rağmen en mantıklı olur. Bu tamam - API tüketicisinin bakış açısından doğru olanı yapın ve karışıklığı önlemek için açıkça belgelendiğinden emin olun.

Bu yaklaşıma 2 oy verdim. API arayanlar için sakar yapay kaynaklar gibi görünse de, gerçekte sunucuda neler olup bittiği hakkında hiçbir bilgisi yok. /article/123/deactivationsMadde 123 için yeni bir devre dışı bırakma isteği oluşturmak üzere POST'u ararsam, sunucu yalnızca istenen kaynağı devre dışı bırakmakla kalmayabilir, aslında devre dışı bırakma isteğimi de kaydedebilir, böylece durumunu daha sonra alabilirim.
JustAMartin

2
Neden PUT /gists/:id/star değil POST /gists/:id/star?
Filip Bartuzi

9
@FilipBartuzi PUT idempotent olduğu için - yani, aynı parametrelerle kaç kez bir işlem yaparsanız yapın sonuç her zaman aynıdır (örneğin, bir lambayı kapatıp açsanız, değişir. yine açık, hiçbir şey değişmiyor - zaten açık. POST belirsiz değildir - yani aynı parametrede bile bir eylem gerçekleştirdiğinizde, eylemin farklı bir sonucu olur (örneğin, birine bir mektup gönderirseniz, o kişiye bir mektup gönderilir. Aynı bir mektup gönderirseniz, Aynı kişi, şimdi 2 harf var).
Raphael,

9

Tanımladığınız "yayınla" eylemi gibi sunucu tarafında büyük durum ve davranış değişikliklerine neden olan işlemlerin REST'te açıkça modellenmesi zordur. Sık sık gördüğüm bir çözüm, bu karmaşık davranışı dolaylı olarak veriler aracılığıyla yürütmektir.

Çevrimiçi bir satıcı tarafından sunulan bir REST API'sinden mal siparişi vermeyi düşünün. Sipariş vermek karmaşık bir işlemdir. Birkaç ürün paketlenecek ve sevk edilecek, hesabınızdan ücret alınacak ve bir makbuz alacaksınız. Siparişinizi sınırlı bir süre için iptal edebilirsiniz ve elbette ürünleri iade için geri göndermenize olanak tanıyan tam bir para iade garantisi vardır.

Karmaşık bir satın alma işlemi yerine, böyle bir API, yeni bir kaynak, satınalma siparişi yaratmanıza izin verebilir. Başlangıçta, üzerinde istediğiniz değişiklikleri yapabilirsiniz: ürün ekleyebilir veya çıkarabilir, teslimat adresini değiştirebilir, başka bir ödeme seçeneği seçebilirsiniz veya siparişinizi tamamen iptal edebilirsiniz. Bunların hepsini yapabilirsiniz, çünkü henüz bir şey satın almadınız, sadece sunucudaki bazı verileri değiştiriyorsunuz.

Satın alma siparişiniz tamamlandıktan ve ödemesiz süresi geçtikten sonra, sunucu başka değişiklikleri önlemek için siparişinizi kilitler. Ancak şu anda karmaşık işlem dizisi başlar, ancak doğrudan, daha önce satınalma siparişine verdiğiniz veriler aracılığıyla dolaylı olarak kontrol edemezsiniz.

Açıklamanıza göre, "yayınla" bu şekilde uygulanabilir. Bir işlemi göstermek yerine, incelediğiniz ve yayınladığınız / yeni bir kaynak olarak yayınlamak istediğiniz taslağın bir kopyasını yerleştirirsiniz. Bu, yayınlama işleminin kendisi saatler sonra tamamlansa bile, taslakta daha sonra yapılan güncellemelerin yayınlanmayacağını garanti eder.


Tüm kaynağı yayınlanmamış makaleden taslak haline getirme fikri tam olarak bu davaya uyacak ancak genel olarak bir kaynakta var olan diğer tüm eylemlere uymayacaktır. REST'in bile idare etmesi gerekiyor mu? Belki de kötüye kullanıyorum ve sadece CRUD olarak kullanmamalı ve herhangi bir mantığın sorgunun içinde olmasını beklemeyeceğim SQL sorguları gibi kullanmalı.
Miro Svrtan

Neden hepsini soruyorum? Bir süre önce web uygulamaları çok platformlu hale gelmeye başladığından ve iOS, Android, web, masaüstündeki iş mantığı ve diğer platformların akla geldiği şeyleri yapmak oldukça imkânsız hale geldiğinden beri sunucuda çok sayıda iş mantığı bulundurmayı tercih ederim. hızlı bir şekilde ve küçük bir BL parçası değiştirirken geriye dönük uyumluluk sorunlarından kaçınmak istiyorum.
Miro Svrtan

2
REST'in iş mantığını gayet iyi idare edebileceğini düşünüyorum, ancak mevcut iş mantığını REST düşünmeden açıklamak için uygun değil. Bu nedenle, Microsoft, SAP ve diğerleri gibi birçok şirket, sizin dediğiniz gibi, yalnızca CRUD işlemleri ile ilgili verileri gösterir. Nasıl yaptıklarını görmek için Açık Veri Protokolü'ne göz atın.
Ferenc Mihaly

7

Tüm makale verilerini güncelleme için API'ye geri göndermemiz gerekiyor ve çok kullanıcılı çalışma yapılamadı. Örneğin, editör 5 saniye daha eski veriler gönderebilir ve diğer bazı gazetecilerin sadece 2 saniye önce yaptığı düzeltmenin üzerine yazabilir ve bir makaleyi yayınlayanların içeriği güncellemeyle hiçbir şekilde bağlantısı olmadığı için bunu müşterilere açıklayamamın bir yolu yoktur.

Bu tür bir şey ne yaparsanız yapın bir meydan okumadır, Dağınık kaynak kontrolüne (mercurial, git, vb.) Benzer bir problemdir ve HTTP / ReST'de yazılan çözüm biraz benzer görünüyor.

Her ikisinin de üzerinde çalıştığı Alice ve Bob olmak üzere iki kullanıcınız olduğunu varsayalım /articles/lunch. (netlik için, cevap kalın şekildedir)

İlk önce, Alice makaleyi yaratır.

PUT /articles/lunch HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

301 Moved Permanently
Location: /articles/lunch/1 

Sunucu bir kaynak oluşturmadı, çünkü isteğe eklenmiş bir "sürüm" yoktu, (bir tanımlayıcı varsayarak /articles/{id}/{version}. Yaratmayı gerçekleştirmek için, Alice, oluşturacağı makalenin / sürümün URL'sine yönlendirildi. aracı daha sonra isteği yeni adreste yeniden uygular.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

201 Created

Ve şimdi makale oluşturuldu. sonra, Bob makaleye bakar:

GET /articles/lunch HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

301 Moved Permanently
Location: /articles/lunch/1 

Bob orada görünüyor:

GET /articles/lunch/1 HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

200 Ok
Content-Type: text/plain

Hey Bob, what do you want for lunch today?

Kendi değişikliğini eklemeye karar verir.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

301 Moved Permanently
Location: /articles/lunch/2

Alice'te olduğu gibi, Bob yeni bir sürüm oluşturacağı yere yönlendirilir.

PUT /articles/lunch/2 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

201 Created

Sonunda, Alice kendi makalesine eklemek istediğine karar verir:

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?
I was thinking about getting Sushi.

409 Conflict
Location: /articles/lunch/3
Content-Type: text/diff

---/articles/lunch/2
+++/articles/lunch/3
@@ 1,2 1,2 @@
 Hey Bob, what do you want for lunch today?
-Does pizza sound good to you, Alice?
+I was thinking about getting Sushi.

Normal olarak yönlendirilmek yerine, müşteriye farklı bir durum kodu geri gönderilir; 409bu, Alice'e dallamaya çalıştığı sürümün zaten dallanmış olduğunu söyler. Yeni kaynaklar yine de yaratıldı ( Locationbaşlıkta gösterildiği gibi ) ve ikisi arasındaki farklar yanıt organına dahil edildi. Alice şimdi, az önce yaptığı isteğin nasıl birleştirileceğini biliyor.


Tüm bu yeniden yönlendirme, PUTtam olarak istek satırının istediği yerde yeni kaynakların yaratılmasını gerektiren anlamıyla ilgilidir. bu aynı zamanda POSTbunun yerine bir istek döngüsünü de kaydedebilir , ancak daha sonra sürüm numarasının, çizim amacıyla bana daha az açık görünen başka bir sihir tarafından talep edilmesi durumunda kodlanması gerekecekti, ancak muhtemelen hala gerçek bir API'de tercih edilecekti. istek / yanıt döngülerini en aza indirmek için.


1
Versoning burada bir sorun değildi, sadece olası sorunların örneği olarak söz konusu yayın eylem için bir kaynak olarak makale kullanılıyorsa
Miro Svrtan

3

İşte belgelerin içeriğiyle değil geçici durumla daha çok ilgilenen başka bir örnek. (Versiyonlama buluyorum - genel olarak, her versiyonun yeni bir kaynak olabileceği düşünüldüğünde - bir tür kolay problem.)

Diyelim ki bir makinede çalışan bir hizmeti REST yoluyla göstermek, böylece durdurulabilir, başlatılabilir, yeniden başlatılabilir ve benzeri.

Buradaki en RESTful yaklaşım nedir? POST / service? Command = örneğin yeniden başlat? Ya da 'koşuyor' diyelim ki POST / hizmet / devlet?

Buradaki en iyi uygulamaları ve REST'in bu tip bir duruma doğru yaklaşıp yaklaşmadığını kodlamak iyi olurdu.

İkincisi, bir eylemi kendi durumunu etkilemeyen bir hizmetten yürütmek istediğimi varsayalım, bunun yerine bir yan etki tetikleyeceğim. Örneğin, arama sırasında oluşturulmuş bir e-posta adresine bir rapor gönderen bir posta gönderme hizmeti.

GET / rapor, raporun bir kopyasını kendim almanın bir yolu olabilir; Fakat sunucu tarafına, e-postayla gönderme gibi diğer eylemleri yukarıda belirttiğim gibi itmek istiyorsak. Veya bir veritabanına yazma.

Bu davalar kaynak-eylem bölünmesi etrafında dans ediyor ve ben onları REST odaklı bir şekilde ele almanın yollarını görüyorum, ama açıkçası bunu yapmak için biraz kesmek gibi hissettiriyor. Belki de kilit soru, bir REST API'sinin genel olarak yan etkileri destekleyip desteklememesi gerektiğidir.


2

REST veri odaklı ve bu tür kaynaklar en iyi eylemler değil "şeyler" olarak çalışır. Http yöntemlerinin örtük anlambilimi; GET, PUT, DELETE, vb yönelimi güçlendirmeye hizmet eder. POST elbette istisnadır.

Bir kaynak, örneğin bir veri karışımı olabilir. makale içeriği; ve meta veri yani. yayınlanan, kilitli, revizyon. Verileri dilimlemek için başka birçok olası yol vardır, ancak en uygun olanı (varsa) belirlemek için ilk önce veri akışının nasıl görüneceğini incelemelisiniz. Örneğin, değişikliklerin TokenMacGuy'un önerdiği gibi makale kapsamında kendi kaynakları olması gerekebilir.

Uygulama ile ilgili olarak muhtemelen TockenMacGuy'un önerdiği gibi bir şey yapacağım. Ayrıca “kilitli” ve “yayınlanmış” gibi revizyonlara değil makale üzerine bir meta veri alanları eklerdim.


1

Bunu makalenin durumunu doğrudan değiştirmek gibi düşünmeyin. Bunun yerine, makalenin oluşturulmasını isteyen bir değişiklik emri veriyorsunuz .

Değişiklik sırasını koyarak yeni bir değişiklik siparişi kaynağı (POST) oluşturarak modelleyebilirsiniz. Çok fazla avantaj var. Örneğin, makalenin değişiklik sırasının bir parçası olarak yayınlanacağı bir gelecek tarih ve saat belirtebilir ve sunucunun nasıl uygulandığı konusunda endişelenmesine izin verebilirsiniz.

Yayıncılık anlık bir işlem değilse, müşteriye geri dönmeden önce işlemin bitmesini beklemeniz gerekmez. Siz sadece değişiklik siparişinin yaratıldığını onaylar ve değişiklik siparişi kimliğini iade edersiniz. Değişiklik sırasının durumunu paylaşmak için bu değişiklik sırasına karşılık gelen URL’yi kullanabilirsiniz.

Benim için önemli bir kavrayış, bu değişim sırası metaforunun nesne yönelimli programlamanın tanımlanmasının başka bir yolunun olduğunu görmekti. Kaynaklar yerine, nesneleri çağırırız. Değişim emri yerine, onlara mesaj diyoruz. OO’da A’dan B’ye bir mesaj göndermenin bir yolu A’nın B yöntemini çağırmasıdır. Bunu özellikle de A ve B farklı bilgisayarlarda olduğunda A’nın yeni bir nesne yaratmasını sağlamaktır. B'ye gönder. REST bu süreci basitleştirir.


Aslında bunu Aktör modeline OO'dan daha yakın olarak düşünürdüm. REST isteklerini Mesajlar (olduğu gibi) olarak kabul etmek, durumu yönetmek için onları Olay Kaynak Kullanımı'na çok iyi hizalar. REST, etkileşimlerini CQRS çizgileri boyunca düzgün bir şekilde böler. GET mesajları Sorgu, POST, PUT, PATCH, DELETE komut alanına girer.
18'de

0

Seni doğru anlarsam, sahip olduğunun teknik bir meseleden çok bir "iş kuralı" belirleme meselesi olduğunu düşünüyorum.

Bir makalenin üzerine yazılabileceği gerçeği, üst düzey kullanıcıların küçük kullanıcıların sürümlerini geçersiz kılabileceği yetki seviyelerinin getirilmesiyle çözülebilir. Ayrıca, sürümlerin yanı sıra makalenin durumunu yakalamak için bir sütunun yanı sıra (örneğin 'geliştirilme aşamasında', 'final') , vb), bunun üstesinden gelebilirsin. Ayrıca, kullanıcıya belirli bir sürümü, gönderim zamanının bir kombinasyonu ve sürüm numarasıyla seçme olanağı da verebilirsiniz.

Yukarıdaki tüm durumlarda, hizmetinizin sizin belirlediğiniz iş kurallarını yerine getirmesi gerekir. Böylece servisi şu parametrelerle çağırabilirsiniz: userid, makale, sürüm, eylem (sürümün isteğe bağlı olduğu yerlerde, bu iş kurallarınıza bağlıdır).


Bunun bir iş kuralı olduğuna inanmıyorum ama kesinlikle teknik bir kural. Sürüm ekleme fikri, geçersiz kılma kurallarına yardımcı olmak için iyi bir fikirdir, ancak içeriğin güncellenmesi ve içerik yayınlamanın ilgili eylemler olmadığı durumu çözmez.
Miro Svrtan
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.