Impaginazione in un’applicazione Web REST

Questa è una riformulazione più generica di questa domanda (con l’eliminazione delle parti specifiche di Rails)

Non sono sicuro di come implementare l’impaginazione su una risorsa in un’applicazione web RESTful. Supponendo che disponga di una risorsa denominata products , quale dei seguenti ritiene sia l’approccio migliore e perché:

1. Utilizzando solo stringhe di query

per esempio. http://application/products?page=2&sort_by=date&sort_how=asc
Il problema qui è che non posso usare il caching a piena pagina e anche l’URL non è molto pulito e facile da ricordare.

2. Utilizzo di pagine come risorse e stringhe di query per l’ordinamento

per esempio. http://application/products/page/2?sort_by=date&sort_how=asc
In questo caso, il problema che si vede è che http://application/products/pages/1 non è una risorsa univoca dal momento che l’uso sort_by=price può produrre un risultato completamente diverso e non riesco ancora a utilizzare la memorizzazione nella cache della pagina.

3. Utilizzo di pagine come risorse e un segmento di URL per l’ordinamento

per esempio. http://application/products/by-date/page/2
Personalmente non vedo alcun problema nell’usare questo metodo, ma qualcuno mi ha avvertito che questo non è un buon modo per andare (non ha dato un motivo, quindi se sai perché non è raccomandato, per favore fatemelo sapere)

Qualsiasi suggerimento, opinione, critica è più che benvenuto. Grazie.

Penso che il problema con la versione 3 sia più un problema di “punto di vista”: vedi la pagina come risorsa o i prodotti sulla pagina.

Se vedi la pagina come risorsa è una soluzione perfetta, dal momento che la query per la pagina 2 restituirà sempre la pagina 2.

Ma se vedi i prodotti nella pagina come risorsa hai il problema che i prodotti a pagina 2 potrebbero cambiare (vecchi prodotti cancellati, o qualsiasi altra cosa), in questo caso l’URI non restituisce sempre le stesse risorse.

Ad esempio, un cliente memorizza un collegamento alla pagina X dell’elenco dei prodotti, la volta successiva che il collegamento viene aperto il prodotto in questione potrebbe non essere più sulla pagina X.

Sono d’accordo con Fionn, ma farò un passo avanti e dirò che la Pagina non è una risorsa, è una proprietà della richiesta. Questo mi fa scegliere solo la stringa di query dell’opzione 1. Sembra giusto. Mi piace molto il modo in cui l’ API di Twitter è strutturata in modo riposante. Non troppo semplice, non troppo complicato, ben documentato. Nel bene o nel male, è il mio design di “andare a” quando sono alla deriva nel fare qualcosa in un modo o in un altro.

HTTP ha un grande header Range che è adatto anche per l’impaginazione. Puoi inviare

 Range: pages=1 

avere solo la prima pagina. Questo potrebbe costringerti a ripensare a cos’è una pagina. Forse il cliente vuole una diversa gamma di articoli. L’intestazione intervallo funziona anche per dichiarare un ordine:

 Range: products-by-date=2009_03_27- 

per ottenere tutti i prodotti più recenti rispetto a quella data o

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

per ottenere tutti i prodotti più vecchi di quella data. ‘0’ probabilmente non è la soluzione migliore, ma RFC sembra voler qualcosa per l’inizio della gamma. Potrebbero essere distribuiti parser HTTP che non analizzeranno unità = -range_end.

Se le intestazioni non sono un’opzione (accettabile), ritengo che la prima soluzione (tutta in stringa di query) sia un modo per gestire le pagine. Ma per favore normalizza le stringhe di query (ordinamento (chiave = valore) in ordine alfabetico). Questo risolve “? A = 1 & b = x” e “? B = x & a = 1” problema di differenziazione.

L’opzione 1 sembra la migliore, nella misura in cui l’applicazione visualizza l’impaginazione come tecnica per produrre una vista diversa della stessa risorsa.

Detto questo, lo schema URL è relativamente insignificante. Se stai progettando la tua applicazione in modo ipertestuale (poiché tutte le applicazioni REST devono essere per definizione), il tuo cliente non costruirà alcun URI da solo. Invece, la tua applicazione fornirà i link al cliente e il cliente li seguirà.

Un tipo di collegamento che il tuo cliente può fornire è un link di paginazione.

Il piacevole effetto collaterale di tutto questo è che anche se cambi idea sulla struttura dell’URI di impaginazione e realizzi qualcosa di completamente diverso la prossima settimana, i tuoi clienti possono continuare a lavorare senza alcuna modifica.

Ho sempre usato lo stile dell’opzione 1. Il caching non è stato un problema dal momento che i dati cambiano spesso nel mio caso. Se si consente di configurare la dimensione della pagina, i dati non possono essere memorizzati nella cache.

