risposta-alla-domanda-sullo-sviluppo-web-bd.com

Come progettare ricerca / filtro RESTful?

Attualmente sto progettando e implementando un'API RESTful in PHP. Tuttavia, non sono riuscito a implementare il mio progetto iniziale.

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

Finora piuttosto standard, giusto?

Il mio problema è con il primo GET /users. Stavo considerando l'invio di parametri nel corpo della richiesta per filtrare l'elenco. Questo perché voglio essere in grado di specificare filtri complessi senza ottenere un URL lungo, come:

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

Invece volevo qualcosa di simile:

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

che è molto più leggibile e offre grandi possibilità di impostare filtri complessi.

In ogni caso, file_get_contents('php://input') non ha restituito il corpo della richiesta per le richieste GET. Ho anche provato http_get_request_body(), ma l'hosting condiviso che sto usando non ha pecl_http. Non sono sicuro che avrebbe comunque aiutato.

Ho trovato questa domanda e ho capito che GET probabilmente non dovrebbe avere un corpo di richiesta. È stato un po 'inconcludente, ma hanno sconsigliato.

Quindi ora non sono sicuro di cosa fare. Come progettate una funzione di ricerca/filtraggio RESTful?

Suppongo che potrei usare POST, ma questo non sembra molto RESTful.

395
Erik B

Il modo migliore per implementare una ricerca RESTful è considerare la ricerca stessa come una risorsa. Quindi puoi usare il _ POST verbo perché stai creando una ricerca. Non è necessario creare letteralmente qualcosa in un database per utilizzare un POST.

Per esempio:

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

Stai creando una ricerca dal punto di vista dell'utente. I dettagli di implementazione di questo sono irrilevanti. Alcune API RESTful potrebbero non avere nemmeno bisogno di persistenza. Questo è un dettaglio di implementazione.

376
Jason Harrelson

Se si utilizza il corpo della richiesta in una richiesta GET, si interrompe il principio REST, poiché la richiesta GET non potrà essere memorizzata nella cache, poiché il sistema cache utilizza solo l'URL.

E quel che è peggio, il tuo URL non può essere aggiunto ai segnalibri, perché l'URL non contiene tutte le informazioni necessarie per reindirizzare l'utente a questa pagina

Utilizza i parametri URL o Query invece dei parametri del corpo della richiesta.

per esempio.:

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

In effetti, l'HTTP RFC 7231 afferma che:

Un payload all'interno di un messaggio di richiesta GET non ha una semantica definita; l'invio di un corpo del payload su una richiesta GET potrebbe causare il rifiuto di alcune implementazioni esistenti.

Per maggiori informazioni dai un'occhiata a qui

125
jfcorugedo

Sembra che il filtro/ricerca delle risorse possa essere implementato in modo RESTful. L'idea è di introdurre un nuovo endpoint chiamato /filters/ o /api/filters/.

L'utilizzo di questo filtro filtro può essere considerato come una risorsa e quindi creato tramite il metodo POST. In questo modo - ovviamente - il corpo può essere utilizzato per trasportare tutti i parametri così come possono essere create strutture di ricerca/filtro complesse.

Dopo aver creato tale filtro ci sono due possibilità per ottenere il risultato di ricerca/filtro.

  1. Una nuova risorsa con ID univoco verrà restituita insieme al codice di stato 201 Created. Quindi usando questo ID, una richiesta GET può essere fatta a /api/users/ come:

    GET /api/users/?filterId=1234-abcd
    
  2. Dopo aver creato il nuovo filtro tramite POST, non risponderà con 201 Created ma contemporaneamente con 303 SeeOther insieme all'intestazione Location che punta a /api/users/?filterId=1234-abcd. Questo reindirizzamento verrà gestito automaticamente tramite la libreria sottostante.

In entrambi gli scenari devono essere fatte due richieste per ottenere i risultati filtrati - questo può essere considerato un inconveniente, specialmente per le applicazioni mobili. Per le applicazioni mobili userei la singola POST chiamata a /api/users/filter/.

Come mantenere i filtri creati?

Possono essere archiviati in DB e utilizzati in seguito. Possono anche essere memorizzati in un archivio temporaneo, ad es. ridisegna e ha alcuni TTL dopo i quali scadranno e verranno rimossi.

Quali sono i vantaggi di questa idea?

I filtri, i risultati filtrati sono memorizzabili nella cache e possono anche essere aggiunti ai segnalibri.

55
Opal

