RESTful API. Oluşturulan / güncellenen nesneyi iade etmeli miyim?


36

WebApi kullanarak bir RESTful web hizmeti tasarlıyorum ve nesneleri güncellerken / oluştururken hangi HTTP yanıtlarının ve yanıt gövdelerinin geri döneceğini merak ediyorum.

Örneğin bazı JSON'ları web servisine göndermek ve ardından bir nesne oluşturmak için POST yöntemini kullanabilirim. HTTP durumunu ayarlamak (201) veya ok (200) olarak ayarlamak ve basitçe "Yeni Çalışan eklendi" gibi bir mesaj döndürmek ya da orijinal olarak gönderilen nesneyi döndürmek en iyi yöntem midir?

Aynı PUT yöntemi için de geçerlidir. Hangi HTTP durumunu kullanmak en iyisidir ve oluşturulan nesneyi mi yoksa sadece bir mesajı mı geri göndermem gerekir? Kullanıcının hangi nesneyi oluşturmaya / güncellemeye çalıştığını bildiği gerçeğini göz önüne alarak.

Düşüncesi olan var mı?

Örnek:

Yeni Çalışan ekle:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

Mevcut çalışanı güncelle:

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Tepkiler:

Oluşturulan / güncellenen nesneyle yanıt

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Sadece mesajla cevap:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

Sadece durum koduyla yanıt:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT

2
Güzel soru, ancak "en iyi uygulama" terimini kullanmak bu sitedeki bir tabudur meta.programmers.stackexchange.com/questions/2442/… Sadece soruyu yeniden yazmak isteyebilirsiniz. meta.programmers.stackexchange.com/questions/6967/…
Snoop

3
Bir miktar takip olarak, bir mobil uygulamanın WiFi üzerinde iken tüm nesnenin geri dönmesini, ancak hücresel verileri kullanırken yalnızca ID'nin alınabilmesi için istek üzerine bir bayrak koymak iyi bir fikir olabilir mi? JSON'u kirletmekten kaçınmak için kullanılması gereken bir başlık var mı?
Andrew, Monica

@AndrewPiliser İlginç bir fikir, kişisel olarak bir yaklaşım seçmenin ve buna bağlı kalmanın en iyisi olduğunu düşünüyorum. Sonra uygulamanız geliştikçe veya popülerleştikçe, onu optimize edin
iswinky

@AndrewPiliser fikriniz UPDATE/INSERT ... RETURNINGSQL için Postgresql değişkenine çok benzer . Özellikle yeni verilerin sunulmasını ve güncellenmiş atom sürümü için talepte bulunmasını sağladığı için son derece kullanışlıdır.
beldaz

Yanıtlar:


31

Çoğu şeyde olduğu gibi, buna bağlı. Tradeoff'unuz ağ boyutuna göre kullanım kolaylığı. Müşterilerin oluşturulan kaynağı görmesi çok yararlı olabilir. Son oluşturma zamanı gibi, sunucu tarafından doldurulan alanları içerebilir. idKullanmak yerine dahil gibi gözüktüğünüz için hateoas, müşteriler muhtemelen kullandıkları kaynağın kimliğini görmek isteyeceklerdir POST.

Oluşturulan kaynağı eklemiyorsanız, lütfen keyfi bir mesaj oluşturmayın. 2xx ve Konum alanları, müşterilerin isteklerinin doğru bir şekilde yerine getirildiğinden emin olmaları için yeterli bilgidir.


+1 Müşterinin uri'yi oluşturmasına izin vermeme hedefi, müşterinin sunucu tarafından sağlanan URL şablonlarını belirli kimlik bilgileriyle doldurmasına izin verilerek de elde edilebilir. Evet, müşteri "yalnızca" boşlukları doldurun "biçiminde" oluşturur ". Saf HATEOAS olmasa da hedefe ulaşır ve (büyük) “aksiyon” sayısına sahip nesnelerle çalışmayı sağlar, uri biraz daha az bant genişliğine duyarlıdır;
Marjan Venema

3
"Lütfen keyfi bir mesaj oluşturma" tavsiyesi üzerine +1
HairOfTheDog

"Keyfi mesaj yok" ifadesi dize mesajlarına mı yoksa yaratılan kaynak olmayan herhangi bir dönüş değerine mi odaklanıyor ? Yaratılan kaynağın kimliğini (ancak kaynağın kendisine değil) döndürdüğümüz vakalara odaklanıyorum ve bunun nereye sığdığını merak ediyordum.
Flater

12

Şahsen ben her zaman sadece dönerim 200 OK.

Sorunuzu alıntılamak için

