Come si inseriscono le ricerche in un'interfaccia RESTful?

115

Quando si progetta un'interfaccia RESTful, la semantica dei tipi di richiesta è ritenuta vitale per il design.

  • OTTIENI - Elenca la raccolta o il recupero dell'elemento
  • PUT - Sostituisci raccolta o elemento
  • POST - Crea raccolta o elemento
  • DELETE - Bene, erm, elimina la raccolta o l'elemento

Tuttavia, questo non sembra coprire il concetto di "ricerca".

es. nella progettazione di una suite di servizi Web che supportano un sito di ricerca di lavoro si potrebbero avere i seguenti requisiti:

  • Trova un annuncio di lavoro individuale
    • OTTIENI a domain/Job/{id}/
  • Crea annuncio di lavoro
    • POST a domain/Job/
  • Annuncio del processo di aggiornamento
    • PUT a domain/Job/
  • Elimina annuncio di lavoro
    • DELETE a domain/Job/

Anche "Ottieni tutti i lavori":

  • OTTIENI a domain/Jobs/

Tuttavia, in che modo il lavoro "cerca" rientra in questa struttura?

potresti sostenere che si tratta di una variazione di "raccolta di elenchi" e implementare come:

  • OTTIENI a domain/Jobs/

Tuttavia, le ricerche possono essere complesse ed è del tutto possibile produrre una ricerca che genera una lunga stringa GET. Cioè, facendo riferimento a una domanda SO qui , ci sono problemi con le stringhe GET più lunghe di circa 2000 caratteri.

Un esempio potrebbe essere in una ricerca sfaccettata - continua l'esempio di "lavoro".

Posso consentire la ricerca su faccette: "Tecnologia", "Titolo lavoro", "Disciplina", nonché parole chiave a testo libero, età del lavoro, posizione e salario.

Con un'interfaccia utente fluida e un gran numero di tecnologie e titoli di lavoro, è possibile che una ricerca possa comprendere un gran numero di scelte di faccette.

Modificare questo esempio in CV, piuttosto che in lavori, aggiungere ancora più sfaccettature, e puoi facilmente immaginare una ricerca con cento facce selezionate, o anche solo 40 facce ognuna delle quali ha una lunghezza di 50 caratteri (ad es. Job Titles, Nomi universitari, nomi dei datori di lavoro).

In questa situazione potrebbe essere preferibile spostare un PUT o POST per garantire che i dati di ricerca vengano correttamente inviati. Per esempio:.

  • POST a domain/Jobs/

Ma semanticamente è un'istruzione per creare una collezione.

Puoi potresti anche dire che lo esprimerai come la creazione di una ricerca:

  • POST a domain/Jobs/Search/

o (come suggerito da burninggramma sotto)

  • POST a domain/JobSearch/

Semanticamente può sembrare sensato, ma in realtà non stai creando nulla, stai facendo una richiesta di dati.

Quindi, semanticamente è un GET , ma non è garantito che GET supporti ciò di cui hai bisogno.

Quindi, la domanda è: cercare di mantenere il più fedele possibile al progetto RESTful, assicurandomi di mantenere i limiti di HTTP, qual è il design più appropriato per una ricerca?

    
posta Rob Baillie 21.03.2014 - 11:59
fonte

7 risposte

75

Non dovresti dimenticare che le richieste GET hanno alcuni vantaggi superiori rispetto ad altre soluzioni:

1) Le richieste GET possono essere copiate dalla barra degli URL, sono digerite dai motori di ricerca, sono "amichevoli". Dove "amichevoli" significa che normalmente una richiesta GET non dovrebbe modificare qualsiasi cosa all'interno della tua applicazione (idempotente) . Questo è il caso standard per una ricerca.

2) Tutti i questi concetti sono molto importanti non solo da utente e motore di ricerca, ma da un punto di vista architettonico, design API .

3) Se crei una soluzione con POST / PUT avrai problemi a cui non stai pensando in questo momento. Ad esempio, in caso di browser, tornare indietro / aggiornare pagina / cronologia. Ovviamente possono essere risolti, ma sarà un'altra soluzione, poi un'altra e un'altra ...

Considerando tutto questo il mio consiglio sarebbe:

