API REST: perché usare PUT DELETE POST GET?


155

Quindi, stavo guardando alcuni articoli sulla creazione di API REST. E alcuni di loro suggeriscono di utilizzare tutti i tipi di richieste HTTP: come PUT DELETE POST GET. Vorremmo creare ad esempio index.php e scrivere API in questo modo:

$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));

switch ($method) {
  case 'PUT':
    ....some put action.... 
    break;
  case 'POST':
    ....some post action.... 
    break;
  case 'GET':
    ....some get action.... 
    break;
  case 'DELETE':
    ....some delete action.... 
    break;
}

OK, garantito - non so molto sui servizi web (ancora). Ma non sarebbe più semplice accettare semplicemente l' oggetto JSON tramite regolare POSTo GET(che conterrebbe il nome del metodo e tutti i parametri) e quindi rispondere anche in JSON. Possiamo facilmente serializzare / deserializzare via PHP di json_encode()e json_decode()e fare tutto ciò che vogliamo con i dati senza avere a che fare con diversi metodi di richiesta HTTP.

Mi sto perdendo qualcosa?

AGGIORNAMENTO 1:

Ok - dopo aver esaminato varie API e aver imparato molto su XML-RPC , JSON-RPC , SOAP , REST , sono giunto alla conclusione che questo tipo di API è valido. In realtà lo scambio di stack sta praticamente usando questo approccio sui loro siti e penso che queste persone sappiano cosa stanno facendo API di Exchange Stack .


4
Perché forzare un payload JSON? E se non ci fosse JSON, ed è semplicemente un vecchio GET?
Mike DeSimone,

Risposte:


200

L'idea di RE di presentazione S tato T ransfer non riguarda l'accesso ai dati nel modo più semplice possibile.

Hai suggerito di utilizzare le richieste post per accedere a JSON, che è un modo perfettamente valido per accedere / manipolare i dati.

REST è una metodologia per l' accesso significativo ai dati. Quando vedi una richiesta in REST, dovrebbe immediatamente essere evidente cosa sta succedendo con i dati.

Per esempio:

GET: /cars/make/chevrolet

è probabile che restituirà un elenco di auto chevy. Un buon API REST potrebbe anche incorporare alcune opzioni di output simili a querystring ?output=jsono ?output=htmlche consentirebbero all'accessorio di decidere in quale formato devono essere codificate le informazioni.

Dopo un po 'di pensare a come digitazione dati ragionevolmente incorporare in un API REST, ho concluso che il modo migliore per specificare il tipo di dati in modo esplicito sarebbe tramite l'estensione del file già esistente, come .js, .json, .html, o .xml. Un'estensione di file mancante sarebbe predefinita in qualsiasi formato predefinito (come JSON); un'estensione di file non supportata potrebbe restituire un 501 Not Implementedcodice di stato .

Un altro esempio:

POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }

è probabile che creerà un nuovo malibu chevy nel db con i colori associati. Dico probabilmente perché l'API REST non ha bisogno di essere direttamente correlata alla struttura del database. È solo un'interfaccia di mascheramento in modo che i dati reali siano protetti (pensali come accessori e mutatori per una struttura di database).

Ora dobbiamo passare alla questione dell'idempotenza . Di solito REST implementa CRUD su HTTP. HTTP utilizza GET, PUT, POSTe DELETEper le richieste.

Un'implementazione molto semplicistica di REST potrebbe utilizzare la seguente mappatura CRUD:

Create -> Post
Read   -> Get
Update -> Put
Delete -> Delete

C'è un problema con questa implementazione: Post è definito come un metodo non idempotente. Ciò significa che le chiamate successive dello stesso metodo Post genereranno stati server diversi . Ottieni, Put ed Elimina, sono idempotenti; il che significa che chiamandoli più volte dovrebbe risultare in un identico stato del server.

Ciò significa che una richiesta come:

Delete: /cars/oldest

potrebbe effettivamente essere implementato come:

Post: /cars/oldest?action=delete

Mentre

Delete: /cars/id/123456

risulterà nello stesso stato del server se lo chiami una volta o se lo chiami 1000 volte.

Un modo migliore di gestire la rimozione oldestdell'articolo sarebbe quello di richiedere:

Get: /cars/oldest

e utilizzare i IDdati risultanti per effettuare una deleterichiesta:

Delete: /cars/id/[oldest id]

Un problema con questo metodo sarebbe se un altro /carsarticolo fosse aggiunto tra quando è /oldeststato richiesto e quando è deletestato emesso.


3
@Andre è una combinazione di una serie di motivi: seguire le linee guida HTTP significa che (probabilmente) avrai meno problemi di compatibilità all'indietro quando le cose cambiano; l'utilizzo di un modulo html tramite POST avviserà l'utente per più invii degli stessi dati (ciò serve a prevenire una transazione non idempotente); seguire una best practice ben definita è, beh, la migliore pratica. Il riposo non è definito pensando a un'implementazione specifica, che ti consente di usarlo come ritieni opportuno. Suggerirei di sfruttare tutti i codici di errore HTTP e i metodi di richiesta, ma ti è permesso farlo come vuoi
zzzzBov

4
Quindi il problema con questa risposta (è una risposta decente, ma non completa) è che non affronta la domanda principale che ha posto: perché dovresti usare i verbi HTTP e l'URI piuttosto che dati JSON personalizzati (forse una sorta di Sintassi di invocazione dell'API basata su JSON). Puoi creare la tua sintassi JSON personalizzata in modo che sia "immediatamente ... evidente cosa sta succedendo con i dati". Quello che non puoi fare è usare facilmente le strutture integrate e i livelli di rete sopra HTTP come puoi con un'API che segue tutte le convenzioni REST. Non che la mia risposta sia perfetta, ovviamente;)
Merlyn Morgan-Graham,

4
@Andre: gli esempi utilizzati dalla voce wiki sono l'autenticazione, la memorizzazione nella cache e la negoziazione del tipo di contenuto. Ora che ci sto pensando di più, potresti essere in grado di usarli con interfacce in stile RPC, ma la tentazione sarà spesso quella di implementare il tuo sistema da zero o codificare un'integrazione con un sistema esistente. Con REST è possibile utilizzare l'integrazione integrata e amministrarla sul server Web. Questo significa accoppiamento più lento, il che significa che devi implementare meno e che la tua app è molto più flessibile per cambiare le opzioni in futuro con un codice minore e un impatto di test.
Merlyn Morgan-Graham,

10
Invece di DELETE: / cars / old, che ne dici di GET: / cars / old seguito da DELETE? In questo modo, hai due comandi idempotenti separatamente.
Neil,

3
+1; Sono d'accordo che questa è una buona risposta (ci riproverò per divertimento e profitto). POST: /cars/oldestessere un sostituto di un DELETE non ha molto senso. Qualcosa del genere - POST: /cars/oldest/deletepotrebbe, anche se penso che mi piaccia di più la soluzione di Neil. L'unico vantaggio che una cancellazione diretta offre alla sua soluzione get-id-delete-id è l'atomicità. Vorrei una chiara giustificazione commerciale con uno scenario non inventato prima di implementare una cosa del genere. Non è necessario supportare tutti i verbi su tutti gli oggetti / URL.
Merlyn Morgan-Graham,

39

Questa è una domanda di sicurezza e manutenibilità.

metodi sicuri

Quando possibile, è necessario utilizzare metodi "sicuri" (unidirezionali) come GET e HEAD al fine di limitare la potenziale vulnerabilità.

metodi idempotenti

Quando possibile, dovresti usare metodi "idempotenti" come GET, HEAD, PUT e DELETE, che non possono avere effetti collaterali e sono quindi meno soggetti a errori / più facili da controllare.

fonte


1
Siamo spiacenti, ma come sono i metodi idempotenti PUT e DELETE? Interessano lo stato del server e i suoi dati!
Mahmoud Al-Qudsi,

27
@Computer: l'esecuzione dello stesso PUT o della stessa ELIMINA comporta lo stesso stato finale. Questo è ciò che significa "idempotente".
Ignacio Vazquez-Abrams,

4
Per ulteriori chiarimenti: un'operazione F è idempotente, se la sua singola applicazione e le sue diverse applicazioni conseguenti restituiscono entrambi lo stesso risultato. Più precisamente F è idempotente se e solo se F (x) = F (F (x)). Ad esempio, Elimina è idempotente, perché quando si elimina un elemento una volta o lo si elimina più volte, il risultato è lo stesso: l'elemento viene eliminato solo una volta con la prima applicazione Elimina e non accade nulla nella seconda o terza applicazione Elimina.
qartal,

1
Ma in termini di creazione, quando si crea un nuovo record con un comando create e si immette nuovamente lo stesso comando, vengono (probabilmente) creati due record (sebbene entrambi riflettano le stesse informazioni).
qartal,

qartal - la definizione funzionale per idempotente dovrebbe essere 'F (X) = F (X) F (X)'. Bel modo di esprimerlo però.
Gerard ONeill,

26

In breve, REST enfatizza i nomi sui verbi. Man mano che l'API diventa più complessa, aggiungi più cose anziché più comandi.


2
Ho avuto un po 'di problemi a farmi girare la testa. Questo post ( lornajane.net/posts/2013/… ) secondo il quale il verbo dovrebbe provenire dalla richiesta HTTP in modo che l'URI dovesse contenere solo sostantivi mi ha chiarito un po '
icc97

9

Hai chiesto :

non sarebbe più semplice accettare l'oggetto JSON tramite $ _POST normale e quindi rispondere anche in JSON

Da Wikipedia su REST :

Le applicazioni RESTful massimizzano l'uso dell'interfaccia preesistente e ben definita e di altre funzionalità integrate fornite dal protocollo di rete scelto e minimizzano l'aggiunta di nuove funzionalità specifiche dell'applicazione

Da quello che (poco) ho visto, credo che questo di solito si realizzi massimizzando l'uso dei verbi HTTP esistenti e progettando uno schema URL per il tuo servizio che sia il più potente ed evidente possibile.

I protocolli dati personalizzati (anche se basati su standard, come SOAP o JSON) sono sconsigliati e dovrebbero essere ridotti al minimo per conformarsi al meglio all'ideologia REST.

SOAP RPC su HTTP, d'altra parte, incoraggia ogni progettista dell'applicazione a definire un nuovo e arbitrario vocabolario di nomi e verbi (ad esempio getUsers (), savePurchaseOrder (...)), solitamente sovrapposto al verbo HTTP 'POST'. Ciò ignora molte delle funzionalità esistenti di HTTP come l'autenticazione, la memorizzazione nella cache e la negoziazione del tipo di contenuto e può lasciare al progettista dell'applicazione di reinventare molte di queste funzionalità all'interno del nuovo vocabolario.

Gli oggetti reali con cui stai lavorando possono essere in qualsiasi formato. L'idea è di riutilizzare il maggior numero possibile di HTTP per esporre le operazioni che l'utente desidera eseguire su tali risorse (query, gestione / mutazione dello stato, eliminazione).

Hai chiesto :

Mi sto perdendo qualcosa?

C'è molto altro da sapere su REST e sulla stessa sintassi URI / sui verbi HTTP stessi. Ad esempio, alcuni dei verbi sono idempotenti, altri no. Non ho visto nulla al riguardo nella tua domanda, quindi non mi sono preoccupato di provare ad immergermi. Le altre risposte e Wikipedia hanno entrambe molte buone informazioni.

Inoltre, c'è molto da imparare sulle varie tecnologie di rete basate su HTTP che puoi sfruttare se stai usando un'API davvero riposante. Vorrei iniziare con l'autenticazione.


8

Per quanto riguarda l'utilizzo dell'estensione per definire il tipo di dati. Ho notato che l'API MailChimp lo sta facendo, ma non credo sia una buona idea.

GET /zzz/cars.json/1

GET /zzz/cars.xml/1

Il mio suono è una buona idea, ma penso che l'approccio "più vecchio" sia migliore - usando le intestazioni HTTP

GET /xxx/cars/1
Accept: application/json

Anche le intestazioni HTTP sono molto migliori per la comunicazione tra tipi di dati (se mai qualcuno ne avesse bisogno)

POST /zzz/cars
Content-Type: application/xml     <--- indicates we sent XML to server
Accept: application/json          <--- indicates we want get data back in JSON format  

4

Mi sto perdendo qualcosa?

Sì. ;-)

Questo fenomeno esiste a causa del vincolo di interfaccia uniforme . A REST piace usare standard già esistenti invece di reinventare la ruota. Lo standard HTTP ha già dimostrato di essere altamente scalabile (il Web funziona da un po '). Perché dovremmo riparare qualcosa che non è rotto ?!

nota: il vincolo uniforme dell'interfaccia è importante se si desidera disaccoppiare i client dal servizio. È simile alla definizione delle interfacce per le classi al fine di separarle l'una dall'altra. Ofc. qui l'interfaccia uniforme è costituita da standard come HTTP , tipi MIME , URI , RDF , vocaboli di dati collegati , vocabolario di hydra , ecc ...


2

La buona semantica è importante nella programmazione.

Utilizzare più metodi oltre a GET / POST sarà utile perché aumenterà la leggibilità del codice e ne renderà più semplice la manutenzione.

Perché?

Perché sai che GET recupererà i dati dalla tua API. Sai che POST aggiungerà nuovi dati al tuo sistema. Sai che PUT effettuerà aggiornamenti. ELIMINA eliminerà le righe, ecc., Ecc.

Normalmente strutturo i miei servizi Web RESTFUL in modo da disporre di una funzione di callback denominata come il metodo.

Uso PHP, quindi uso function_exists (penso che sia chiamato). Se la funzione non esiste, lancio un 405 (METODO NON AMMESSO).


2

Bill Venners: Nel tuo post sul blog intitolato "Why REST Failed", hai affermato che abbiamo bisogno di tutti e quattro i verbi HTTP — GET, POST, PUT e DELETE — e hai lamentato che i fornitori di browser ricevono solo GET e POST. "Perché abbiamo bisogno di tutti e quattro verbi? Perché GET e POST non sono abbastanza?

Elliotte Rusty Harold: ci sono quattro metodi di base in HTTP: GET, POST, PUT e DELETE. GET viene utilizzato la maggior parte del tempo. È usato per tutto ciò che è sicuro, che non causa effetti collaterali. GET è in grado di essere aggiunto ai segnalibri, memorizzato nella cache, collegato a, passato attraverso un server proxy. È un'operazione molto potente, un'operazione molto utile.

Il POST al contrario è forse l'operazione più potente. Può fare qualsiasi cosa. Non ci sono limiti a ciò che può accadere e, di conseguenza, devi stare molto attento. Non lo aggiungi ai segnalibri. Non lo memorizzi nella cache. Non lo precarichi. Non si fa nulla con un POST senza chiedere all'utente. Vuoi fare questo? Se l'utente preme il pulsante, è possibile pubblicare alcuni contenuti. Ma non vedrai tutti i pulsanti di una pagina e inizierai a premerli in modo casuale. Al contrario, i browser potrebbero guardare tutti i collegamenti sulla pagina e pre-recuperarli, oppure pre-recuperare quelli che pensano che probabilmente seguiranno dopo. E infatti alcuni browser ed estensioni di Firefox e vari altri strumenti hanno provato a farlo in un punto o nell'altro.

PUT e DELETE sono nel mezzo tra GET e POST. La differenza tra PUT o DELETE e POST è che PUT e DELETE sono * idempotenti, mentre POST no. PUT e DELETE possono essere ripetuti se necessario. Supponiamo che tu stia tentando di caricare una nuova pagina su un sito. Supponi di voler creare una nuova pagina all'indirizzo http://www.example.com/foo.html, quindi digiti i tuoi contenuti e li metti in quell'URL. Il server crea quella pagina in corrispondenza dell'URL fornito. Supponiamo ora per qualche motivo che la connessione di rete non funzioni. Non sei sicuro, la richiesta è arrivata o no? Forse la rete è lenta. Forse si è verificato un problema con il server proxy. Quindi è perfettamente OK riprovare, o di nuovo, tutte le volte che vuoi. Perché INSERIRE lo stesso documento nello stesso URL dieci volte non sarà diverso dal metterlo una volta. Lo stesso vale per DELETE. Puoi CANCELLARE qualcosa dieci volte, ed è lo stesso che cancellarlo una volta.

Al contrario, POST può causare ogni volta qualcosa di diverso. Immagina di fare il check-out da un negozio online premendo il pulsante Acquista. Se invii di nuovo quella richiesta POST, potresti finire per comprare una seconda volta tutto nel tuo carrello. Se lo invii di nuovo, l'hai acquistato per la terza volta. Ecco perché i browser devono stare molto attenti a ripetere le operazioni POST senza il consenso esplicito dell'utente, perché POST può causare due cose se lo fai due volte, tre cose se lo fai tre volte. Con PUT e DELETE, c'è una grande differenza tra zero richieste e una, ma non c'è differenza tra una richiesta e dieci.

Si prega di visitare l'URL per maggiori dettagli. http://www.artima.com/lejava/articles/why_put_and_delete.html

Aggiornare:

Metodi idempotenti Un metodo HTTP idempotente è un metodo HTTP che può essere chiamato molte volte senza risultati diversi. Non importa se il metodo viene chiamato solo una volta o dieci volte. Il risultato dovrebbe essere lo stesso. Ancora una volta, questo vale solo per il risultato, non per la risorsa stessa. Questo può ancora essere manipolato (come un timestamp di aggiornamento, a condizione che queste informazioni non siano condivise nella rappresentazione (corrente) delle risorse.

Considera i seguenti esempi:

a = 4;

a ++;

Il primo esempio è idempotente: non importa quante volte eseguiamo questa affermazione, a sarà sempre 4. Il secondo esempio non è idempotente. L'esecuzione di questo 10 volte comporterà un risultato diverso rispetto a quando si esegue 5 volte. Poiché entrambi gli esempi stanno modificando il valore di a, entrambi sono metodi non sicuri.


1
Sull'esempio di una nuova pagina, POST non dovrebbe essere usato in quel modo, mentre PUT per un aggiornamento? La creazione di una nuova pagina è un processo che genera un nuovo risultato ogni volta, mentre la stessa modifica può essere reinserita in qualsiasi numero di volte, restituendo lo stesso risultato ogni volta. Bella frase e spiegazione, però.
Spyryto

0

Fondamentalmente REST è ( wiki ):

  1. Architettura client-server
  2. apolidia
  3. Cache
  4. Sistema a strati
  5. Codice su richiesta (opzionale)
  6. Interfaccia uniforme

REST non è protocollo, è principi. Uri e metodi diversi - qualcuno cosiddetto best practice.

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.