RESTfully progettare / accedere o / registrare risorse?


122

Stavo progettando un'app Web e poi mi sono fermato a pensare a come la mia API dovrebbe essere progettata come servizio Web RESTful. Per ora, la maggior parte dei miei URI sono generici e potrebbero essere applicati a varie app Web:

GET  /logout   // destroys session and redirects to /
GET  /login    // gets the webpage that has the login form
POST /login    // authenticates credentials against database and either redirects home with a new session or redirects back to /login
GET  /register // gets the webpage that has the registration form
POST /register // records the entered information into database as a new /user/xxx
GET  /user/xxx // gets and renders current user data in a profile view
POST /user/xxx // updates new information about user

Ho la sensazione che sto facendo molto di sbagliato qui dopo aver dato un'occhiata a SO e google.

Iniziando con /logout, forse dal momento che non ho davvero GETnulla, potrebbe essere più appropriato POSTuna richiesta /logout, distruggere la sessione e quindi GETil reindirizzamento. E il /logouttermine dovrebbe restare?

Che mi dici di /logine /register. Potrei cambiare /registerin, /registrationma ciò non cambia il modo in cui funziona fondamentalmente il mio servizio, se ha problemi più profondi.

Noto ora che non ho mai esposto una /userrisorsa. Forse potrebbe essere utilizzato in qualche modo. Ad esempio, prendi l'utente myUser:

foo.com/user/myUser

o

foo.com/user

L'utente finale non richiede quel livello di dettaglio extra nell'URI. Tuttavia, quale è visivamente più accattivante?

Ho notato alcune altre domande qui su SO su questo business REST, ma apprezzerei davvero alcune indicazioni su ciò che ho esposto qui, se possibile.

Grazie!

AGGIORNARE:

Vorrei anche alcune opinioni su:

/user/1

vs

/user/myUserName

Risposte:


63

Una cosa spicca in particolare come non REST-ful: l'uso di una richiesta GET per disconnettersi.

(da http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Alcuni metodi (ad esempio, HEAD, GET, OPTIONS e TRACE) sono definiti sicuri, il che significa che sono intesi solo per il recupero delle informazioni e non devono modificare lo stato del server. In altre parole, non dovrebbero avere effetti collaterali, al di là di effetti relativamente innocui come la registrazione, la memorizzazione nella cache, la pubblicazione di banner pubblicitari o l'incremento di un contatore web. [...]

[...] L'elaborazione [delle richieste GET] da parte del server non è tecnicamente limitata in alcun modo. Pertanto, una programmazione imprudente o deliberata può causare modifiche non banali sul server. Questo è sconsigliato, perché può causare problemi alla cache Web, ai motori di ricerca e ad altri agenti automatici [...]

Per quanto riguarda la disconnessione e il reindirizzamento, è possibile che un messaggio al tuo URI di disconnessione dia una risposta 303 che reindirizza alla pagina di post-logout.

http://en.wikipedia.org/wiki/Post/Redirect/Get

http://en.wikipedia.org/wiki/HTTP_303

Modifica per affrontare i problemi di progettazione dell'URL:

"Come progetto le mie risorse?" è una domanda importante per me; "come faccio a progettare i miei URL?" è una considerazione in due aree:

Gli URL che gli utenti vedranno non dovrebbero essere troppo brutti e significativi se possibile; se vuoi che i cookie vengano inviati nelle richieste ad alcune risorse ma non ad altre, ti consigliamo di strutturare i tuoi percorsi e percorsi dei cookie.

Se JRandomUservuole guardare il suo profilo e vuoi che l'URL sia più carino di foo.com/user/JRandomUsero foo.com/user/(JRandom's numeric user id here), potresti creare un URL separato solo per consentire a un utente di guardare le proprie informazioni:

GET foo.com/profile /*examines cookies to figure out who 
                     * is logged in (SomeUser) and then 
                     * displays the same response as a
                     * GET to foo.com/users/SomeUser.
                     */

