Ci sono problemi con l'implementazione di metodi HTTP personalizzati?


34

Abbiamo un URL nel seguente formato

/ Instance / {instanceType} / {} instanceId

Puoi chiamarlo con i metodi HTTP standard: POST, GET, DELETE, PUT. Tuttavia, ci sono alcune altre azioni che intraprendiamo come "Salva come bozza" o "Curare"

Pensavamo di poter usare solo metodi HTTP personalizzati come: DRAFT, VALIDATE, CURATE

Penso che questo sia accettabile poiché gli standard dicono

"Il set di metodi comuni per HTTP / 1.1 è definito di seguito. Sebbene questo set possa essere espanso, non si può presumere che metodi aggiuntivi condividano la stessa semantica per client e server estesi separatamente."

E strumenti come WebDav creano alcune delle proprie estensioni.

Ci sono problemi che qualcuno ha incontrato con metodi personalizzati? Sto pensando a server proxy e firewall, ma qualsiasi altra area di interesse è benvenuta. Dovrei rimanere al sicuro e avere solo un parametro URL come action = validate | curate | draft?


6
Come cito di nuovo da RFC 1925 - "Nella progettazione del protocollo, la perfezione è stata raggiunta non quando non c'è più niente da aggiungere, ma quando non c'è più nulla da togliere". - se funziona, non c'è motivo di aggiungere a http.

4
Niente di male, purché ti rendi conto che ora stai utilizzando un protocollo personalizzato e non HTTP.
user16764

10
@ user16764 "Il set di metodi comuni per HTTP / 1.1 è definito di seguito. Sebbene questo set possa essere espanso, non si può presumere che metodi aggiuntivi condividano la stessa semantica per client e server estesi separatamente." w3.org/Protocols/rfc2616/rfc2616-sec9.html Pertanto è consentito ed è ancora HTTP
Juan Mendes

imho non c'è nulla da aggiungere / rimuovere da HTTP poiché le definizioni dei metodi affermano che l'utilizzo di metodi personalizzati è già accettabile nell'ambito HTTP / 1.1 ma non ci si può aspettare che condividano la stessa semantica, quindi immagino che i punti sia di @MichaelT che di Juan Mendes possano essere un po 'placato
Prof83

Risposte:


42

Uno dei vincoli fondamentali di HTTP e la caratteristica di progettazione centrale di REST è un'interfaccia uniforme fornita da (tra le altre cose) una piccola serie fissa di metodi che si applicano universalmente a tutte le risorse. Il vincolo di interfaccia uniforme ha una serie di aspetti positivi e negativi. Sto citando da Fielding qui liberamente.

Un'interfaccia uniforme:

  • è più semplice.
  • disaccoppia le implementazioni dai servizi che forniscono.
  • consente un'architettura a più livelli, inclusi elementi come bilanciamento del carico HTTP (nginx) e cache (vernice).

D'altra parte, un'interfaccia uniforme:

  • riduce l'efficienza, poiché le informazioni vengono trasferite in una forma standardizzata anziché in una specifica per le esigenze di un'applicazione.

I compromessi sono "progettati per il caso comune del Web" e hanno permesso di costruire un grande ecosistema che fornisce soluzioni a molti dei problemi comuni nelle architetture web. L'adesione a un'interfaccia uniforme consentirà al tuo sistema di beneficiare di questo ecosistema mentre la sua rottura lo renderà così difficile. È possibile che si desideri utilizzare un bilanciamento del carico come nginx, ma ora è possibile utilizzare solo un bilanciamento del carico che comprende DRAFT e CURATE. Potresti voler utilizzare un livello di cache HTTP come Varnish ma ora puoi utilizzare solo un livello di cache HTTP che comprende DRAFT e CURATE. Potresti chiedere a qualcuno aiuto per la risoluzione di un errore del server, ma nessun altro conosce la semantica di una richiesta CURATE. Potrebbe essere difficile modificare le librerie client o server preferite per comprendere e implementare correttamente i nuovi metodi. E così via.

Il modo corretto * di rappresentarlo è come una trasformazione di stato sulla risorsa (o risorse correlate). Non si disegna un post, si trasforma il suo draftstato trueo si crea una draftrisorsa che contiene le modifiche e i collegamenti alle precedenti versioni di bozza. Non CURATE un post, ne trasformate lo curatedstato trueo create una curationrisorsa che collega il post all'utente che lo ha curato.

* Corretto in quanto segue da vicino i principi dell'architettura REST.


Grazie per i commenti sul bilanciamento del carico, lo guarderò sicuramente. Conosci una risorsa che afferma se i metodi personalizzati sono accettabili o no?
Juan Mendes,

2
Non vedo alcun vantaggio sui metodi personalizzati a meno che non facciano parte di un'estensione ampiamente supportata come WEBDAV (e anche allora, non così tanto), quindi non ci ho mai pensato. Vorrei solo raccomandare di trattare questi cambiamenti come trasformazioni di stato. Il web funziona perfettamente con i metodi che già abbiamo. Non c'è davvero alcun buon motivo per aggiungere altro a meno che non abbiano senso come parte dell'interfaccia uniforme (come PATCH).
Rein Henrichs,

5
Vedo il vantaggio nel progettare il modo in cui vuoi che il tuo servizio HTTP funzioni da solo. tuttavia "non si può presumere che metodi aggiuntivi condividano la stessa semantica" - Abbastanza equo, MA fa ancora parte dell'ambito HTTP / 1.1, quindi firewall, proxy, bilanciamento del carico e simili dovrebbero consentire questa possibilità, se non non stanno quindi implementando HTTP / 1.1 correttamente?
Prof83,

Probabilmente hai ragione dal restante POV, tuttavia, non riesco a capire perché i verbi personalizzati dovrebbero essere un problema. Tutti gli strumenti dovrebbero trattarli come POST, ovvero "la risorsa probabilmente cambia e questo è tutto ciò che sappiamo".
maaartinus,

7

Preferirei progettare questi come risorse secondarie, su cui eseguire una richiesta POST.

Considerando che hai una risorsa in /instance/type/1, vorrei che la rappresentazione di quella risorsa trasmettesse un paio di collegamenti ad "azioni" che possono essere eseguite sulla risorsa, come /instance/type/1/drafte /instance/type/1/curate. In JSON, questo potrebbe essere semplice come:

{
    "some property":"the usual value",
    "state": "we can still inform the client about the current state",
    "draft": "http://server/instance/type/1/draft",
    "curate": "http://server/instance/type/1/curate"
}

Ciò consente al cliente di essere molto esplicito su ciò che deve accadere, durante la richiesta POST al collegamento fornito dal curatemembro. La risorsa pubblicata potrebbe includere argomenti che descrivono in dettaglio l'evento che potrebbe, forse, infliggere una transizione di stato.

Seguire l'approccio "ingenuo" di spostarsi tra i possibili stati su una risorsa ha lo svantaggio di non catturare quali eventi hanno portato a queste transizioni.

Le transizioni di stato di solito si verificano in risposta a eventi specifici e preferirei catturare quegli eventi piuttosto che lasciare che il client decida che qualcosa è ora in uno "stato" specifico. Inoltre rende la validazione molto più difficile. Inoltre, non saresti in grado di catturare alcun 'argomento' se non descrivi anche quelli nello stato stesso. E poi diventa tutto scemo quando un po 'di codice cambia quelli senza transizione di stato reale e la convalida richiesta, e tutto diventa rapidamente un casino.


Buona risposta. Essere in grado di fornire argomenti alle transizioni di stato e avere il server incapsulato e gestirli, è di gran lunga l'approccio migliore.
Thomas W,

La società in cui mi trovo attualmente (VMware) lo fa. Un GET su /vms/some-idrestituisce collegamenti ad azioni simili POST /vms/some-id/restarte lo usiamo per determinare se le azioni devono essere abilitate o disabilitate. Ho una relazione amore / odio con HATEOAS :)
Juan Mendes,

Avrebbe molto più senso se l'azione intrapresa fosse il verbo della richiesta rispetto ad alcuni parametri di query casuali, segmento del percorso delle risorse o proprietà del corpo.
Matthew Whited,

Non puoi collegarti a un verbo.
Dave Van den Eynde,

6

Penso che il metodo HTTP personalizzato sia il modo migliore per implementare le azioni dell'entità. L'aggiunta dell'azione al corpo dell'entità (POST) non sembra corretta, non fa parte dell'entità (anche se il risultato potrebbe essere salvato in esso). Inoltre, l'utilizzo dei proxy HTTP personalizzati può determinare le loro azioni senza la necessità di analizzare il corpo dell'entità.

È come CRUD, vorresti sempre implementarli, ma hai anche il tuo specifico set di azioni (per enitity). Davvero non vedo quale sarebbe il problema estenderli.

Anche @Rein Henrichs "Non progetti un post, trasformi il suo stato di bozza in vero o crei una bozza di risorsa" mi sembra falso. Una draftsproprietà verrebbe utilizzata per il salvataggio persistente dello stato, non per la trasformazione. Le azioni non comportano necessariamente necessariamente uno "stato", né essere salvate in una proprietà. La creazione di un'entità separata per ogni stato / trasformazione sembra ancora più sfocata. Cerca di mantenere lo stesso riferimento (URI) all'entità.


1
Questo è un punto giusto, sebbene ampiamente in disaccordo, posso vedere il ragionamento che sta dietro e non sono d'accordo con il voto negativo (specialmente senza commenti dell'elettore). Prendiamo ad esempio la gestione delle eccezioni PHP, "Best Practice" sembra incline all'utilizzo di tipi di Eccezione specifici per suggerire il tipo di eccezione, anche ignorando il messaggio reale come RuntimeException vs BadMethodCallException. Quindi perché si discute così tanto contro l'utilizzo di DRAFT se l'utilizzo di metodi personalizzati è già considerato parte dell'ambito HTTP / 1.1? E i bilanciatori del carico e i proxy dovrebbero davvero accettare anche questa possibilità
Prof83
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.