RESTful arama / filtreleme nasıl tasarlanır? [kapalı]


457

Şu anda PHP'de RESTful API tasarlayıp uyguluyorum. Ancak, ilk tasarımımı uygulamada başarısız oldum.

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1

Şimdiye kadar oldukça standart, değil mi?

Benim sorunum birincisi ile ilgili GET /users. Listeyi filtrelemek için istek gövdesinde parametreler göndermeyi düşünüyordum. Bunun nedeni, süper uzun bir url almadan karmaşık filtreler belirtmek istiyorum, çünkü:

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4

Bunun yerine şöyle bir şey istedim:

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

çok daha okunabilir ve karmaşık filtreler ayarlamak için size büyük olanaklar sunuyor.

Her neyse, istekler file_get_contents('php://input')için istek gövdesini döndürmedi GET. Ben de denedim http_get_request_body(), ama kullandığım paylaşılan barındırma yok pecl_http. Yine de yardımcı olurdu emin değilim.

Bu soruyu buldum ve GET'in muhtemelen bir talep gövdesine sahip olmaması gerektiğini fark ettim. Biraz sonuçsuz, ama buna karşı tavsiye.

Şimdi ne yapacağımdan emin değilim. RESTful arama / filtreleme işlevini nasıl tasarlarsınız?

Sanırım kullanabiliyorum POST, ama bu çok RESTful gibi görünmüyor.



60
Dikkatli ol!!! GET yöntemi IDEMPOTENT olmalı ve "önbelleğe alınabilir" olmalıdır. Gövdeye bilgi gönderirseniz Sistem isteğinizi nasıl önbelleğe alabilir? HTTP, GET isteğinin istek gövdesini değil, yalnızca URL'yi kullanarak önbelleğe alınmasına izin verir. Örneğin, bu iki istek: example.com {test: "bazı"} example.com {anotherTest: "some2"} önbellek sistemi tarafından aynı kabul edilir: Her ikisi de tam olarak aynı URL'ye sahiptir
jfcorugedo

15
Sadece eklemek için, / user (single user) yerine / users (koleksiyon) 'a POST yapmalısınız.
Mladen B.

1
Dikkate alınması gereken başka bir nokta, çoğu uygulama sunucusunun URL'yi günlüğe kaydeden erişim günlüklerine sahip olmasıdır ve bu nedenle aradaki herhangi bir şey olabilir. Bu yüzden GET'te amaçlanmamış bilgi sızıntısı olabilir.
user3206144

Yanıtlar:


396

RESTful arama yapmanın en iyi yolu, aramanın kendisini bir kaynak olarak değerlendirmektir. Ardından, bir arama oluşturduğunuz için POST fiilini kullanabilirsiniz. Bir POST kullanmak için veritabanında bir şey oluşturmanız gerekmez.

Örneğin:

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}

Kullanıcının bakış açısından bir arama oluşturuyorsunuz. Bunun uygulama ayrıntıları önemsizdir. Bazı RESTful API'lerin kalıcılığa ihtiyacı bile olmayabilir. Bu bir uygulama detayı.


209
Bir arama uç noktası için POST isteği kullanmanın önemli bir sınırlaması, yer imi eklenememesidir. Arama sonuçlarını (özellikle karmaşık sorgular) yer imlerine eklemek oldukça faydalı olabilir.
couchand

73
Arama yapmak için POST kullanmak REST önbellek kısıtlamasını bozabilir. whatisrest.com/rest_constraints/cache_excerps
Filipe

56
Aramalar, doğası gereği, geçici: veriler aynı parametrelerle iki arama arasında gelişir, bu nedenle bir GET isteğinin arama modeliyle temiz eşleşmediğini düşünüyorum. Bunun yerine, arama isteği POST (/ Kaynak / arama) olmalıdır, o zaman bu aramayı kaydedebilir ve bir arama sonucuna, örneğin / Kaynak / arama / iyn3zrt gibi yönlendirebilirsiniz. Bu şekilde, GET istekleri başarılı olur ve anlamlı olur.
sleblanc

32
Gönderinin arama için uygun bir yöntem olduğunu düşünmüyorum, normal GET isteklerine ilişkin veriler de zaman içinde değişebilir.
merak

82
Bu kesinlikle mümkün olan en kötü cevaptır. Çok fazla oyu olduğuna inanamıyorum. Bu cevabın nedenini açıklıyor: programmers.stackexchange.com/questions/233164/…
richard

141

İstek gövdesini bir GET isteğinde kullanırsanız, önbellek sistemi yalnızca URL'yi kullandığından GET isteğiniz önbelleğe alınamayacağı için REST ilkesini ihlal edersiniz.

Ve daha da kötüsü, URL, kullanıcıyı bu sayfaya yönlendirmek için gereken tüm bilgileri içermediğinden URL'niz yer işareti eklenemez