Affermerei l'ignoranza molto più prontamente della saggezza su questo argomento, ma qui ci sono alcune considerazioni sulla progettazione delle risorse:

  1. Consumatore: quali risorse devono essere visualizzate direttamente in un browser, caricate tramite XHR o accessibili da un altro tipo di client?
  2. Accesso / identità: la risposta dipende dai cookie o dai referrer?

1
Ottima risposta, grazie! Se avessi intenzione di implementare il tuo suggerimento URL separato ( GET foo.com/profile/), farebbe parte, come suggerito da momo, del livello di presentazione? In altre parole, cosa dovrebbe GETrestituire esattamente quella richiesta? Una pagina web o un po 'di JSON?
Qcom

2
Ah, credo di capire ora. La risposta di Momo ha davvero chiarito le cose. Quindi un API RESTful è costruito per consentire a più piattaforme per GET, POST, PUT, e DELETEle risorse. Un sito Web è solo un'altra piattaforma che accede all'API. In altre parole, la progettazione dell'URL del sito Web è completamente diversa dalla progettazione dell'API RESTful. Per favore dimmi se ho ancora torto ahah.
Qcom

Sì, rendi la tua API REST un insieme di URL e il tuo sito web un insieme diverso. Quindi l'URL del tuo sito web dovrebbe restituirti HTML + Javascript appropriato in modo che la pagina effettui le richieste XmlHttpRequests appropriate agli URL API per agire come client.
ellisbben

129

RESTful può essere utilizzato come linea guida per la creazione di URL e puoi creare sessioni e risorse per gli utenti :

  • GET /session/new ottiene la pagina web che ha il modulo di accesso
  • POST /session autentica le credenziali sul database
  • DELETE /session distrugge la sessione e reindirizza a /
  • GET /users/new ottiene la pagina web che contiene il modulo di registrazione
  • POST /users registra le informazioni inserite nel database come nuovo / utente / xxx
  • GET /users/xxx // ottiene ed esegue il rendering dei dati utente correnti in una vista profilo
  • POST /users/xxx // aggiorna le nuove informazioni sull'utente

Questi possono essere plurali o singolari (non sono sicuro quale sia corretto). Di solito ho usato /usersper una pagina di indice utente (come previsto) e/sessions per vedere chi ha effettuato l'accesso (come previsto).

Utilizzando il nome nell'URL invece di un numero ( /users/43vs./users/joe ) è solitamente guidato dal desiderio di essere più amichevoli per gli utenti o i motori di ricerca, non da requisiti tecnici. O va bene, ma ti consiglio di essere coerente.

Penso che se vai con il registro / login / logout o sign(in|up|out), non funziona altrettanto bene con la terminologia riposante.


6
Eccezionale! Mi piace come hai chiamato queste risorse; è abbastanza pulito. Anche se, da quello che ho sentito, non si aggiunge /newa GET /session/non RESTful? Ho sentito dire che i verbi sono in genere a sinistra per i verbi HTTP ( GET, POST, ecc).
Qcom

2
@Zach new non è un verbo. In questo caso è una sottorisorsa di sessione.
Kugel

Come determinare quale sessione eliminare in DELETE / session? Curl non invia né cookie né alcun parametro nella richiesta di CANCELLAZIONE. Presumo - solo per usare DELETE / session / sessionId? Un'altra domanda è come restituire l'ID di sessione in POST / sessione e in quale formato.
Tvaroh

9
Restful è davvero un modo per rendersi infelici e perdere tempo in cose che non contano affatto.
Jian Chen

6
Personalmente non mi piace l'idea di avere dei percorsi che restituiscano la forma (/ nuovo). Questo rompe la separazione tra la vista e la logica aziendale. Detto questo, senza le / nuove vie quella suggerita sembra perfetta.
Scadge

60

