Bir REST web uygulamasında sayfalandırma


329

Bu, bu sorunun daha genel bir yeniden formülasyonudur ( Rails'e özgü parçaların ortadan kaldırılmasıyla)

RESTful web uygulamasında bir kaynak sayfalandırma nasıl uygulanacağından emin değilim. Bir kaynağım olduğunu varsayarsak products, aşağıdakilerden hangisinin en iyi yaklaşım olduğunu düşünüyorsunuz ve neden:

1. Yalnızca sorgu dizelerini kullanma

Örneğin. http://application/products?page=2&sort_by=date&sort_how=asc
Burada sorun tam sayfa önbellek kullanamıyorum ve ayrıca URL çok temiz ve hatırlanması kolay değildir.

2. Sayfaları kaynak olarak ve sıralama için sorgu dizeleri olarak kullanma

Örneğin. http://application/products/page/2?sort_by=date&sort_how=asc
Bu durumda, gördüğünüz sorun, http://application/products/pages/1benzersiz bir kaynak olmadığıdır, çünkü kullanım sort_by=pricetamamen farklı bir sonuç verebilir ve hala sayfa önbelleği kullanamıyorum.

3. Sayfaları kaynak olarak ve sıralama için bir URL segmentini kullanma

Örneğin. http://application/products/by-date/page/2
Şahsen bu yöntemi kullanırken herhangi bir sorun görmüyorum, ancak biri beni bunun iyi bir yol olmadığı konusunda uyardı (bir sebep vermedi, bu yüzden neden önerilmediğini biliyorsanız, lütfen bana bildirin)

Herhangi bir öneri, görüş, eleştiri bekliyoruz. Teşekkürler.


34
Bu harika bir soru.
Iain Holder

7
Bonus soru: Kullanıcılar genellikle sayfa boyutlarını nasıl belirler?
Heiko Rupp

Matrix parametrelerini unutmayın w3.org/DesignIssues/MatrixURIs.html
CMCDragonkai

Yanıtlar:


66

Ben sürüm 3 ile ilgili sorun daha bir "bakış açısı" sorunu olduğunu düşünüyorum - sayfayı kaynak veya sayfadaki ürünler olarak görüyorsunuz.

Sayfayı kaynak olarak görürseniz, mükemmel bir çözümdür, çünkü sayfa 2 için sorgu her zaman sayfa 2'yi verecektir.

Ancak sayfadaki ürünleri kaynak olarak görüyorsanız, sayfa 2'deki ürünlerin değişebileceği (eski ürünler silindi veya herhangi bir şekilde) sorun yaşıyorsanız, bu durumda URI her zaman aynı kaynakları döndürmez.

Örneğin, bir müşteri ürün listesi sayfası X'e bir bağlantı saklar, bağlantı bir sonraki açışında söz konusu ürün artık X sayfasında olmayabilir.


6
Ama bir şeyi silerseniz, aynı URI'de başka bir şey olmamalıdır. X sayfasının tüm ürünlerini silerseniz - X sayfası hala geçerli olabilir ancak şimdi X + 1 sayfasındaki ürünleri içeriyorsa. Bu nedenle X sayfası için URI, "ürün kaynak görünümünde" görüyorsanız X + 1 sayfası için URI haline gelmiştir. ".
Fionn

1
> Sayfayı kaynak olarak görürseniz, mükemmel bir çözümdür, çünkü sayfa 2 sorgusu her zaman sayfa 2'yi verecektir. Hatta mantıklı mı? Aynı URL (2. sayfadan bahseden herhangi bir URL), kaynak olarak ne olursa olsun her zaman 2. sayfayı getirir.
temoto

2
Sayfayı kaynak olarak görmek, muhtemelen yeni bir sayfa oluşturmak için POST / foo / page özelliğini tanıtmalıdır, değil mi?
temoto

18
Yanıtınız sorunsuz bir şekilde "doğru çözüm 1'dir", ancak bunu belirtmez.
temoto

2
Zihnimde, sayfa kayan bir kavramdır ve temel alanla ilgili değildir. Bu nedenle kaynak olarak görülmemelidir. Yani akıcı olması, sayfa kavramının bağlamla değişmesi anlamında yüzer; API'nizin bir kullanıcısı, sayfa başına yalnızca 2 ürün tüketebilen bir mobil uygulama, diğeri ise tüm kahrolası listeyi tüketebilecek bir makine uygulaması olabilir. Kısacası, sayfa, temel alan varlığının (ürününün) "temsilidir" ve URL'nin bir parçası olarak dahil edilmemelidir; yalnızca sorgu parametresi olarak.
Kingz

106

Fionn'a katılıyorum, ama bir adım daha ileri gideceğim ve bana Sayfa'nın bir kaynak olmadığını , isteğin bir özelliği olduğunu söyleyeceğim . Bu beni yalnızca seçenek 1 sorgu dizesini seçti. Sadece doğru geliyor. Twitter API'nın nasıl yapılandırıldığını gerçekten çok seviyorum . Çok basit değil, çok karmaşık değil, iyi belgelenmiş. Daha iyisi ya da daha kötüsü, bir şeye karşı bir şekilde başka bir şey yapmaya karar verdiğimde "git" tasarımım.


28
+1: sorgu dizeleri birinci sınıf kaynak tanımlayıcıları değildir; sadece kaynağın sıralanması ve gruplandırılması için açıklığa kavuştururlar.
S.Lott

1
S.Lott @ isteği olan bir kaynak. "Birinci sınıf kaynaklar" olarak adlandırdığınız şey , tezinin 5.2.1.1 bölümündeki Fielding tarafından değerler olarak tanımlanır . Ayrıca, aynı bölümde, Fielding , bir kaynak örneği olarak bir kaynak kodu dosyasının En Son Revizyonunu verir . Bu nasıl bir kaynak olabilir ancak en son 10 ürün "ürün kaynağına yönelik talebin özellikleri" olabilir? Görüşünüzün daha pratik olduğunu anlıyorum, ancak daha az RESTful olduğunu düşünüyorum.
edsioufi

Yorumumun URL'ler üzerinde sorgu dizeleri kullanma seçeneğine katılmadığım anlamına gelmediğini unutmayın: @RichApodaca'nın cevabında belirttiği gibi, API hipermedusa dayalı olduğu sürece her ikisi de uygulanabilir çözümlerdir. Sadece Sayfanın bir REST bakış açısından bir kaynak olarak kabul edilmesi gerektiğine işaret ediyorum.
edsioufi

37

HTTP, sayfalandırma için de uygun olan harika Aralık başlığına sahiptir. Gönderebilirsiniz

Range: pages=1

sadece ilk sayfaya sahip olmak. Bu sizi bir sayfayı yeniden düşünmeye zorlayabilir. Belki müşteri farklı bir ürün yelpazesi ister. Aralık başlığı ayrıca bir sipariş beyan etmeye çalışır:

Range: products-by-date=2009_03_27-

tüm ürünleri bu tarihten daha yeni hale getirmek veya

Range: products-by-date=0-2009_11_30

tüm ürünleri bu tarihten daha eski hale getirmek için. '0' muhtemelen en iyi çözüm değildir, ancak RFC aralık başlangıcı için bir şeyler istiyor gibi görünmektedir. Dağıtılmış HTTP ayrıştırıcıları olabilir; bunlar ayrıştırılamaz = -range_end.

Üstbilgiler (kabul edilebilir) bir seçenek değilse, ilk çözüm (tüm sorgu dizesinde) sayfaları ile başa çıkmak için bir yol olduğunu düşünüyorum. Ancak lütfen sorgu dizelerini normalleştirin (sıralama (anahtar = değer) çiftlerini alfabe sırasına göre). Bu, "? A = 1 & b = x" ve "? B = x & a = 1" farklılaşma problemini çözer.


34
başlıklar ilk bakışta iyi görünebilir, ancak sayfayı paylaşmaya izin vermezler (ör. url'yi kopyalayarak). Yani ajax isteği için güzel bir çözüm olabilir (çünkü ajax tarafından değiştirilen sayfalar zaten mevcut durumlarında paylaşılamaz), ancak bunları normal sayfalandırma için kullanmam.
Markus

3
Ve Range başlığı yalnızca bayt aralıkları içindir. Bkz. [HTTP üstbilgileri spesifikasyonu] ( w3.org/Protocols/rfc2616/rfc2616-sec14.html ), bölüm 14.35.
Chris Westin

16
@ChrisWestin w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.12 HTTP / 1.1, Aralık (bölüm 14.35) ve İçerik Aralığı (bölüm 14.16) başlık alanlarında aralık birimleri kullanır. range-unit = bytes-unit | other-range-unit Belki de bu ifadenizle The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1 implementations MAY ignore ranges specified using other units.aynı şeyden bahsediyorsunuzdur .
temoto

1
@ Markus dinlenme api kaynağı paylaşırken kullanım durumunu hayal edemiyorum :)
JakubKnejzlik

@JakubKnejzlik Paylaşım bir sorun değildir, ancak disk belleği için HTTP üstbilgilerinin kullanılması disk belleği için HATEOAS bağlantılarının kullanılmasını engeller.
xarx

25

Seçenek 1, uygulamanızın sayfalandırmayı aynı kaynağın farklı bir görünümünü oluşturmak için bir teknik olarak gördüğü ölçüde en iyi şekilde görünür.

Bunu söyledikten sonra, URL şeması nispeten önemsizdir. Uygulamanızı köprü metnine dayalı olarak tasarlıyorsanız (tüm REST uygulamalarının tanım gereği olması gerektiği gibi), istemciniz kendi başına URI oluşturmayacaktır. Bunun yerine, uygulamanız istemciye bağlantılar verecek ve istemci bunları takip edecektir.

Müşterinizin sağlayabileceği bir bağlantı türü bir sayfalandırma bağlantısıdır.

Tüm bunların hoş yan etkisi, sayfalandırma URI yapısı hakkındaki fikrinizi değiştirseniz ve gelecek hafta tamamen farklı bir şey uygulasanız bile, müşterilerinizin herhangi bir değişiklik yapmadan çalışmaya devam edebilmesidir.


3
REST web hizmetlerinde hipermedya gibi bağlantılar kullanmanın güzel bir hatırlatıcısı.
Paul D. Eden

11

Her zaman seçenek 1'in stilini kullandım. Veri zaten benim durumumda sık sık değiştiği için önbellekleme endişe kaynağı olmamıştır. Sayfanın boyutunun yapılandırılabilir olmasına izin verirseniz, veriler tekrar önbelleğe alınamaz.

URL'yi hatırlamakta zorlanıyorum veya kirli değilim. Bana göre bu sorgu parametrelerinin güzel bir kullanımı. Kaynak açıkça bir ürün listesidir ve sorgu parametreleri sadece listenin nasıl görüntülenmesini istediğinizi sıralar ve hangi sayfayı anlatır.


1
+1 Bence haklısın ve sorgu parametreleriyle gideceğim (seçenek 1)
andi

"URL'yi hatırlamakta zorlanmıyorum". Bu gözlem REST uygulamalarında işe yaramaz, çünkü bunlar genellikle yalnızca tek bir yer işaretine sahip olmalıdır ... Bir kullanıcı (veya istemci uygulaması) URL'yi "hatırlamaya" çalışırsa, bu API'nın huzurlu olmadığının iyi bir işaretidir.
edsioufi

8

Hiç kimsenin Seçenek 3'ün belirli bir sırada parametreleri olduğunu işaret etmediği garip. http // uygulama / ürünler / Tarih / Azalan / Ad / Artan / sayfa / 2 ve http // uygulama / ürünler / Ad / Artan / Tarih / Azalan / sayfa / 2

aynı kaynağa işaret ediyor, ancak tamamen farklı URL'lere sahip.

Benim için Seçenek 1 en kabul edilebilir görünüyor, çünkü açıkça "Ne istiyorum" ve "Nasıl istiyorum" onu ayırır (hatta aralarında soru işareti vardır lol). Tam sayfa önbellekleme, tam URL kullanılarak uygulanabilir (Tüm seçenekler yine de aynı sorundan muzdarip olacaktır).

URL'deki Parametreler yaklaşımıyla tek fayda temiz URL'dir. Gerçi parametreleri kodlamak ve kayıpsız bir şekilde kodunu çözmek için bir yol bulmalısınız. Tabii URLencode / decode ile gidebilirsiniz, ancak yine url'ler çirkin hale getirecek :)


1
Bunlar iki farklı düzen. İlki tarihe göre azalarak sıralanır ve bağları yalnızca artan ada göre keser; ikincisi ada göre artan şekilde sıralar ve bağları yalnızca tarihe göre azalarak keser.
Imran Rashid

Aslında burada verilen iki örnek URL yalnızca yazılı olarak değil, aynı zamanda anlam bakımından da farklıdır. Bir yolu ifade ettiğinden, ilk önce ve sonra sola dönerken ya da tam tersi yönde aynı şeyi bulduğunuzun garantisi yoktur. Bunu söyledikten sonra, URL yolu parçaları olarak sıralama parametreleri, genel anlamı değiştirmeden değişmeli olarak değiştirilebilmesi gereken URL parametrelerine göre resmi avantajlara sahiptir, ancak aslında burada belirtildiği gibi tuzakları kodlamaktan muzdariptir.
Christian Gosch

7

Ofset ve limit sorgu parametrelerini kullanmayı tercih ederim.

offset : koleksiyondaki öğenin dizini için.

limit : öğe sayısı için.

İstemci ofseti aşağıdaki gibi güncellemeye devam edebilir

offset = offset + limit

sonraki sayfa için.

Yol, kaynak tanımlayıcısı olarak kabul edilir. Ve bir sayfa bir kaynak değil, kaynak koleksiyonunun bir alt kümesidir. Sayfalandırma genellikle bir GET isteği olduğundan, sorgu parametreleri başlıklardan ziyade sayfalandırma için en uygunudur.

Referans: https://metamug.com/article/rest-api-developers-dilemma.html#Requesting-the-next-page


5

Bu siteye rastladığım en iyi uygulamaları arıyorum:

http://www.restapitutorial.com

Kaynaklar sayfasında, yazarın önerdiği REST en iyi uygulamalarını içeren bir .pdf dosyası indirmek için bir bağlantı vardır. Diğer şeylerin yanı sıra sayfalandırma hakkında bir bölüm var.

Yazar, hem bir Range üstbilgisi hem de sorgu dizesi parametreleri kullanarak destek eklemenizi önerir.

İstek

HTTP üstbilgisi örneği:

Range: items=0-24

Sorgu dizesi parametreleri örneği:

GET http://api.example.com/resources?offset=0&limit=25

Nerede ofset başlayan madde numarası ve sınır dönmek için öğelerin sayısıdır.

Tepki

Yanıt, kaç öğenin döndürüldüğünü ve henüz alınmamış toplam öğenin olduğunu belirten bir İçerik Aralığı başlığı içermelidir

HTTP üstbilgisi örnekleri:

Content-Range: items 0-24/66

Content-Range: items 40-65/*

.Pdf dosyasında daha spesifik durumlar için başka öneriler de vardır.


4

Şu anda ASP.NET MVC uygulamalarımda buna benzer bir düzen kullanıyorum:

Örneğin http://application/products/by-date/page/2

özellikle: http://application/products/Date/Ascending/3

Ancak, rotadaki bilgileri bu şekilde sıralamak ve sıralamaktan gerçekten memnun değilim.

Öğe listesi (bu durumda ürünler) değiştirilebilir. örneğin, birisinin disk belleği ve sıralama parametrelerini içeren bir URL'ye bir sonraki dönüşünde, elde ettiği sonuçlar değişmiş olabilir. Böylece, http://application/products/Date/Ascending/3tanımlanmış, değişmeyen bir ürün grubuna işaret eden benzersiz bir url fikri kaybedilir.


1
İlk sayı, birden çok sütuna göre sıralama ile, bence 3 yöntem için de geçerlidir. Bu yüzden hiçbiri için gerçekten bir pro / con değil. İkinci sorunla ilgili olarak: bu herhangi bir kaynağa gerçekleşemez mi? Örneğin bir ürün de düzenlenebilir / silinebilir.
andi

Ben url sadece daha büyük ve daha yönetilemez alır gibi birden çok sütun sıralama gerçekten tüm 3 yöntem için 'con' olduğunu düşünüyorum - bu nedenle form tabanlı sayfa / sıralama parametreleri için taşıma düşünüyoruz. İkinci sayı için, geçici bir ürün listesinden ziyade ürün kimliği gibi benzersiz bir kalıcı tanımlayıcı arasında temel bir kavramsal fark olduğunu düşünüyorum. Silinen ürünler için, örneğin 'Bu ürün sistemde mevcut değildir' mesajı size bu ürün hakkında somut bir şey söyler.
Steve Willcock

1
Tüm disk belleği ve sıralama bilgilerini rotadan kaldırmak iyidir. Ve POST parametrelerine zorlamak kötüdür. Merhaba? Soru REST ile ilgili. POST'u yalnızca URL'yi REST'te kısaltmak için kullanmıyoruz. Fiil mantıklı.
temoto

1
Şahsen, neredeyse bir POST veya PUT HTTP yöntemi gerektireceği için (şimdi istekte bir gövde olduğundan) bir sorgu için form parametrelerini kullanmazdım. Hem POST hem de PUT, kaynağın değiştirilmesini ima ettiğinden, GET bana daha uygun bir yöntem gibi geliyor. Bu nedenle, birden çok sütuna göre sıralama gerektiğinde URL'ye daha fazla sorgu parametresi ekleyerek giderdim.
Paul D. Eden

1

Ben "sayfa" gerçekten bir kaynak olmadığını slf ile kabul eğilimindedir. Öte yandan, seçenek 3 daha temiz, okunması daha kolaydır ve kullanıcı tarafından daha kolay tahmin edilebilir ve hatta gerekirse yazılabilir. Seçenek 1 ve 3 arasında kaldım, ancak seçenek 3'ü kullanmamak için herhangi bir neden görmüyorum.

Ayrıca, hoş görünseler de, sorgu dizeleri veya URL segmentleri yerine gizli parametreleri kullanmanın bir dezavantajı, kullanıcının belirli bir sayfaya yer işareti koyamaması veya doğrudan bağlantı verememesidir. Bu, uygulamaya bağlı olarak bir sorun olabilir veya olmayabilir, ancak farkında olması gereken bir şey olabilir.


1
Tahmin edilmesi daha kolay olduğundan bahsettiğinizde bu önemli değil. Bir hiper ortam API'sı oluşturuyorsa, kullanıcılar URI'ları tahmin etmek zorunda kalmamalıdır.
JR Garcia

0

Daha önce çözüm 3'ü kullandım (bir sürü django uygulaması yazıyorum). Ve bunda yanlış bir şey olduğunu düşünmüyorum. Diğer ikisi kadar üretilebilir (eğer bir miktar kütle kazıma veya benzeri yapmanız gerekiyorsa) ve daha temiz görünüyor. Ayrıca, kullanıcılarınız URL'leri tahmin edebilir (eğer halka açık bir uygulama ise) ve insanlar doğrudan istedikleri yere gidebilirler ve URL tahminleri güçlenir.


0

Projelerimde aşağıdaki URL'leri kullanıyorum:

http://application/products?page=2&sort=+field1-field2

bunun anlamı - "bana alan1'e göre artan ve sonra alan2'ye göre azalan sıralı ikinci sayfayı ver". Veya daha fazla esnekliğe ihtiyacım olursa:

http://application/products?skip=20&limit=20&sort=+field1-field2

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.