İstek gövdesi parametreleri yerine URL veya Sorgu parametreleri kullanın.

Örneğin:

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 

Aslında, HTTP RFC 7231 şunları söylüyor:

Bir GET istek mesajındaki bir yükün tanımlanmış bir anlamı yoktur; bir GET isteğine bir yük gövdesi göndermek, mevcut bazı uygulamaların isteği reddetmesine neden olabilir.

Daha fazla bilgi için buraya bakınız.


29
Benim hatamdan öğrenin - Kabul edilen yanıtın önerisini (POSTing json) kullanarak bir API tasarladım, ancak url parametrelerine geçiyorum. Yer imi yeteneği düşündüğünüzden daha önemli olabilir. Benim durumumda, trafiği belirli arama sorgularına (reklam kampanyası) yönlendirmeye ihtiyaç vardı. Ayrıca, geçmiş API'sını kullanmak URL parametreleriyle daha anlamlı olur.
Jake

2
Nasıl kullanıldığına bağlıdır. Sayfayı bu parametrelere göre yükleyen bir URL'ye bağlanıyorsanız, mantıklıdır, ancak ana sayfa yalnızca filtre parametrelerine dayalı verileri almak için bir AJAX çağrısı yapıyorsa, ajax çağrısı ve hiçbir etkisi yoktur. Doğal olarak, oraya gittiğinizde bir filtre ve POX'ların ajax çağrısına gönderdiği ve sadece iyi çalışacağı bir URL'ye de yer işareti koyabilirsiniz.
Daniel Lorenz

@DanielLorenz En iyi kullanıcı deneyimi için, URL yine de bu durumda Geçmiş API'sı aracılığıyla değiştirilmelidir. Bir web sitesi önceki sayfalara gitmek için tarayıcı geri işlevini kullanmaya izin vermediğinde duramam. Standart sunucu tarafında oluşturulan bir sayfa ise, yer imi koymanın tek yolu bir GET isteği kullanmak olacaktır. İyi ol 'sorgu parametreleri en iyi çözüm gibi görünüyor.
Nathan

@Nathan Sanırım bu cevabı yanlış okudum. Bir get sorgu dize parametreleri kullanma hakkında konuşuyordu. Bir GET çağrısında asla gövde parametrelerini kullanmamalısınız, çünkü bu tamamen işe yaramaz olacaktır. Ben sorgu dizesi kullanılabilir / yer imi ile bir GET hakkında daha fazla konuşuyordum ve daha sonra sayfanın başlangıcında, veri almak için bu parametreleri kullanarak POST için bir filtre oluşturmak için bu parametreleri kullanabilirsiniz. Bu senaryoda tarih hala iyi çalışır.
Daniel Lorenz

@DanielLorenz Ah tamam mantıklı. Sanırım ne dediğini yanlış anladım.
Nathan

70

Kaynak filtreleme / arama RESTful bir şekilde uygulanabilir görünüyor. Fikir, /filters/veya adlı yeni bir son nokta getirmektir /api/filters/.

Bu uç nokta filtresinin kullanılması bir kaynak olarak kabul edilebilir ve bu nedenle POSTyöntemle oluşturulabilir . Bu şekilde - elbette - vücut tüm parametreleri taşımak için kullanılabilir ve karmaşık arama / filtre yapıları oluşturulabilir.

Böyle bir filtre oluşturduktan sonra, arama / filtre sonucunu elde etmek için iki olasılık vardır.

  1. 201 CreatedDurum kodu ile birlikte benzersiz kimliğe sahip yeni bir kaynak döndürülür . Daha sonra bu kimliği kullanarak GETaşağıdakilerden /api/users/hoşlanabilirsiniz:

    GET /api/users/?filterId=1234-abcd
    
  2. Yeni filtre aracılığıyla oluşturulduktan sonra POSTonunla cevap vermez 201 Createdama bir kez ile en 303 SeeOtherberaber Locationbaşlık işaret etmek /api/users/?filterId=1234-abcd. Bu yönlendirme, temel kütüphane aracılığıyla otomatik olarak ele alınacaktır.

Her iki senaryoda da, filtrelenmiş sonuçları elde etmek için iki istekte bulunulması gerekir - bu, özellikle mobil uygulamalar için bir dezavantaj olarak düşünülebilir. Mobil uygulamalar için tek POSTaramayı kullanırdım /api/users/filter/.

Oluşturulan filtreler nasıl saklanır?

DB'de saklanabilir ve daha sonra kullanılabilirler. Ayrıca, redis gibi bazı geçici depolarda da saklanabilirler ve bir miktar TTL'ye sahip olabilirler;

Bu fikrin avantajları nelerdir?

Filtreler, filtrelenen sonuçlar önbelleğe alınabilir ve hatta yer imi bile eklenebilir.


2
Peki bu kabul edilen cevap olmalı. REST ilkelerini ihlal etmezsiniz ve kaynaklarla ilgili uzun karmaşık sorgular yapabilirsiniz. Güzel, temiz ve yer imiyle uyumlu. Tek ek dezavantaj, oluşturulan filtreler için anahtar / değer çiftlerini saklama gereksinimi ve daha önce belirtilen iki istek adımıdır.
dantebarba

2
Bu yaklaşımla ilgili tek endişe, sorguda (veya sürekli değişen bir değer) tarih-saat filtreleriniz varsa. Daha sonra db (veya önbellek) içinde saklanacak filtre sayısı sayısızdır.
Rvy Pandey

17

Bence istek parametreleri ile gitmek gerekir ama sadece ne yapmak istediğinizi gerçekleştirmek için uygun bir HTTP üstbilgisi olmadığı sürece. HTTP şartname açıkça o GET bir gövdesi olamaz, söylemez. Ancak bu makalede şunlar yazılıdır:

Kural olarak, GET yöntemi kullanıldığında, kaynağı tanımlamak için gereken tüm bilgiler URI'de kodlanır. HTTP / 1.1'de, istemcinin URI'nin sorgu bölümü yerine bir HTTP varlık gövdesinde sunucuya veri sağladığı güvenli bir etkileşim (örneğin, geri alma) için herhangi bir kural yoktur. Bu güvenli operasyonlar için URI'lerin uzun olabileceği anlamına gelir.


6
ElastikArama ayrıca vücut ile GET yapar ve iyi çalışır!
Tarun Sapra

Evet ama onlar sunucu uygulaması kontrol interwebs üzerinde xase olmayabilir.
user432024

7

Ben laravel / php arka uç kullanıyorum gibi ben böyle bir şey ile gitmek eğilimindedir:

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

PHP otomatik olarak []bir dizi haline params döner , bu yüzden bu örnekte ben $filterbir sayfa ve istekli yüklenmesini istediğiniz herhangi bir ilgili kaynak ile birlikte bir dizi filtre / nesne tutan bir değişken ile sonuçlanır.

Başka bir dil kullanırsanız, bu yine de iyi bir kural olabilir []ve bir diziye dönüştürmek için ayrıştırıcı oluşturabilirsiniz .


Bu yaklaşım hoş görünüyor, ancak URL'lerde köşeli parantez kullanma ile ilgili sorunlar olabilir, url'de hangi karakterlerin kullanılabileceğini görün
Sky

2
@Sky Bu ve [ve kodlayan URI tarafından önlenebilir ]. Sorgu parametrelerini gruplamak için bu karakterlerin kodlanmış gösterimlerini kullanmak iyi bilinen bir uygulamadır. JSON: API spesifikasyonunda bile kullanılır .
jelhan

6

İlk API'niz tamamen RESTful ise veya çok fazla endişelenmeyin (özellikle sadece alfa aşamalarında olduğunuzda). Önce arka uç tesisatını çalıştırın. Her şeyi eşleştirmek için her zaman bir tür URL dönüşümü / yeniden yazma yapabilirsiniz, yaygın testler ("beta") için yeterince kararlı bir şey elde edene kadar tekrarlayan bir şekilde iyileştirebilirsiniz.

Parametreleri her zaman bir şeyle eşleyeceğinizi bildiğiniz bir yolun önüne URI'lerin kendileri üzerinde konum ve kural ile kodlanan URI'leri tanımlayabilirsiniz. PHP bilmiyorum, ama böyle bir tesis var (varsa web çerçeveleri ile diğer dillerde var) varsayalım:

.ie. 1 numaralı mağazada i = 1..4 için param [i] = değer [i] ile bir "kullanıcı" arama türü yapın (URI sorgu parametreleri için kısayol olarak değer1, değer2, değer3, ... ile):

1) GET /store1/search/user/value1,value2,value3,value4

veya

2) GET /store1/search/user,value1,value2,value3,value4

veya aşağıdaki gibi (bunu tavsiye etmem olsa da, daha sonra)

3) GET /search/store1,user,value1,value2,value3,value4

Seçenek 1 ile, öneki olan tüm URI'leri /store1/search/userstore1 (eşdeğeri olan) için kaynak araması yapmak üzere varsayılan olarak arama işleyicisine (veya PHP atamalarından hangisine) eşlersiniz /search?location=store1&type=user.

API tarafından belgelenen ve uygulanan kural ile, 1 ila 4 arasındaki parametre değerleri virgülle ayrılır ve bu sırayla sunulur.

Seçenek 2, arama tipini (bu örnekte user) konumsal parametre # 1 olarak ekler . Her iki seçenek de sadece kozmetik bir seçimdir.