Le sessioni non sono riposanti

  • Si, lo so. Viene fatto, di solito con OAuth, ma in realtà le sessioni non sono RESTful. Non dovresti avere una risorsa / login / logout principalmente perché non dovresti avere sessioni.

  • Se hai intenzione di farlo, rendilo RIPOSO. Le risorse sono nomi e / login e / logout non sono nomi. Vorrei andare con / session. Ciò rende la creazione e la cancellazione un'azione più naturale.

  • POST vs GET per le sessioni è facile. Se invii utente / password come variabili, utilizzerei POST perché non voglio che la password venga inviata come parte dell'URI. Apparirà nei registri e probabilmente sarà esposto sul filo. Corri anche il rischio che il software si guasti sulle limitazioni degli argomenti GET.

  • In genere utilizzo l'autenticazione di base o nessuna autenticazione con i servizi REST.

Creazione di utenti

  • È una risorsa, quindi non dovresti aver bisogno di / registrarti.

    • POST / utente: crea un utente se il richiedente non è in grado di specificare l'id
    • PUT / user / xxx - Crea o aggiorna un utente supponendo che tu conosca l'ID in anticipo
    • GET / utente - elenca x ID utente
    • GET / user / xxx - Ottiene i dettagli dell'utente con ID xxx
    • DELETE / user / xxx - Elimina l'utente con ID xxx
  • Quale tipo di ID usare è una domanda difficile. Devi pensare a rafforzare l'unicità, al riutilizzo di vecchi ID che erano DELETEd. Ad esempio, non si desidera utilizzare questi ID come chiavi esterne su un backend se gli ID verranno riciclati (se possibile). Tuttavia, è possibile cercare la conversione dell'ID esterno / interno per mitigare i requisiti di backend.


6
Questa è la migliore risposta. / login e / logout non sono risorse e interrompono l'idea di REST.
wle8300

5
Autenticazione! = Sessione
dietbuddha

1
Sì, la tesi di Fielding afferma nella sezione 5.1.3 che "[s] ession state è [...] mantenuto interamente sul cliente". Inoltre, direi che, idealmente, l'autenticazione dovrebbe anche essere senza stato sul lato server, ovvero, invece di memorizzare "ticket di autenticazione" attivi in ​​un database, il server dovrebbe essere in grado di verificare una credenziale di autenticazione basandosi solo sulla credenziale stessa, ad esempio utilizzando un token crittografico autonomo insieme a una chiave privata. Quindi, invece di una risorsa / session si potrebbe introdurre una risorsa / authentication, ma in realtà non risolve nemmeno il problema ...
raner

In realtà, / login e / logout sono sostantivi. Presumo tu stia pensando a / log_in e / log_out.
TiggerToo

21

Parlerò semplicemente della mia esperienza nell'integrazione di vari servizi Web REST per i miei clienti, sia che vengano utilizzati per app mobili o per comunicazioni da server a server, sia per la creazione di API REST per altri. Ecco alcune osservazioni che ho raccolto dall'API REST di altre persone e da quelle che abbiamo costruito noi stessi:

  • Quando diciamo API, normalmente si riferisce al set di interfacce di programmazione e non è necessario il livello di presentazione. REST è anche incentrato sui dati e non basato sulla presentazione. Detto questo, la maggior parte dei REST restituisce dati sotto forma di JSON o XML e raramente restituisce uno specifico livello di presentazione. Questa caratteristica (di restituire i dati e non la pagina web diretta) ha dato a REST la capacità di eseguire consegne multicanale. Ciò significa che lo stesso servizio web può essere visualizzato in HTML, iOS, Android o anche utilizzato come combinazione da server a server.
  • È molto raro combinare HTML e REST come URL. Per impostazione predefinita, i REST sono pensieri come servizi e non hanno un livello di presentazione. È compito di chi consuma i servizi web rendere i dati dei servizi che chiama in base a ciò che vuole. A quel punto il tuo URL qui sotto non è conforme alla maggior parte del design basato su REST che ho incontrato finora (né agli standard come quelli che provengono da Facebook o Twitter)
GET / register // ottiene la pagina web che ha il modulo di registrazione
  • Continuando dal punto precedente, è anche raro (e non ho incontrato) che il servizio basato su REST esegua il reindirizzamento come quelli suggeriti di seguito:
GET / logout // distrugge la sessione e reindirizza a /
POST / login // autentica le credenziali sul database e reindirizza a casa con una nuova sessione o reindirizza a / login
 