Non trovo l’urlo difficile da ricordare o sporco. Per me questo è un buon uso dei parametri di query. La risorsa è chiaramente un elenco di prodotti e i parametri della query indicano semplicemente come si desidera visualizzare l’elenco, in ordine e in quale pagina.

Strano che nessuno abbia sottolineato che l’Opzione 3 ha parametri in un ordine specifico. http // application / products / Date / Descending / Name / Crescente / page / 2 e http // application / products / Name / Crescente / Data / Decrescente / pagina / 2

stanno puntando alla stessa risorsa, ma hanno URL completamente diversi.

Per me l’opzione 1 sembra la più accettabile, dal momento che separa chiaramente “Quello che voglio” e “Come lo voglio” (Ha persino un punto interrogativo tra di loro lol). La memorizzazione nella cache a pagina intera può essere implementata utilizzando l’URL completo (tutte le opzioni risentiranno comunque dello stesso problema).

Con l’approccio Parameters-in-URL, l’unico vantaggio è l’URL pulito. Anche se devi inventare un modo per codificare i parametri e decodificarli senza perdita di tempo. Ovviamente puoi usare URLencode / decode, ma renderà gli URL ancora più brutti 🙂

Preferirei usare i parametri di ricerca offset e limite.

offset : per l’indice dell’articolo nella raccolta.

limite : per il conteggio degli oggetti.

Il cliente può semplicemente continuare ad aggiornare l’offset come segue

 offset = offset + limit 

per la prossima pagina.

Il percorso è considerato l’identificativo della risorsa. E una pagina non è una risorsa ma un sottoinsieme della raccolta di risorse. Poiché la paginazione è generalmente una richiesta GET, i parametri di query sono più adatti per l’impaginazione piuttosto che per le intestazioni.

Io uso metamug . Hanno questo configurabile. Impaginazione su metamug di query selezionate

Attualmente sto utilizzando uno schema simile a questo nelle mie app ASP.NET MVC:

ad esempio http://application/products/by-date/page/2

in particolare è: http://application/products/Date/Ascending/3

Tuttavia, non sono molto contento di includere le informazioni di paging e di smistamento nel percorso in questo modo.

L’elenco degli articoli (prodotti in questo caso) è modificabile. cioè la prossima volta che qualcuno ritorna a un url che include i parametri di paging e di ordinamento, i risultati che ottengono potrebbero essere cambiati. Quindi l’idea di http://application/products/Date/Ascending/3 come un URL univoco che punta a un insieme definito e invariato di prodotti viene persa.

Cercando le migliori pratiche mi sono imbattuto in questo sito:

http://www.restapitutorial.com

Nella pagina delle risorse c’è un link per scaricare un file .pdf che contiene le best practice REST complete suggerite dall’autore. In cui tra le altre cose c’è una sezione sull’impaginazione.

L’autore suggerisce di aggiungere il supporto a entrambi utilizzando un’intestazione Range e utilizzando parametri di stringa di query.

Richiesta

Esempio di intestazione HTTP:

 Range: items=0-24 

Esempio di parametri della stringa di query:

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

Dove offset è il numero dell’articolo iniziale e il limite è il numero massimo di articoli da restituire.

Risposta

La risposta dovrebbe includere un’intestazione Content-Range che indica quanti oggetti vengono restituiti e quanti elementi totali esistono ancora da recuperare

Esempi di intestazione HTTP:

 Content-Range: items 0-24/66 Content-Range: items 40-65/* 

Nel .pdf ci sono altri suggerimenti per casi più specifici.

Tendo ad essere d’accordo con SLF che la “pagina” non è realmente una risorsa. D’altra parte, l’opzione 3 è più pulita, più facile da leggere e può essere più facilmente indovinata dall’utente e persino digitata se necessario. Sono diviso tra le opzioni 1 e 3, ma non vedo alcun motivo per non utilizzare l’opzione 3.

Inoltre, mentre sono belli, uno svantaggio nell’usare parametri nascosti, come qualcuno ha menzionato, piuttosto che eseguire query su stringhe o segmenti di URL è che l’utente non può aggiungere un segnalibro o collegarsi direttamente a una determinata pagina. Questo può o non può essere un problema a seconda dell’applicazione, ma solo qualcosa di cui essere a conoscenza.

Ho già usato la soluzione 3 (scrivo molte app di django). E non penso che ci sia qualcosa di sbagliato in questo. È altrettanto generabile degli altri due (nel caso in cui sia necessario eseguire alcuni raschiamenti di massa o simili) e sembra più pulito. Inoltre, i tuoi utenti possono indovinare gli URL (se si tratta di una app pubblica), e alla gente piace essere in grado di andare direttamente dove vogliono, e l’url-indovinare ti dà potere.

Uso nei miei progetti i seguenti URL:

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

che significa – “dammi pagina la seconda pagina ordinata in ordine crescente per campo1 e poi decrescente per campo2”. O se ho bisogno di ancora più flessibilità, io uso:

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