Design URL RESTful per la ricerca

Sto cercando un modo ragionevole per rappresentare le ricerche come URL RESTful.

Il setup: ho due modelli, Cars e Garage, dove le auto possono essere in garage. Quindi i miei url sono:

/car/xxxx xxx == car id returns car with given id /garage/yyy yyy = garage id returns garage with given id 

Una macchina può esistere da sola (quindi la / macchina), oppure può esistere in un garage. Qual è il modo giusto per rappresentare, ad esempio, tutte le auto in un determinato garage? Qualcosa di simile a:

 /garage/yyy/cars ? 

Che ne dici dell’unione delle auto nel garage yyy e zzz?

Qual è il modo giusto per rappresentare una ricerca di auto con determinati attributi? Di ‘: mostrami tutte le berline blu con 4 porte:

 /car/search?color=blue&type=sedan&doors=4 

o dovrebbe essere / auto invece?

L’uso della “ricerca” sembra inappropriato lì – qual è il modo migliore / termine? Dovrebbe essere solo:

 /cars/?color=blue&type=sedan&doors=4 

I parametri di ricerca dovrebbero essere parte di PATHINFO o QUERYSTRING?

In breve, sto cercando una buona guida / esercitazione per la progettazione di URL REST cross-model e per la ricerca.

[Aggiornamento] Mi piace la risposta di Justin, ma non copre il caso di ricerca multi-campo:

 /cars/color:blue/type:sedan/doors:4 

o qualcosa di simile. Da dove partiamo?

 /cars/color/blue 