Seçenek 3 de mümkündür, ancak hoşuma gittiğini sanmıyorum. Belirli kaynakların içinde arama yeteneğinin, aramanın kendisinden önceki URI'de sunulması gerektiğini düşünüyorum (sanki URI'de aramanın kaynak içinde spesifik olduğunu açıkça gösteriyormuş gibi).

URI üzerinde bu parametrelerin geçmesinin avantajı, aramanın URI'nin bir parçası olmasıdır (böylece bir aramayı bir kaynak olarak, içeriği zamanla değişebilen ve değişecek olan bir kaynak olarak ele alır). Dezavantajı parametre sırasının zorunlu olmasıdır. .

Böyle bir şey yaptıktan sonra, GET'i kullanabilirsiniz ve salt okunur bir kaynak olacaktır (POST veya PUT yapamadığınız için - GET'lendiğinde güncellenir). Ayrıca, yalnızca çağrıldığında var olan bir kaynak olacaktır.

Ayrıca, sonuçları bir süre önbelleğe alarak veya önbelleğin silinmesine neden olan bir DELETE ile daha fazla anlambilim eklenebilir. Ancak bu, insanların genellikle DELETE için kullandıkları şeye ters düşebilir (ve insanlar genellikle önbelleğe alma üstbilgileriyle önbelleğe almayı denetlediğinden).

Nasıl başlayacağınız bir tasarım kararı olurdu, ama bu benim yolum olurdu. Mükemmel değil ve eminim bunu yapmanın en iyi şey olmadığı durumlar olacaktır (özellikle çok karmaşık arama kriterleri için).


7
Yo, (birisi, her kim / ne olursa olsun) cevabımı küçümsemek için bir şey uygunsa, en azından tam olarak neye katılmadığınızı belirten bir yorum yapmak için egoya zarar verir mi? Ben interweebz biliyorum, ama ...;)
luis.espinal

107
Aşağı inmedim, ancak sorunun şu anda başlaması gerçeği: "Şu anda RESTful API tasarlıyorum ve uyguluyorum" ve cevabınız "İlk API'niz tamamen RESTful ise ya da değil ise çok fazla endişelenme" ile başlıyor benim için yanlış. Bir API tasarlıyorsanız bir API tasarlıyorsunuz. Soru, API'nın tasarlanıp tasarlanmayacağı değil, API'nın en iyi nasıl tasarlanacağını sormaktır.
gardarh

14
API olduğu ilk API sistem, iş, değil arka uç sıhhi tesisat, ilk uygulama / sadece sahte olabilir ve olmalıdır. HTTP, parametreleri iletmek için bir mekanizmaya sahiptir, yeniden keşfedilmesini öneririz, ancak daha kötüsü (ad değeri çiftleri yerine sıralı parametreler). Bu yüzden aşağı oy.
Steven Herod

14
@gardarh - evet, yanlış geliyor, ama bazen pragmatik. Birincil hedef, eldeki iş bağlamı için çalışan bir API tasarlamaktır. Tamamen RESTFULL bir yaklaşım elinizdeki iş için uygunsa, o zaman devam edin. Değilse, o zaman gitmeyin. Yani, belirli işletme gereksinimlerinizi karşılayan bir API tasarlayın. Temel gereksinimi olarak RESTfull yapmaya çalışırken, "X / Y probleminde adaptör modelini nasıl kullanırım" diye sormaktan çok farklı değildir. Gerçek, değerli sorunları çözmedikleri sürece ayakkabı boynuz paradigmalarını kullanmayın.
luis.espinal

1
Bir kaynağı bir durum koleksiyonu ve parametrelerin bu durumun gösterimini parametrik olarak değiştirmek için bir araç olarak görüyorum. Bu şekilde düşünün, eğer kaynağın nasıl görüntüleneceğini ayarlamak için topuzlar ve anahtarlar kullanabilseydiniz (belirli bitlerini göster / gizle, farklı sırala, vs ...) bu kontroller params. Aslında farklı bir kaynaksa (örneğin, '/ albums' vs '/ artist'), o zaman yolda temsil edilmelidir. Zaten benim için sezgisel olan bu.
Eric Elliott

2

FYI: Bunun biraz geç olduğunu biliyorum ama ilgilenen herkes için. Ne kadar RESTful olmak istediğinize bağlı olarak, HTTP spesifikasyonu bu konuda çok net olmadığı için kendi filtreleme stratejilerinizi uygulamanız gerekecektir. URL'nin tüm filtre parametrelerini kodlamasını önermek istiyorum.

GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2

Çirkin olduğunu biliyorum ama bunu yapmanın en RESTful yolu olduğunu düşünüyorum ve sunucu tarafında ayrıştırmak kolay olmalı :)

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.