a) dovresti essere in grado di adattarti al tuo GET con utilizzando la struttura dei parametri intelligente . In casi estremi puoi persino utilizzare tattiche come questa ricerca di google dove ho impostato molti parametri è ancora un URL molto breve.

b) Crea un'altra entità nella tua applicazione come JobSearch . Supponendo che tu abbia così tante opzioni, è probabile che dovrai salvare anche queste ricerche e gestirle, quindi è sufficiente chiarire la tua domanda. Puoi lavorare con gli oggetti JobSearch come entità intera, ovvero puoi testarlo / usarlo più facile .

Personalmente proverei a combattere con tutti i miei artigli per farlo con a) e quando ogni speranza è persa, vorrei strisciare di nuovo con le lacrime agli occhi per l'opzione b) .

    
risposta data 21.03.2014 - 13:44
fonte
10

In REST, la definizione della risorsa è molto generica. In realtà, tuttavia, desideri raggruppare alcuni dati.

  • È utile pensare a una risorsa di ricerca come a una risorsa di raccolta. I parametri di query, talvolta denominati la parte ricercabile dell'URI, restringono la risorsa agli elementi a cui è interessato il client.

Ad esempio, l'URI principale di Google punta a una risorsa di raccolta di "link a ogni sito su Internet". I parametri di ricerca restringono il contenuto ai siti che desideri visualizzare.

(URI = identificatore di risorsa universale, di cui URL = universal resource locator, dove il familiare "http: //" è il formato predefinito per un URI. Quindi URL è un localizzatore, ma in REST è bene generalizzare a un identificatore di risorse, ma le persone le usano in modo intercambiabile.)

  • Poiché la risorsa che stai cercando nell'esempio è la raccolta di lavori, è consigliabile eseguire la ricerca con

GET site/jobs?type=blah&location=here&etc=etc

(return) {jobs: [{job: ...}]}

E poi usa POST, che è il verbo append o process per aggiungere nuovi elementi a quella raccolta:

POST site/jobs

{job: ...}

  • Si noti che è la stessa struttura per l'oggetto job in ciascun caso. Un client può ottenere una raccolta di lavori, utilizzando parametri di query per restringere la ricerca e quindi utilizzare lo stesso formato per uno degli elementi per POST un nuovo lavoro. Oppure può prendere uno di questi elementi e PUT al suo URI per aggiornarlo.

  • Per stringhe di query molto lunghe o complicate, la convenzione rende preferibile inviare quelle come richieste POST. Raggruppa i parametri della query come coppie nome / valore o oggetti nidificati in una struttura JSON o XML e invialo nel corpo della richiesta. Ad esempio, se la query ha dati annidati anziché un gruppo di coppie nome / valore. La specifica HTTP per POST la descrive come verbo append o process. (Se vuoi far navigare una nave da guerra attraverso una feritoia in REST, usa POST.)

Lo userei come piano di riserva, però.

Ciò che perdi quando lo fai è a) GET è nullipotent - cioè, non cambia nulla - il POST non lo è. Quindi, se la chiamata fallisce, il middleware non riproverà automaticamente o memorizzerà i risultati nella cache e 2) con i parametri di ricerca nel corpo, non sarà più possibile tagliare e incollare l'URI. Cioè, l'URI non è un identificatore specifico per la ricerca che desideri.

Per differenziare tra "crea" da "cerca". Ci sono un paio di opzioni che sono coerenti con la pratica REST:

  • Puoi farlo nell'URI aggiungendo qualcosa al nome della raccolta, come la ricerca di lavoro invece dei lavori. Significa solo che stai trattando la raccolta di ricerche come risorsa separata.

  • Poiché la semantica del POST è sia il processo di aggiunta OR, è possibile identificare i corpi di ricerca con il carico utile. Come {job: ...} vs. {search: ...}. È compito della logica del POST pubblicarlo o elaborarlo in modo appropriato.

È piuttosto una preferenza di progettazione / implementazione. Non penso ci sia una convenzione chiara.

Quindi, come hai già esposto, l'idea è definire una risorsa di raccolta per jobs

site/jobs

Cerca con GET + query param per restringere la ricerca. Le query di dati lunghe o strutturate vengono inserite nel corpo di un POST (eventualmente in una raccolta di ricerche separata). Crea con POST per aggiungere alla raccolta. E aggiorna con PUT a un URI specifico.