al caso di più campi?

    Per la ricerca, usa querystrings. Questo è perfettamente RESTful:

     /cars?color=blue&type=sedan&doors=4 

    Un vantaggio per i querystring regolari è che sono standard e ampiamente compresi e che possono essere generati dal form-get.

    Il design RESTful pretty URL riguarda la visualizzazione di una risorsa basata su una struttura (struttura simile a una directory, data: articoli / -id , object e attributi, …), la barra / indica la struttura gerarchica, usa l’- -id anziché.

    Struttura gerarchica

    Preferirei personalmente:

     /garage-id/cars/car-id /cars/car-id #for cars not in garages 

    Se un utente rimuove la parte /car-id , porta l’anteprima delle cars – intuitiva. L’utente sa esattamente dove si trova l’albero, cosa sta guardando. Sa fin dal primo sguardo che garage e auto sono in relazione. /car-id indica anche che appartiene insieme a differenza di /car/id .

    Ricerca

    La ricerca è OK così com’è , c’è solo la tua preferenza, ciò che dovrebbe essere preso in considerazione. La parte divertente arriva quando si uniscono le ricerche (vedi sotto).

     /cars?color=blue;type=sedan #most prefered by me /cars;color-blue+doors-4+type-sedan #looks good when using car-id /cars?color=blue&doors=4&type=sedan #I don't recommend using &* 

    O praticamente tutto ciò che non è una barra, come spiegato sopra.
    La formula: /cars[?;]color[=-:]blue[,;+&] , * sebbene non utilizzerei il segno & perché è irriconoscibile dal testo a prima vista.

    ** Sapevi che passare oggetti JSON nell’URI è RESTful? **

    Elenchi di opzioni

     /cars?color=black,blue,red;doors=3,5;type=sedan #most prefered by me /cars?color:black:blue:red;doors:3:5;type:sedan /cars?color(black,blue,red);doors(3,5);type(sedan) #does not look bad at all /cars?color:(black,blue,red);doors:(3,5);type:sedan #little difference 

    caratteristiche possibili?

    Annulla le stringhe di ricerca (!)
    Per cercare qualsiasi auto, ma non nera e rossa :
    ?color=!black,!red
    color:(!black,!red)

    Ricerche congiunte
    Cerca auto rosse o blu o nere con 3 porte nei garage id 1..20 o 101..103 o 999 ma non 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
    È quindi ansible build query di ricerca più complesse. (Guarda la corrispondenza dell’attributo CSS3 per l’idea di sottostringhe corrispondenti. Ad esempio, cerca gli utenti che contengono “bar” user*=bar .)

    Conclusione

    Ad ogni modo, questa potrebbe essere la parte più importante per te, perché puoi farlo comunque, dopo tutto, tieni a mente che l’URI RESTful rappresenta una struttura che può essere facilmente compresa ad esempio /directory/file /collection/node/item , date /articles/{year}/{month}/{day} .. E quando ometti uno degli ultimi segmenti, sai immediatamente cosa ottieni.

    Quindi .., tutti questi caratteri sono consentiti non codificati :

    • senza riserva: a-zA-Z0-9_.-~
    • riservato ;/?:@=&$-_.+!*'(),
    • non sicuro *: <>"#%{}|\^~[]`

    * Perché non sicuro e perché dovrebbe essere codificato: RFC 1738 vedi 2.2

    RFC 3986 vedi 2.2
    Nonostante ciò che ho detto in precedenza, qui c’è una distinzione comune dei delimitatori, il che significa che alcuni “sono” più importanti di altri.

    • delimitatori generici:: :/?#[]@
    • sub-delimeters:! !$&'()*+,;=

    Altre letture:
    Gerarchia: vedi 2.3 , vedi 1.2.3
    syntax del parametro path url
    Corrispondenza degli attributi CSS3
    IBM: servizi Web RESTful – Le basi
    Nota: RFC 1738 è stato aggiornato da RFC 3986

    Anche se avere i parametri nel percorso ha alcuni vantaggi, ci sono, IMO, alcuni fattori superanti.

    • Non tutti i caratteri necessari per una query di ricerca sono consentiti in un URL. La maggior parte dei caratteri di punteggiatura e Unicode dovrebbe essere codificata come parametro di stringa di query. Sto lottando con lo stesso problema. Vorrei usare XPath nell’URL, ma non tutta la syntax XPath è compatibile con un percorso URI. Quindi per percorsi semplici, /cars/doors/driver/lock/combination sarebbe opportuno individuare l’elemento ” combination ” nel documento XML della porta del guidatore. Ma /car/doors[id='driver' and lock/combination='1234'] non è così amichevole.

    • C’è una differenza tra il filtrare una risorsa basata su uno dei suoi attributi e specificare una risorsa.

      Ad esempio, da

      /cars/colors restituisce un elenco di tutti i colors per tutte le auto (la risorsa restituita è una raccolta di oggetti colorati)

      /cars/colors/red,blue,green restituirebbero un elenco di oggetti di colore rossi, blu o verdi, non una raccolta di automobili.

      Per restituire le auto, il percorso sarebbe

      /cars?color=red,blue,green o /cars/search?color=red,blue,green

    • I parametri nel percorso sono più difficili da leggere perché le coppie nome / valore non sono isolate dal resto del percorso, che non è coppie nome / valore.

    Un ultimo commento. Preferisco /garages/yyy/cars (sempre al plurale) a /garage/yyy/cars (forse era un refuso nella risposta originale) perché evita di cambiare il percorso tra singolare e plurale. Per le parole con aggiunta di “s”, il cambiamento non è così male, ma cambiare /person/yyy/friends a /people/yyy sembra ingombrante.

    Per espandere la risposta di Peter, potresti fare di cercare una risorsa di prima class:

     POST /searches # create a new search GET /searches # list all searches (admin) GET /searches/{id} # show the results of a previously-run search DELETE /searches/{id} # delete a search (admin) 

    La risorsa Cerca avrebbe i campi per colore, marca modello, stato garaged, ecc. E potrebbe essere specificata in XML, JSON o in qualsiasi altro formato. Come la risorsa Car e Garage, è ansible limitare l’accesso alle Ricerche basate sull’autenticazione. Gli utenti che eseguono frequentemente le stesse ricerche possono memorizzarli nei loro profili in modo che non debbano essere ricreati. Gli URL saranno abbastanza brevi che in molti casi possono essere facilmente scambiati via e-mail. Queste ricerche memorizzate possono essere la base di feed RSS personalizzati e così via.

    Ci sono molte possibilità per usare le Ricerche quando le consideri come risorse.

    L’idea è spiegata in modo più dettagliato in questo Railscast .

    La risposta di Justin è probabilmente la strada da percorrere, anche se in alcune applicazioni potrebbe essere opportuno considerare una ricerca particolare come una risorsa a sé stante, ad esempio se si desidera supportare ricerche salvate con nome:

     /search/{searchQuery} 

    o

     /search/{savedSearchName} 

    Questo non è REST. Non è ansible definire URI per le risorse all’interno dell’API. La navigazione delle risorse deve essere basata su ipertesti. Va bene se si vogliono degli URI piuttosto lunghi e una quantità elevata di accoppiamenti, ma semplicemente non chiamarlo REST, perché viola direttamente i vincoli dell’architettura RESTful.

    Vedi questo articolo dall’inventore di REST.

    Io uso due approcci per implementare le ricerche.

    1) Caso più semplice, per interrogare gli elementi associati e per la navigazione.

      /cars?q.garage.id.eq=1 

    Ciò significa, interrogare le auto che hanno l’ID del garage uguale a 1.

    È anche ansible creare ricerche più complesse:

      /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100 

    Auto in tutti i garage di FirstStreet che non sono rosse (3 ° pagina, 100 elementi per pagina).

    2) Le query complesse sono considerate risorse regolari che vengono create e possono essere ripristinate.

      POST /searches => Create GET /searches/1 => Recover search GET /searches/1?offset=300&max=100 => pagination in search 

    Il corpo POST per la creazione della ricerca è il seguente:

      { "$class":"test.Car", "$q":{ "$eq" : { "color" : "red" }, "garage" : { "$ne" : { "street" : "FirstStreet" } } } } 

    Si basa su Grails (criteri DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html

    Anche se mi piace la risposta di Justin, ritengo che rappresenti più accuratamente un filtro piuttosto che una ricerca. Cosa succede se voglio sapere di automobili con nomi che iniziano con cam?

    Per come la vedo io, potresti costruirlo nel modo in cui gestisci risorse specifiche:
    / Auto / cam *

    Oppure potresti semplicemente aggiungerlo al filtro:
    / Auto / porte / 4 / nome / CAM * / colors / rosso, blu, verde

    Personalmente, preferisco quest’ultimo, tuttavia non sono affatto un esperto di REST (avendo sentito parlare per la prima volta solo 2 o più settimane fa …)

    Inoltre vorrei anche suggerire:

     /cars/search/all{?color,model,year} /cars/search/by-parameters{?color,model,year} /cars/search/by-vendor{?vendor} 

    Qui, la Search è considerata una risorsa Cars risorsa Cars .

    RESTful non consiglia l’uso di verbi in URL / auto / ricerca non è riposante. Il modo giusto per filtrare / cercare / impaginare le tue API è attraverso i parametri di query. Tuttavia potrebbero esserci casi in cui devi rompere la norma. Ad esempio, se stai cercando tra più risorse, devi usare qualcosa come / search? Q = query

    Puoi consultare http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/ per comprendere le migliori pratiche per la progettazione delle API RESTful

    Ci sono molte buone opzioni per il tuo caso qui. Comunque dovresti considerare di usare il corpo POST.

    La stringa di query è perfetta per il tuo esempio, ma se hai qualcosa di più complicato, ad esempio un lungo elenco arbitrario di elementi o condizionali booleani, potresti voler definire il post come un documento, che il client invia su POST.

    Ciò consente una descrizione più flessibile della ricerca, oltre a evitare il limite di lunghezza dell’URL del server.

    Il mio consiglio sarebbe questo:

     /garages Returns list of garages (think JSON array here) /garages/yyy Returns specific garage /garage/yyy/cars Returns list of cars in garage /garages/cars Returns list of all cars in all garages (may not be practical of course) /cars Returns list of all cars /cars/xxx Returns specific car /cars/colors Returns lists of all posible colors for cars /cars/colors/red,blue,green Returns list of cars of the specific colors (yes commas are allowed :) ) 

    Modificare:

     /cars/colors/red,blue,green/doors/2 Returns list of all red,blue, and green cars with 2 doors. /cars/type/hatchback,coupe/colors/red,blue,green/ Same idea as the above but a lil more intuitive. /cars/colors/red,blue,green/doors/two-door,four-door All cars that are red, blue, green and have either two or four doors. 

    Spero che questo ti dia l’idea. Essenzialmente la tua API di Rest dovrebbe essere facilmente individuabile e dovrebbe consentirti di navigare tra i tuoi dati. Un altro vantaggio dell’utilizzo di URL e non di stringhe di query è che si è in grado di sfruttare i meccanismi di memorizzazione nella cache nativi esistenti sul server Web per il traffico HTTP.

    Ecco un collegamento a una pagina che descrive i mali delle stringhe di query in REST: http://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

    Ho usato la cache di Google perché la pagina normale non funzionava per me anche questo link: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful