web-services - tra - web service vs rest api




Informazioni su REST: verbi, codici di errore e autenticazione (7)

Sto cercando un modo per avvolgere le API attorno alle funzioni predefinite nelle mie applicazioni web, database e CMS basati su PHP.

Mi sono guardato intorno e ho trovato diversi scheletri "scheletrici". Oltre alle risposte nella mia domanda, c'è Tonic , un framework REST che mi piace perché è molto leggero.

Mi piace REST il meglio per la sua semplicità e vorrei creare un'architettura API basata su di esso. Sto cercando di capire quali sono i principi di base e non l'ho ancora compreso appieno. Pertanto, una serie di domande.

1. Sto comprendendo giusto?

Diciamo che ho una risorsa "utenti". Potrei impostare un numero di URI in questo modo:

/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

è questa una rappresentazione corretta di un'architettura RESTful finora?

2. Ho bisogno di più verbi

Creare, aggiornare e cancellare potrebbe essere sufficiente in teoria, ma in pratica avrò bisogno di molti più verbi. Mi rendo conto che queste sono cose che potrebbero essere incorporate in una richiesta di aggiornamento, ma sono azioni specifiche che possono avere codici di ritorno specifici e non vorrei buttarli tutti in un'unica azione.

Alcuni che vengono in mente nell'esempio utente sono:

activate_login
deactivate_login
change_password
add_credit

come potrei esprimere azioni come quelle in un'architettura URL RESTful?

Il mio istinto sarebbe di fare una chiamata GET a un URL come

/api/users/1/activate_login 

e aspettarsi un codice di stato.

Ciò si discosta dall'idea di utilizzare i verbi HTTP, però. Cosa pensi?

3. Come restituire messaggi di errore e codici

Gran parte della bellezza di REST deriva dall'uso dei metodi HTTP standard. In caso di errore, emetto un'intestazione con un codice di stato di errore 3xx, 4xx o 5xx. Per una descrizione dettagliata dell'errore, posso usare il corpo (giusto?). Fin qui tutto bene. Ma quale sarebbe il modo di trasmettere un codice di errore proprietario che è più dettagliato nel descrivere cosa è andato storto (ad esempio "non è riuscito a connettersi al database", o "login nel database sbagliato")? Se lo inserisco nel corpo insieme al messaggio, devo analizzarlo in seguito. C'è un header standard per questo genere di cose?

4. Come fare l'autenticazione

  • Come sarebbe l'autenticazione basata su chiave API seguendo i principi REST?
  • Ci sono punti forti contro l'utilizzo delle sessioni durante l'autenticazione di un client REST, a parte il fatto che si tratta di una palese violazione del principio REST? :) (scherzando solo a metà, l'autenticazione basata su sessioni potrebbe giocare bene con la mia infrastruttura esistente).

Nozioni di base REST

REST ha un vincolo di interfaccia uniforme, che stabilisce che il client REST deve basarsi su standard invece di dettagli specifici dell'applicazione del servizio REST effettivo, quindi il client REST non si interromperà con piccole modifiche e sarà probabilmente riutilizzabile.

Quindi esiste un contratto tra il client REST e il servizio REST. Se si utilizza HTTP come protocollo sottostante, i seguenti standard fanno parte del contratto:

  • HTTP 1.1
    • definizioni dei metodi
    • definizioni del codice di stato
    • intestazioni di controllo della cache
    • accettare e intestazioni di tipo contenuto
    • intestazioni auth
  • IRI ( URI utf8)
  • corpo (sceglierne uno)
    • tipo MIME specifico dell'applicazione registrata, ad es. maze+xml
    • tipo MIME specifico del fornitore, ad esempio vnd.github+json
    • tipo MIME generico con
      • Vocabolario RDF specifico dell'applicazione, ad es. ld+json & hydra , schema.org
      • profilo specifico dell'applicazione, ad esempio hal+json e profilo link param (credo)
  • collegamenti ipertestuali
    • cosa dovrebbe contenere (sceglierne uno)
      • invio di intestazioni di link
      • invio di una risposta hypermedia, ad esempio html, atom + xml, hal + json, ld + json & hydra, ecc ...
    • semantica
      • utilizzare le relazioni di collegamento IANA e probabilmente le relazioni di collegamento personalizzate
      • usa un vocab RDF specifico per l'applicazione

REST ha un vincolo stateless, che dichiara che la comunicazione tra il servizio REST e il client deve essere stateless. Ciò significa che il servizio REST non può mantenere gli stati client, quindi non è possibile avere una memoria di sessione lato server. Devi autenticare ogni singola richiesta. Quindi, per esempio l'autenticazione di base HTTP (parte dello standard HTTP) va bene, perché invia lo username e la password ad ogni richiesta.

Per rispondere alle tue domande

  1. Sì, può essere.

    Solo per citare, i clienti non si preoccupano della struttura dell'IRI, si preoccupano della semantica, perché seguono link che hanno relazioni di link o attributi di dati collegati (RDF).

    L'unica cosa importante dell'IRI è che un singolo IRI deve identificare solo una singola risorsa. È consentito a una singola risorsa, come un utente, di avere diversi IRI diversi.

    È piuttosto semplice perché utilizziamo simpatici IRI come /users/123/password ; è molto più facile scrivere la logica di routing sul server quando si capisce l'IRI semplicemente leggendolo.

  2. Hai più verbi, come PUT, PATCH, OPTIONS, e anche di più, ma non ne hai bisogno di più ... Invece di aggiungere nuovi verbi devi imparare come aggiungere nuove risorse.

    activate_login -> PUT /login/active true deactivate_login -> PUT /login/active false change_password -> PUT /user/xy/password "newpass" add_credit -> POST /credit/raise {details: {}}

    (Il login non ha senso dal punto di vista REST, a causa del vincolo stateless.)

  3. I tuoi utenti non si preoccupano del perché il problema esiste. Vogliono sapere solo se c'è successo o errore, e probabilmente un messaggio di errore che possono capire, ad esempio: "Scusa, ma non siamo riusciti a salvare il tuo post"., Ecc ...

    Le intestazioni di stato HTTP sono le intestazioni standard. Tutto il resto dovrebbe essere nel corpo, penso. Una singola intestazione non è sufficiente per descrivere ad esempio messaggi di errore multilingue dettagliati.

  4. Il vincolo stateless (insieme ai vincoli di cache e di sistema stratificato) assicura che il servizio sia scalabile correttamente. Sicuramente non vorrai mantenere milioni di sessioni sul server, quando puoi fare lo stesso sui client ...

    Il client di terze parti ottiene un token di accesso se l'utente concede l'accesso utilizzando il client principale. Successivamente, il client di terze parti invia il token di accesso ad ogni richiesta. Esistono soluzioni più complicate, ad esempio è possibile firmare ogni singola richiesta, ecc. Per ulteriori dettagli, consultare il manuale OAuth.

Letteratura correlata


  1. Usa il post quando non sai come sarà il nuovo URI della risorsa (creerai un nuovo utente, l'applicazione assegnerà al nuovo utente il suo id), PUT per l'aggiornamento o la creazione di risorse che sai come verranno rappresentate (esempio : PUT /myfiles/thisismynewfile.txt)
  2. restituisce la descrizione dell'errore nel corpo del messaggio
  3. È possibile utilizzare l'autenticazione HTTP (se sufficiente) I servizi Web dovrebbero essere stateles

In poche parole, lo stai facendo completamente all'indietro.

Non dovresti avvicinarti a questo da quali URL dovresti utilizzare. Gli URL verranno effettivamente "gratuitamente" una volta che avrai deciso quali risorse sono necessarie per il tuo sistema E come rappresenterai quelle risorse e le interazioni tra le risorse e lo stato dell'applicazione.

Per citare Roy Fielding

Un'API REST deve dedicare quasi tutto il suo sforzo descrittivo alla definizione dei tipi di supporto utilizzati per rappresentare le risorse e guidare lo stato dell'applicazione, o nella definizione di nomi di relazione estesi e / o markup abilitato per ipertesti per i tipi di media standard esistenti. Qualsiasi sforzo impiegato per descrivere quali metodi utilizzare su quali URI di interesse dovrebbero essere interamente definiti nell'ambito delle regole di elaborazione per un tipo di supporto (e, nella maggior parte dei casi, già definito dai tipi di media esistenti). [Il fallimento qui implica che le informazioni fuori banda stanno guidando l'interazione invece dell'ipertesto.]

La gente inizia sempre con gli URI e pensa che questa sia la soluzione, e quindi tende a perdere un concetto chiave nell'architettura REST, in particolare, come citato sopra, "Il fallimento qui implica che le informazioni fuori banda stanno guidando l'interazione invece dell'ipertesto. "

Ad essere onesti, molti vedono un sacco di URI e alcuni GET e PUT e POST e pensano che REST sia facile. REST non è facile. RPC su HTTP è facile, spostando i blob di dati avanti e indietro attraverso i payload HTTP è facile. REST, tuttavia, va oltre. REST è protocollo indipendente. HTTP è molto popolare e adatto ai sistemi REST.

REST vive nei tipi di media, nelle loro definizioni e in che modo l'applicazione guida le azioni disponibili per tali risorse tramite ipertesto (collegamenti, in modo efficace).

Ci sono diversi punti di vista sui tipi di media nei sistemi REST. Alcuni preferiscono carichi utili specifici per le applicazioni, mentre altri preferiscono elevare tipi di media esistenti a ruoli appropriati per l'applicazione. Ad esempio, da un lato si hanno schemi XML specifici progettati per la propria applicazione rispetto all'uso di qualcosa come XHTML come rappresentazione, magari attraverso microformati e altri meccanismi.

Entrambi gli approcci hanno il loro posto, a mio avviso, l'XHTML funziona molto bene in scenari che si sovrappongono sia al web umano che a quello della macchina, mentre i primi, più specifici tipi di dati, mi fanno sentire meglio a facilitare le interazioni macchina-macchina. Trovo che l'elevazione dei formati merceologici possa rendere la negoziazione dei contenuti potenzialmente difficile. "application / xml + yourresource" è molto più specifico come un tipo di media di "application / xhtml + xml", in quanto quest'ultimo può essere applicato a molti payload che possono o meno essere qualcosa a cui un client macchina è realmente interessato, né può determinare senza introspezione.

Tuttavia, XHTML funziona molto bene (ovviamente) nel web umano in cui i browser Web e il rendering sono molto importanti.

L'applicazione ti guiderà in questo tipo di decisioni.

Parte del processo di progettazione di un sistema REST è la scoperta delle risorse di prima classe nel sistema, insieme alla derivata, risorse di supporto necessarie per supportare le operazioni sulle risorse primarie. Una volta scoperte le risorse, quindi la rappresentazione di tali risorse, nonché i diagrammi di stato che mostrano il flusso di risorse tramite ipertesto all'interno delle rappresentazioni, perché la prossima sfida.

Ricordiamo che ogni rappresentazione di una risorsa, in un sistema ipertestuale, combina sia la rappresentazione effettiva della risorsa che le transizioni di stato disponibili per la risorsa. Considerare ogni risorsa un nodo in un grafico, con i collegamenti come linee che lasciano il nodo ad altri stati. Questi collegamenti informano i clienti non solo su ciò che può essere fatto, ma su ciò che è necessario affinché vengano eseguiti (poiché un buon collegamento combina l'URI e il tipo di supporto richiesto).

Ad esempio, potresti avere:

<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

La documentazione parlerà del campo rel denominato "users" e del tipo di media di "application / xml + youruser".

Questi collegamenti possono sembrare ridondanti, stanno tutti parlando allo stesso URI, praticamente. Ma non lo sono.

Questo perché per la relazione "utenti", quel collegamento sta parlando della collezione di utenti, e puoi usare l'interfaccia uniforme per lavorare con la raccolta (GET per recuperarli tutti, CANCELLA per eliminarli tutti, ecc.)

Se si POST a questo URL, sarà necessario passare un documento "application / xml + usercollection", che conterrà probabilmente solo una singola istanza utente all'interno del documento in modo da poter aggiungere l'utente, o forse, per aggiungerne diversi a una volta. Forse la tua documentazione suggerirà che puoi semplicemente passare un singolo tipo di utente, invece della raccolta.

Puoi vedere cosa l'applicazione richiede per eseguire una ricerca, come definito dal link "cerca" ed è mediatype. La documentazione per il tipo di media di ricerca ti dirà come si comporta e cosa aspettarsi come risultati.

Il takeaway qui, tuttavia, è che gli URI stessi sono fondamentalmente poco importanti. L'applicazione ha il controllo degli URI, non dei client. Oltre a pochi "punti di ingresso", i vostri clienti dovrebbero fare affidamento sugli URI forniti dall'applicazione per il suo lavoro.

Il cliente ha bisogno di sapere come manipolare e interpretare i tipi di media, ma non ha molto bisogno di preoccuparsi di dove va.

Questi due collegamenti sono semanticamente identici negli occhi dei clienti:

<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

Quindi concentrati sulle tue risorse. Concentrati sulle transizioni di stato nell'applicazione e su come ottenerle.


Per gli esempi che hai dichiarato, userei il seguente:

activate_login

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

cambia la password

PUT /passwords (questo presuppone che l'utente sia autenticato)

add_credit

POST /credits (questo presuppone che l'utente sia autenticato)

Per gli errori, restituiresti l'errore nel corpo nel formato in cui hai ricevuto la richiesta, quindi se ricevi:

DELETE /users/1.xml

Dovresti rispedire la risposta in XML, lo stesso sarebbe vero per JSON ecc ...

Per l'autenticazione è necessario utilizzare l'autenticazione http.


Vorrei suggerire (come prima passata) che PUT dovrebbe essere usato solo per aggiornare le entità esistenti. POST dovrebbe essere usato per crearne di nuovi. vale a dire

/api/users     when called with PUT, creates user record

non mi sembra giusto Il resto della tua prima sezione (uso del verbo re) sembra logico, comunque.


1. Hai la giusta idea su come progettare le tue risorse, IMHO. Non cambierei nulla.

2. Piuttosto che cercare di estendere HTTP con più verbi, considera a cosa si possono ridurre i verbi proposti in termini di metodi e risorse HTTP di base. Ad esempio, invece di un verbo activate_login , puoi impostare risorse come: /api/users/1/login/active che è un semplice booleano. Per attivare un login, basta PUT un documento lì che dice 'true' o 1 o qualsiasi altra cosa. Per disattivare, PUT un documento vuoto o che dice 0 o falso.

Allo stesso modo, per cambiare o impostare le password, basta mettere PUT a /api/users/1/password .

Ogni volta che devi aggiungere qualcosa (come un credito), pensa in termini di POST s. Ad esempio, potresti fare un POST a una risorsa come /api/users/1/credits con un corpo contenente il numero di crediti da aggiungere. Un PUT sulla stessa risorsa potrebbe essere usato per sovrascrivere il valore piuttosto che aggiungere. Un POST con un numero negativo nel corpo si sottrarrebbe e così via.

3. Sconsiglio vivamente di estendere i codici di stato HTTP di base. Se non riesci a trovarne uno che corrisponda esattamente alla tua situazione, scegli il più vicino e inserisci i dettagli dell'errore nel corpo della risposta. Inoltre, ricorda che le intestazioni HTTP sono estensibili; la tua applicazione può definire tutte le intestazioni personalizzate che ti piacciono. Un'applicazione su cui ho lavorato, ad esempio, potrebbe restituire un 404 Not Found in più circostanze. Piuttosto che far analizzare al cliente il corpo della risposta per il motivo, abbiamo appena aggiunto una nuova intestazione, X-Status-Extended , che conteneva le nostre estensioni di codice di stato proprietarie. Quindi potresti vedere una risposta come:

HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

In questo modo un client HTTP come un browser Web saprà ancora cosa fare con il normale codice 404 e un client HTTP più sofisticato può scegliere di guardare l'intestazione X-Status-Extended per informazioni più specifiche.

4. Per l'autenticazione, si consiglia di utilizzare l'autenticazione HTTP, se possibile. Ma IMHO non c'è niente di sbagliato nell'usare l'autenticazione basata sui cookie, se è più facile per te.


Informazioni sui codici restituiti REST: è errato combinare i codici del protocollo HTTP e i risultati REST.

Tuttavia, ho visto molte implementazioni che li mescolavano e molti sviluppatori potrebbero non essere d'accordo con me.

I codici di ritorno HTTP sono correlati alla HTTP Request stessa. Una chiamata REST viene eseguita utilizzando una richiesta Hypertext Transfer Protocol e funziona a un livello inferiore rispetto al metodo REST invocato stesso. REST è un concetto / approccio e il suo risultato è un risultato logico / aziendale , mentre il codice risultato HTTP è uno di trasporto .

Ad esempio, restituire "404 Not found" quando chiami / utenti / è confuso, perché potrebbe significare:

  • URI è sbagliato (HTTP)
  • Nessun utente trovato (REST)

"403 Proibito / Accesso negato" può significare:

  • È necessario un permesso speciale. I browser possono gestirlo chiedendo all'utente / password. (HTTP)
  • Autorizzazioni di accesso errate configurate sul server. (HTTP)
  • Devi essere autenticato (REST)

E l'elenco potrebbe continuare con "Errore del server 500" (un errore generato da Apache / Nginx HTTP o un errore di vincolo di business in REST) ​​o altri errori HTTP, ecc ...

Dal codice, è difficile capire quale sia stata la causa dell'errore, un errore HTTP (trasporto) o un errore REST (logico).

Se la richiesta HTTP è stata eseguita fisicamente con successo, dovrebbe sempre restituire il codice 200, indipendentemente dal fatto che il record sia stato trovato o meno. Perché la risorsa URI è stata trovata ed è stata gestita dal server http. Sì, potrebbe restituire un set vuoto. È possibile ricevere una pagina web vuota con 200 come risultato http, giusto?

Invece di questo, è possibile restituire 200 codice HTTP e semplicemente un JSON con un array / oggetto vuoto oppure utilizzare un flag bool result / success per informare sullo stato dell'operazione eseguita.

Inoltre, alcuni fornitori di servizi Internet potrebbero intercettare le tue richieste e restituirti un codice http 404. Ciò non significa che i tuoi dati non vengano trovati, ma è qualcosa di sbagliato a livello di trasporto.

Dal Wiki :

Nel luglio 2004, il gruppo di telecomunicazioni britannico BT Group ha implementato il sistema di blocco dei contenuti di Cleanfeed, che restituisce un errore 404 a qualsiasi richiesta di contenuto identificato come potenzialmente illegale da Internet Watch Foundation. Altre ISP restituiscono un errore "proibito" HTTP 403 nelle stesse circostanze. La pratica di impiegare errori falsi 404 come mezzo per nascondere la censura è stata segnalata anche in Tailandia e Tunisia. In Tunisia, dove la censura era severa prima della rivoluzione del 2011, la gente si rese conto della natura dei falsi errori 404 e creò un personaggio immaginario chiamato "Ammar 404" che rappresenta "il censore invisibile".





rest