Poiché REST sono progettati come servizi, funzioni come login e logout normalmente restituiscono risultati di successo / fallimento (normalmente in formato dati JSON o XML) che quindi il consumatore interpreterà. Tale interpretazione potrebbe includere il reindirizzamento alla pagina web appropriata come hai menzionato

  • In REST, l'URL indica le azioni intraprese. Per questo motivo, dovremmo rimuovere quanta più ambiguità possibile. Sebbene sia legittimo nel tuo caso avere sia GET che POST che hanno lo stesso percorso (come / register) che eseguono un'azione diversa, tale design introduce ambiguità nei servizi forniti e può confondere il consumatore dei tuoi servizi. Ad esempio, gli URL come quello introdotto di seguito non sono ideali per i servizi basati su REST
GET / register // ottiene la pagina web che ha il modulo di registrazione
POST / register // registra le informazioni immesse nel database come nuovo / utente / xxx

Questi sono alcuni punti di ciò che ho trattato. Spero che possa fornire alcuni spunti per te.

Per quanto riguarda l'implementazione del tuo REST, queste sono le tipiche implementazioni che ho riscontrato:

  • GET / logout  
    

    Esegui il logout nel back-end e restituisci JSON per indicare il successo / fallimento dell'operazione

  • POST / login
    

    Invia le credenziali al backend. Restituisce successo / fallimento. In caso di successo, normalmente restituirà anche il token di sessione e le informazioni sul profilo.

  • POST / registrati
    

    Invia la registrazione al backend. Restituisce successo / fallimento. In caso di esito positivo, normalmente viene considerato come un accesso riuscito oppure è possibile scegliere di effettuare la registrazione come un servizio distinto

  • GET / utente / xxx
    

    Ottieni il profilo utente e restituisci il formato dati JSON per il profilo dell'utente

  • POST / utente / xxx 
    // rinominato in 
    POST / updateUser / xxx
    

    Pubblica le informazioni del profilo aggiornate in formato JSON e aggiorna le informazioni nel back-end. Restituire il successo / fallimento al chiamante


3
Sì, se stai integrando la tua API REST con un'app basata su HTML (tramite Javascript e AJAX), otterrai enormi vantaggi poiché JSON viene analizzato in modo nativo da Javascript. In Android / Java, JSON è più facile e diretto da analizzare anche rispetto a XML.
momo

15
GET / logout è pericoloso. GET dovrebbe essere idempotente. Anche ai browser piace precaricare <a> hrefs, che ti disconnetterà!
Kugel

4

Credo che questo sia un approccio RESTful all'autenticazione. Per LogIn usi HttpPut. Questo metodo HTTP può essere utilizzato per la creazione quando viene fornita la chiave e le chiamate ripetute sono idempotenti. Per LogOff, specificare lo stesso percorso sotto il HttpDeletemetodo. Nessun verbo utilizzato. Corretta pluralizzazione della raccolta. I metodi HTTP supportano lo scopo.

[HttpPut]
[Route("sessions/current")]
public IActionResult LogIn(LogInModel model) { ... }

[HttpDelete]
[Route("sessions/current")]
public IActionResult LogOff() { ... }

Se lo si desidera, è possibile sostituire la corrente con attiva.


1

Consiglierei di utilizzare un URL dell'account utente simile a Twitter in cui l'URL dell'account dell'utente sarebbe qualcosa di simile foo.com/myUserNamea quello che puoi ottenere al mio account Twitter con l'URL https://twitter.com/joelbyler

Non sono d'accordo sul logout che richiede un POST. Come parte della tua API, se intendi mantenere una sessione, un ID di sessione sotto forma di UUID potrebbe essere qualcosa che può essere utilizzato per tenere traccia di un utente e confermare che l'azione intrapresa è autorizzata. Quindi anche un GET può passare l'ID di sessione alla risorsa.

In breve, ti consiglio di mantenerlo semplice, gli URL dovrebbero essere brevi e facili da ricordare.


La domanda riguarda le risorse API. La tua risposta riguarda il livello di presentazione.
Henno
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.