Penso che dovresti andare con i parametri di richiesta ma solo fino a quando non c'è un'intestazione HTTP appropriata per realizzare ciò che vuoi fare. La specifica HTTP non dice esplicitamente che GET non può avere un corpo. Tuttavia questo documento afferma:

Per convenzione, quando viene utilizzato il metodo GET, tutte le informazioni necessarie per identificare la risorsa sono codificate nell'URI. Non esiste alcuna convenzione in HTTP/1.1 per un'interazione sicura (ad es. Recupero) in cui il client fornisce dati al server in un corpo di entità HTTP anziché nella parte di query di un URI. Ciò significa che per operazioni sicure, gli URI potrebbero essere lunghi.

14
Daff

Non preoccuparti troppo se la tua API iniziale è completamente RESTful o meno (specialmente quando ti trovi solo nelle fasi alfa). Fai in modo che l'impianto idraulico di back-end funzioni prima. Puoi sempre fare una sorta di trasformazione/riscrittura degli URL per mappare le cose, perfezionandole iterativamente fino a quando non ottieni qualcosa di abbastanza stabile per i test diffusi ("beta").

Puoi definire URI i cui parametri sono codificati in base alla posizione e alla convenzione sugli URI stessi, preceduti da un percorso che sai che mapperai sempre a qualcosa. Non conosco PHP, ma suppongo che esista una tale struttura (come esiste in altre lingue con i framework web):

.ie. Fai un tipo di ricerca "utente" con param [i] = valore [i] per i = 1..4 sul negozio # 1 (con valore1, valore2, valore3, ... come una scorciatoia per i parametri di query URI):

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

o

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

o come segue (anche se non lo consiglierei, ne parleremo più avanti)

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

Con l'opzione 1, si mappano tutti gli URI con prefisso /store1/search/user al gestore di ricerca (o qualunque sia la designazione PHP) per eseguire le ricerche di risorse in store1 (equivalente a /search?location=store1&type=user.

Per convenzione documentata e applicata dall'API, i valori dei parametri da 1 a 4 sono separati da virgole e presentati in tale ordine.

L'opzione 2 aggiunge il tipo di ricerca (in questo caso user) come parametro posizionale # 1. Qualsiasi opzione è solo una scelta cosmetica.

Anche l'opzione 3 è possibile, ma non penso che mi piacerebbe. Penso che la capacità di ricerca all'interno di determinate risorse dovrebbe essere presentata nell'URI stesso che precede la ricerca stessa (come se indicasse chiaramente nell'URI che la ricerca è specifica all'interno della risorsa).

Il vantaggio di questi parametri che passano sopra l'URI è che la ricerca fa parte dell'URI (trattando così una ricerca come una risorsa, una risorsa i cui contenuti possono cambiare e cambiano nel tempo.) Lo svantaggio è che l'ordine dei parametri è obbligatorio .

Una volta che fai qualcosa del genere, puoi utilizzare GET, e sarebbe una risorsa di sola lettura (dato che non puoi POST o PUT ad esso - viene aggiornato quando è GET). Sarebbe anche una risorsa che esiste solo quando viene invocata.

Si potrebbe anche aggiungere più semantica aggiungendo nella cache i risultati per un periodo di tempo o con DELETE che causa l'eliminazione della cache. Questo, tuttavia, potrebbe essere contrario a ciò che le persone usano tipicamente DELETE per (e perché le persone in genere controllano il caching con le intestazioni di cache).

Il modo in cui lo faresti sarebbe una decisione di progettazione, ma questo sarebbe il modo in cui farei. Non è perfetto, e sono sicuro che ci saranno casi in cui farlo non è la cosa migliore da fare (specialmente per criteri di ricerca molto complessi).

10
luis.espinal

Siccome sto usando un laravel/php backend tendo ad andare con qualcosa del genere:

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

PHP trasforma automaticamente i parametri [] in un array, quindi in questo esempio avrò una variabile $filter che contiene un array/oggetto di filtri, una pagina e tutte le risorse correlate che desidero caricate.

Se si utilizza un'altra lingua, questa potrebbe comunque essere una buona convenzione ed è possibile creare un parser per convertire [] in una matrice.

8
the-a-train

A proposito: so che è un po 'tardi ma per chiunque sia interessato. Dipende da come RESTful vuoi essere, dovrai implementare le tue strategie di filtraggio in quanto la specifica HTTP non è molto chiara su questo. Vorrei suggerire la codifica url di tutti i parametri del filtro, ad es.

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

So che è brutto, ma penso che sia il modo più RESTful di farlo e dovrebbe essere facile da analizzare dal lato server :)

2
shanks