(FWIW la convenzione di stile con gli URI è quella di usare tutto minuscolo con parole separate da trattini, ma ciò non significa che devi farlo in quel modo.)

(Inoltre, dovrei dire che dalla tua domanda, è chiaro che sei molto avanti su questa strada. Ho spiegato esplicitamente le cose solo per metterle in fila, ma la tua domanda aveva già affrontato gran parte della semantica problemi in questa risposta: stavo solo allineandolo con alcune convenzioni e pratiche.)

    
risposta data 21.03.2014 - 15:52
fonte
8

In genere utilizzo le query OData, funzionano come una chiamata GET ma consentono di limitare le proprietà che vengono restituite e filtrarle.

Usi token come $select= e $filter= così finirai con un URI che assomiglia a qualcosa del tipo:

/users?$select=Id,Name$filter=endswith(Name, 'Smith')

Puoi anche fare il paging usando $skip e $top e ordinando.

Per ulteriori informazioni, consulta OData.org . Non hai specificato quale lingua stai usando, ma se è ASP.NET, la piattaforma WebApi supporta le query OData - per gli altri (PHP, ecc.) Ci sono probabilmente delle librerie che puoi usare per tradurle in query di database.

    
risposta data 21.03.2014 - 13:17
fonte
8

TL; DR: GET per il filtro, POST per la ricerca

Faccio una distinzione tra il filtrare i risultati dall'elenco di una collezione e una ricerca complessa. La cartina del tornasole che utilizzo è fondamentalmente se ho bisogno di più del filtro (positivo, negativo o intervallo) . Lo considero una ricerca più complessa che richiede POST.

Questo tende a essere rinforzato quando si pensa a ciò che verrà restituito. Solitamente utilizzo GET solo se una risorsa ha un ciclo di vita quasi completo (PUT, DELETE, GET, collection GET) . Tipicamente in una collezione GET sto restituendo una lista di URI che sono le risorse REST che compongono quella raccolta. In una query complessa, potrei estrarre da più risorse al fine di costruire la risposta (si pensi al join SQL) , quindi non invierò gli URI, ma i dati effettivi. Il problema è che i dati non saranno rappresentati in una risorsa, quindi dovrò sempre restituire i dati. Questo mi sembra un chiaro caso di richiedere un POST.

    
risposta data 24.03.2014 - 08:10
fonte
5

Un approccio da considerare è il trattamento dell'insieme di possibili query come risorsa di raccolta, ad es. /jobs/filters .

Le richieste di

POST a questa risorsa, con i parametri di query nel corpo, creeranno una nuova risorsa o identificheranno un filtro equivalente esistente e restituiranno un URL contenente il suo ID: /jobs/filters/12345 .

L'ID può quindi essere utilizzato in una richiesta GET per i lavori: /jobs?filter=12345 . Le successive richieste di GET sulla risorsa filtro restituiranno la definizione del filtro.

Questo approccio ha il vantaggio di liberarti dal formato del parametro di query per la definizione del filtro, fornendo potenzialmente più potenza per definire filtri complessi. Le condizioni OR sono un esempio a cui posso pensare che sono difficili da ottenere con le stringhe di query.

Uno svantaggio di questo approccio è che si perde la leggibilità dell'URL (anche se questo può essere mitigato recuperando la definizione attraverso una richiesta di GET per la risorsa filtro). Per questo motivo, potresti anche voler supportare lo stesso o un sottoinsieme dei parametri di query sulla risorsa /jobs come sosterrai per una risorsa filtro. Questo potrebbe essere usato per query più brevi. Se viene fornita questa funzionalità, per mantenere la cache tra i due tipi di filtro, quando si utilizzano i parametri di query sulla risorsa /jobs , l'implementazione deve creare / riutilizzare internamente una risorsa filtro e restituire uno stato 302 o 303 indicando l'URL sotto forma di /jobs?filter=12345 .

    
risposta data 27.03.2014 - 15:30
fonte
5

Questa è una vecchia risposta, ma posso ancora contribuire un po 'alla discussione. Ho osservato molto spesso un fraintendimento di REST, RESTful e Architecture. RESTful non menziona mai nulla riguardo alla ricerca di edifici NOT, non c'è nulla nel RESTful sull'architettura, è un insieme di principi o criteri di progettazione.