Kullanıcının hangi nesneyi oluşturmaya / güncellemeye çalıştığını bildiği gerçeğini göz önüne alarak.

Müşteriye ne bildiğini anlatması için neden ekstra bant genişliği (bunun için ödenmesi gerekebilir) eklesin?


1
Düşündüğüm şey buydu, geçersizse doğrulama iletilerini döndürebilirsin, ancak eğer geçerli ve yaratılmış / güncellenmişse HTTP durum kodunu kontrol et ve kullanıcıya bir mesaj göster; örneğin, buna göre "Yaşasın"
iswinky

3
Bkz stackoverflow.com/a/827045/290182 ilişkin 200/ 204 No ContentjQuery ve benzeri kafa karıştırıcı önlemek için.
beldaz

10

@iswinky POST ve PUT durumunda her zaman yükü geri gönderirdim.

POST durumunda, dahili bir kimliğe veya UUID'ye sahip bir varlık oluşturabilirsiniz. Bu nedenle yükü geri göndermek mantıklıdır.

Benzer şekilde, PUT durumunda, kullanıcının bazı alanlarını (değişmeyen değerler, örneğin) görmezden gelebilir veya PATCH durumunda, veriler de diğer kullanıcılar tarafından değiştirilmiş olabilir.

Verileri kaldığı gibi geri göndermek her zaman iyi bir fikirdir ve kesinlikle zarar vermez. Arayan kişi bu geri dönen verilere ihtiyaç duymazsa, o zaman işleme koymaz, ancak statusCode'u kullanır. Ayrıca, bu verileri kullanıcı arayüzünü güncellemek için bir şey olarak kullanabilirler.

Sadece bir SİLME durumunda, yükü geri göndermeyeceğim ve yanıt içeriğine sahip bir 200 veya yanıt içeriğine sahip olmayan bir 204 yapacağım.

Düzenleme: Aşağıdan gelen bazı yorumlar sayesinde cevabımı yeniden değerlendiriyorum. API'larımı tasarlama ve yanıtları geri gönderme yolunda hala hazırım ancak bazı tasarım düşüncelerimi nitelendirmenin bir anlamı var.

a) Yükü geri gönder deyince, aslında talepte yer alan yükü değil, kaynağın verilerini geri göndermek demek istedim. Örn: bir oluşturma yükü gönderirseniz, arka uçta UUID ve (belki) zaman damgaları ve hatta bazı (grafik) bağlantıları gibi başka varlıklar da oluşturabilirim. Bunların hepsini yanıt olarak geri gönderirdim (sadece istek yükü değil - ki bu anlamsız).

b) Yükün çok büyük olması durumunda cevapları geri göndermem. Bunu yorumlarda tartıştım, ama uyarmak istediğim şey, API'lerimi veya kaynaklarımı çok büyük yüklere sahip olmayacak şekilde tasarlamak için elimden geleni yapacağım. Kaynakları, her kaynağın 15-20 JSON özelliği ile tanımladığı ve daha büyük olmadığı şekilde daha küçük ve yönetilebilir varlıklara ayırmaya çalışacağım.

Nesnenin çok büyük olması veya ana nesnenin güncellenmesi durumunda, yuvalanmış yapıları href olarak geri gönderirim.

Alt satırda, kesinlikle tüketici / kullanıcı arayüzünün hemen işlemesi ve sadece kullanıcı arayüzünü güncellemek için 2-5 daha fazla API almak yerine gidip atomik bir API eylemi ile yapılması için mantıklı olan verileri kesinlikle geri göndermeye çalışacağım. Verilerin oluşturulması / güncellenmesi.

Sunucudan sunucuya API'ler bu konuda farklı düşünebilir. Bir kullanıcı deneyimi sağlayan API'lere odaklanıyorum.


Yükün tamamı büyük olduğunda geri yükün geri gönderilmesinin kötü bir fikir olduğu birçok durumu görebiliyorum.
beldaz

2
@beldaz tamamen katılıyorum. YMMV, REST API tasarımına dayanmaktadır. Genelde çok büyük nesnelerden kaçınır ve onu bir dizi alt kaynak / PUT'a bölerim. Eğer yük çok büyükse, bunu yapmanın daha iyi yolları vardır ve bunun içinde HATEOAS yapmak istersiniz (yukarıda Marjan'ın söylediği gibi) referansı nesnenin yerine nesneye geri gönderirsiniz.
ksprashu