Per descrivere una ricerca in un modo migliore dobbiamo parlare di un'architettura in particolare e quella che si adatta meglio è l'architettura orientata alle risorse (ROA).

In RESTful ci sono principi da progettare, idempotent non significa che il risultato non può cambiare come leggo in alcune risposte, significa che il risultato di una richiesta indipendente non dipende da quante volte viene eseguito. Può cambiare, immaginiamo che sto aggiornando continuamente un database alimentandolo con alcuni dati che sono serviti da un RESTful Api, l'esecuzione dello stesso GET potrebbe cambiare il risultato ma non dipende da quante volte è stato eseguito. Se riesco a bloccare il mondo, significa che non c'è stato, trasformazione, nulla all'interno del servizio quando richiedo la risorsa che porta a un risultato diverso.

By definition, a resource is anything that's important to be referenced as a thing by itself.

In un'architettura orientata alle risorse (chiamiamola ROA d'ora in poi per brevità) ci concentriamo sulla risorsa che potrebbe essere un sacco di cose:

  • Una versione di un documento
  • L'ultima versione aggiornata del documento
  • Un risultato proveniente da una ricerca
  • Un elenco di oggetti
  • Il primo articolo che ho acquistato da un e-commerce

Ciò che lo rende unico in termini di risorse è l' addresability che significa che ha solo un URI

In questo modo la ricerca si adatta perfettamente a RESTful considerando ROA . Dobbiamo utilizzare GET perché suppongo che la ricerca sia una ricerca normale e non cambi nulla, quindi è idempotente (anche se restituisce cose diverse a seconda dei nuovi elementi aggiunti). C'è una confusione in questo senso perché potrei attenermi a RESTful e non a ROA, significa che potrei seguire uno schema che crea una ricerca e restituire cose diverse con gli stessi parametri perché non sto usando il principio di indirizzabilità di ROA. Come è? Bene, se invii i filtri di ricerca nel corpo o nell'intestazione, la risorsa non è INDIRIZZABILE.

Puoi trovare i principi di ciò che è esattamente e l'URI nel documento originale W3:

link

Qualsiasi URL in questa architettura deve essere auto-descrittivo. È necessario seguire i principi per affrontare tutto nell'URI, significa che puoi usare / (barra) per separare tutto ciò di cui hai bisogno o interrogare i parametri. Sappiamo che ci sono dei limiti su questo, ma questo è il modello di architettura.

Seguendo il modello ROA in RESTful una ricerca non è più di ogni altra risorsa, l'unica differenza è che le risorse provengono da un calcolo invece di una relazione diretta con l'oggetto stesso. In base al principio, potrei indirizzare e ottenere un semplice servizio di calcolo aritmetico basato sul seguente schema:

link

Dove sum, 1 e 2 possono essere modificati ma il risultato del calcolo è univoco ed è indirizzabile, ogni volta che chiamo con gli stessi parametri ottengo lo stesso e niente cambia nel servizio. Il resouce / sum / 1/2 e / substract / 5/4 si adattano perfettamente ai principi.

    
risposta data 08.03.2017 - 06:18
fonte
3

GET è ok, se hai una collezione statica che restituisce sempre gli stessi risultati (rappresentazione) per un URI. Ciò implica anche che i dati che generano queste rappresentazioni non vengano mai modificati. L'origine è un database di sola lettura.

Dopo aver ottenuto risultati diversi per uno stesso URI viola idempotency / safety e CoolURI principio ed è quindi non RESTful . È possibile scrivere verbi idempotenti su un database, ma non devono mai influire sulla rappresentazione.

Una ricerca comune inizia con una richiesta POST che restituisce un riferimento al risultato. Genera il risultato (è nuovo e può essere recuperato con un successivo GET). Questo risultato può essere gerarchico (ulteriori riferimenti con URI che è possibile ottenere), ovviamente, e potrebbe riutilizzare elementi di ricerche precedenti, se ha senso per l'applicazione.

A proposito, so che le persone lo fanno diversamente. Non è necessario spiegarmi quanto può essere comodo violare REST.

    
risposta data 11.09.2016 - 02:19
fonte

Leggi altre domande sui tag