@ksprashu: "Dolayısıyla yükü geri göndermenin bir anlamı var" - Bunun kötü bir fikir olduğunu düşünüyorum, bu nedenle bir kaynak birçok yönden elde edilebilir: GET yoluyla, POST'un cevabı olarak, PUT'nin cevabı olarak. Müşterinin potansiyel olarak farklı bir yapıya sahip 3 kaynak elde ettiği anlamına gelir . Sanki beden olmadan sadece URI (konum) döndürürseniz, o zaman bir kaynak elde etmenin tek yolu GET olacaktır. Bu, müşterinin her zaman tutarlı yanıtlar almasını sağlar.
mentallurg

@mentallurg Bu hakkı ifade etmemiş olabileceğimin farkındayım. Gösterdiğin için teşekkürler. Cevabımı düzenledim. Bu daha mantıklı olursa bana bildirin.
ksprashu

Ev işiniz için bir hizmet uyguladığınız sürece, bu gerçekten önemli değil. İstediğin gibi yap. Zaman kazanın.
mentallurg

9

Link RFC standartlarına referans olarak, Post kullanarak istek kaynağını başarıyla depolamak için 201 (yaratılmış) durumu döndürmelisiniz. Uygulamaların çoğunda, kaynağın kimliği sunucunun kendisi tarafından üretilir, bu nedenle oluşturulan kaynağın kimliğini döndürmek iyi bir uygulamadır. Tüm nesneyi döndürmek, Post isteği için ek yüküdür. İdeal uygulama, yeni oluşturulan kaynağın URL konumunu döndürmektir .

Örneğin, Çalışan Nesnesini veritabanına kaydeden ve yeni oluşturulan kaynak nesnesinin URL'sini yanıt olarak veren aşağıdaki örneğe başvurabilirsiniz.

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

Bu dinlenme bitiş noktası yanıtı şu şekilde döndürür:

Durum 201 - OLUŞTURULAN

Üstbilgi Konumu → http: // localhost: 8080 / çalışan / 1


Güzel ve temiz - bunu şimdi ve ötesi takip edecek
Hassan Tareq

0

Geri dönüş gövdesinde bir HTTP parametresine bağlı bir yük oluşturabilirim.

Gereksiz yere dolaşmayı önlemek için bir tür içeriğin tekrar API tüketicisine geri gönderilmesinden daha iyidir (GraphQL'in var olmasının sebeplerinden biri).

Nitekim, uygulamalarımız daha yoğun ve dağıtılmış hale geldikçe, bu kılavuza uymaya çalışıyorum:

Rehberim :

Herhangi bir POST veya PUT'dan hemen sonra bir GET talep eden bir kullanım vakası olduğu zaman, POST / PUT yanıtında bir şeyi döndürmenin en iyi olabileceği durumdur.

Bu nasıl yapılır ve ne tür bir PUT veya POST'tan geri döndürülecek, uygulamaya özgüdür. Şimdi, uygulamanın yanıt gövdesindeki "içerik" türünü parametreleştirmesi ilginç olurdu (yalnızca yeni nesnenin konumunu mı, alanların bir kısmını mı yoksa bütün nesneyi mi istiyoruz?)

Bir uygulama, yanıt gövdesine geri dönmek üzere "içerik" türünü kontrol etmek için bir POST / PUT'un alabileceği bir dizi parametre tanımlayabilir. Veya bir çeşit GraphQL sorgusunu bir parametre olarak kodlayabilir (bunun yararlı olduğunu ancak bakım kabusu haline geldiğini de görebilirim.)

Her iki şekilde de bana öyle geliyor ki:

  1. Bir POST / PUT yanıt gövdesinde bir şey döndürmek iyidir (ve en çok arzu edilen).
  2. Bunun nasıl yapıldığı uygulamaya özel ve genellemesi neredeyse imkansız.
  3. Varsayılan olarak büyük boyutlu "bağlam" ı geri döndürmek istemezsiniz (POST tarafından takip edilen GET'lerden uzaklaşmanın tüm nedenini ortadan kaldıran trafik gürültüsü).

Öyleyse, 1) yapın, ama 2) basit tutun.

Gördüğüm bir diğer seçenek ise, alternatif son noktalar yaratan insanlar (örneğin, POST / PUT için vücutta hiçbir şey döndürmeyen ve / customer_with_detaylarla / POUT / PUT için / müşterilere / müşterilere hiçbir şey döndürmeyen, ancak yanıt gövdesinde bir şey döndüren).

Yine de bu yaklaşımdan kaçınırdım. Yasal olarak farklı türde içerikler döndürmeniz gerektiğinde ne olur? İçerik türü başına bir son nokta? Ölçeklenebilir veya bakımı yapılamaz.

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.