RESTfully concevoir / connecter ou / enregistrer des ressources

122

Je concevais une application Web, puis je me suis arrêté pour réfléchir à la manière dont mon API devrait être conçue comme un service Web RESTful. Pour l'instant, la plupart de mes URI sont génériques et peuvent s'appliquer à diverses applications 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

J'ai le sentiment que je fais beaucoup de mal ici après avoir fouillé sur SO et Google.

En commençant par /logout, peut-être puisque je ne fais vraiment GETrien - il peut être plus approprié de POSTdemander /logout, de détruire la session, puis de GETrediriger. Et le /logoutterme devrait-il rester?

Qu'en est-il /loginet /register. Je pourrais changer /registerpour /registrationmais cela ne modifie pas la façon dont mon service fonctionne fondamentalement - si elle a des problèmes plus profonds.

Je remarque maintenant que je n'expose jamais une /userressource. Peut-être que cela pourrait être utilisé d'une manière ou d'une autre. Par exemple, prenez l'utilisateur myUser:

foo.com/user/myUser

ou

foo.com/user

L'utilisateur final n'a pas besoin de cette verbosité supplémentaire dans l'URI. Cependant, lequel est le plus attrayant visuellement?

J'ai remarqué quelques autres questions ici sur SO à propos de cette activité REST, mais j'apprécierais vraiment quelques conseils sur ce que j'ai présenté ici si possible.

Merci!

METTRE À JOUR:

J'aimerais aussi quelques avis sur:

/user/1

contre

/user/myUserName
Qcom
la source

Réponses:

63

Une chose ressort en particulier comme non REST-ful: l'utilisation d'une requête GET pour se déconnecter.

(à partir de http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Certaines méthodes (par exemple, HEAD, GET, OPTIONS et TRACE) sont définies comme sûres, ce qui signifie qu'elles sont uniquement destinées à la recherche d'informations et ne doivent pas changer l'état du serveur. En d'autres termes, ils ne devraient pas avoir d'effets secondaires, au-delà d'effets relativement inoffensifs tels que la journalisation, la mise en cache, la diffusion de bannières publicitaires ou l'incrémentation d'un compteur Web. [...]

[... La gestion [des requêtes GET] par le serveur n'est en aucune façon limitée techniquement. Par conséquent, une programmation imprudente ou délibérée peut entraîner des changements non négligeables sur le serveur. Ceci est déconseillé, car cela peut causer des problèmes pour la mise en cache Web, les moteurs de recherche et autres agents automatisés [...]

En ce qui concerne la déconnexion et la redirection, vous pourriez avoir un message sur votre URI de déconnexion donnant une réponse 303 redirigeant vers la page de post-déconnexion.

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

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

Modifier pour résoudre les problèmes de conception d'URL:

"Comment concevoir mes ressources?" est une question importante pour moi; "Comment concevoir mes URL?" est une considération dans deux domaines:

Les URL que les utilisateurs verront ne doivent pas être trop laides et significatives si possible; si vous souhaitez que des cookies soient envoyés dans les demandes à certaines ressources mais pas à d'autres, vous souhaiterez structurer vos chemins et chemins de cookies.

Si vous JRandomUsersouhaitez consulter son propre profil et que vous souhaitez que l'URL soit plus jolie que foo.com/user/JRandomUserou foo.com/user/(JRandom's numeric user id here), vous pouvez créer une URL distincte uniquement pour qu'un utilisateur puisse consulter ses propres informations:

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.
                     */

Je revendiquerais l'ignorance beaucoup plus facilement que la sagesse à ce sujet, mais voici quelques considérations sur la conception des ressources:

  1. Consommateur: quelles ressources sont destinées à être visualisées directement dans un navigateur, chargées via XHR ou accessibles par un autre type de client?
  2. Accès / identité: la réponse dépend-elle des cookies ou des référents?
ellisbben
la source
1
Excellente réponse, merci! Si j'allais mettre en œuvre votre suggestion d'URL séparée ( GET foo.com/profile/), est-ce que cela ferait partie, comme momo l'a suggéré, de la couche de présentation? En d'autres termes, que devrait exactement GETrenvoyer cette demande? Une page Web ou du JSON?
Qcom
2
Ah, je pense que je vois maintenant. La réponse de Momo a vraiment clarifié les choses. Ainsi , une API RESTful est conçu pour permettre à de multiples plates - formes à GET, POST, PUTet les DELETEressources. Un site Web n'est qu'une autre plate-forme accédant à l'API. En d'autres termes, la conception d'URL de site Web est complètement différente de la conception d'API RESTful. S'il vous plaît dites-moi si je me trompe toujours haha.
Qcom le
Oui, faites de votre API REST un ensemble d'URL et de votre site Web un ensemble différent. Ensuite, l'URL de votre site Web devrait vous renvoyer le code HTML + Javascript approprié afin que la page fasse des requêtes XmlHttpRequests appropriées à vos URL API pour agir en tant que client.
ellisbben
129

RESTful peut être utilisé comme guide pour la construction d'URL, et vous pouvez créer des sessions et des ressources utilisateurs :

  • GET /session/new obtient la page Web contenant le formulaire de connexion
  • POST /session authentifie les informations d'identification par rapport à la base de données
  • DELETE /session détruit la session et redirige vers /
  • GET /users/new obtient la page Web contenant le formulaire d'inscription
  • POST /users enregistre les informations saisies dans la base de données en tant que nouveau / utilisateur / xxx
  • GET /users/xxx // obtient et restitue les données utilisateur actuelles dans une vue de profil
  • POST /users/xxx // met à jour de nouvelles informations sur l'utilisateur

Ceux-ci peuvent être au pluriel ou au singulier (je ne sais pas lequel est correct). Je l'ai généralement utilisé /userspour une page d'index utilisateur (comme prévu) et /sessionspour voir qui est connecté (comme prévu).

L'utilisation du nom dans l'URL au lieu d'un nombre ( /users/43vs. /users/joe) est généralement motivée par le désir d'être plus convivial avec les utilisateurs ou les moteurs de recherche, et non par des exigences techniques. L'un ou l'autre est bien, mais je vous recommande d'être cohérent.

Je pense que si vous utilisez le registre / connexion / déconnexion ou sign(in|up|out), cela ne fonctionne pas aussi bien avec la terminologie reposante.

ndp
la source
6
Impressionnant! J'aime la façon dont vous nommez ces ressources; c'est assez propre. Bien que, d'après ce que j'ai entendu, ne s'ajoute pas /newà GET /session/non RESTful? Je l' ai entendu dire que les verbes sont généralement laissées aux verbes HTTP ( GET, POST, etc.).
Qcom du
2
@Zach new n'est pas un verbe. Dans ce cas, c'est une sous-ressource de session.
Kugel
Comment déterminer quelle session supprimer dans DELETE / session? Curl n'envoie ni cookies ni paramètres dans la requête DELETE. Je suppose - juste pour utiliser DELETE / session / sessionId? Une autre question est de savoir comment renvoyer l'identifiant de session dans POST / session et dans quel format.
Tvaroh
9
Le repos est en effet un moyen de se rendre malheureux et de perdre du temps sur des choses qui ne comptent pas du tout.
Jian Chen
6
Personnellement je n'aime pas l'idée d'avoir les routes qui renvoient le formulaire (/ new). Cela rompt la séparation entre la vue et la logique métier. Tha dit, sans les / nouveaux itinéraires, celui suggéré semble parfait.
Scadge
60

Les sessions ne sont pas RESTful

  • Oui je sais. Cela se fait, généralement avec OAuth, mais en réalité, les sessions ne sont pas RESTful. Vous ne devriez pas avoir de ressource / login / logout principalement parce que vous ne devriez pas avoir de sessions.

  • Si vous allez le faire, faites-le RESTful. Les ressources sont des noms et / login et / logout ne sont pas des noms. J'irais avec / session. Cela fait de la création et de la suppression une action plus naturelle.

  • POST vs GET pour les sessions est facile. Si vous envoyez un utilisateur / mot de passe en tant que variables, j'utiliserais POST car je ne veux pas que le mot de passe soit envoyé dans le cadre de l'URI. Il apparaîtra dans les journaux et sera éventuellement exposé sur le fil. Vous courez également le risque de voir le logiciel échouer sur les limites des arguments GET.

  • J'utilise généralement Basic Auth ou no Auth avec les services REST.

Créer des utilisateurs

  • C'est une ressource, vous ne devriez donc pas avoir besoin de / vous inscrire.

    • POST / user - Crée un utilisateur si le demandeur ne peut pas spécifier l'id
    • PUT / user / xxx - Créer ou mettre à jour un utilisateur en supposant que vous connaissez l'id au préalable
    • GET / user - répertorie x identifiants utilisateur
    • GET / user / xxx - Obtient les détails de l'utilisateur avec l'ID xxx
    • DELETE / user / xxx - Supprimer l'utilisateur avec l'ID xxx
  • Quel type d'identification utiliser est une question difficile. Vous devez penser à l'application de l'unicité, à la réutilisation d'anciens identifiants qui ont été SUPPRIMÉS. Par exemple, vous ne voulez pas utiliser ces identifiants comme clés étrangères sur un backend si les identifiants vont être recyclés (si possible). Vous pouvez cependant rechercher une conversion d'identifiant externe / interne afin d'atténuer les exigences du backend.

régime bouddha
la source
6
C'est la meilleure réponse. / login et / logout ne sont pas des ressources et brisent l'idée de REST.
wle8300
5
Authentification! = Session
dietbuddha
1
Oui, la thèse de Fielding déclare dans la section 5.1.3 que «[s] ession state est [...] entièrement conservé sur le client». De plus, je dirais que, idéalement, l'authentification devrait également être sans état côté serveur, c'est-à-dire qu'au lieu de stocker des «tickets d'authentification» actifs dans une base de données, le serveur devrait être en mesure de vérifier un justificatif d'authentification basé uniquement sur le justificatif lui-même, par exemple en utilisant un jeton cryptographique autonome en conjonction avec une clé privée. Donc, au lieu d'une ressource / session, on pourrait introduire une ressource / authentication, mais cela ne résout pas vraiment le problème non plus ...
coureur
En fait, / login et / logout sont des noms. Je suppose que vous pensez à / log_in et / log_out.
TiggerToo
21

Je vais simplement parler de mon expérience d'intégration de divers services Web REST pour mes clients, qu'ils soient utilisés pour des applications mobiles ou pour la communication de serveur à serveur ainsi que pour la construction d'API REST pour d'autres. Voici quelques observations que j'ai recueillies à partir de l'API REST d'autres personnes ainsi que celles que nous avons construites nous-mêmes:

  • Quand nous disons API, cela fait normalement référence à un ensemble d'interfaces de programmation et pas nécessairement à la couche de présentation. REST est également centré sur les données et non sur la présentation. Cela dit, la plupart des données REST renvoient des données sous la forme de JSON ou XML et renvoient rarement une couche de présentation spécifique. Cette caractéristique (de renvoyer des données et non la page Web directe) a donné la capacité de REST à faire la livraison multi-canaux. Cela signifie que le même service Web peut être rendu en HTML, iOS, Android ou même utilisé comme combinaison de serveur à serveur.
  • Il est très rare de combiner HTML et REST en tant qu'URL. Par défaut, REST sont des pensées en tant que services et n'ayant pas de couche de présentation. C'est le travail de ceux qui utilisent les webservices de restituer les données des services qu'ils appellent selon ce qu'ils veulent. À ce stade, votre URL ci-dessous n'est pas conforme à la plupart des conceptions basées sur REST que j'ai rencontrées jusqu'à présent (ni aux normes telles que celles qui proviennent de Facebook ou Twitter)
GET / register // obtient la page Web contenant le formulaire d'inscription
  • Dans la continuité du point précédent, il est également rare (et je ne l'ai pas rencontré) pour un service basé sur REST d'effectuer une redirection telle que celles suggérées ci-dessous:
GET / logout // détruit la session et redirige vers /
POST / login // authentifie les informations d'identification par rapport à la base de données et soit redirige la maison avec une nouvelle session, soit redirige vers / login
 

Comme REST est conçu comme des services, des fonctions telles que la connexion et la déconnexion renvoient normalement un résultat de réussite / échec (normalement au format de données JSON ou XML) que le consommateur interpréterait alors. Une telle interprétation pourrait inclure la redirection vers la page Web appropriée comme vous l'avez mentionné

  • Dans REST, l'URL indique les actions entreprises. Pour cette raison, nous devons éliminer le plus d'ambiguïté possible. Bien qu'il soit légitime dans votre cas d'avoir à la fois GET et POST qui ont le même chemin (tel que / register) qui effectuent des actions différentes, une telle conception introduit une ambiguïté dans les services fournis et peut dérouter le consommateur de vos services. Par exemple, les URL telles que celle que vous introduisez ci-dessous ne sont pas idéales pour les services basés sur REST
GET / register // obtient la page Web contenant le formulaire d'inscription
POST / register // enregistre les informations saisies dans la base de données en tant que nouveau / user / xxx

Ce sont quelques points de ce que j'ai traité. J'espère que cela pourra vous fournir des informations.

Maintenant, pour la mise en œuvre de votre REST, voici l'implémentation typique que j'ai rencontrée:

  • GET / déconnexion  
    

    Exécutez la déconnexion dans le backend et retournez JSON pour indiquer le succès / l'échec de l'opération

  • POST / connexion
    

    Soumettez les informations d'identification au backend. Renvoie succès / échec. En cas de succès, il renverra également le jeton de session ainsi que les informations de profil.

  • POST / S'inscrire
    

    Soumettez l'enregistrement au backend. Renvoie succès / échec. En cas de succès, normalement traité comme une connexion réussie ou vous pouvez choisir de vous enregistrer en tant que service distinct

  • GET / utilisateur / xxx
    

    Obtenir le profil utilisateur et renvoyer le format de données JSON pour le profil de l'utilisateur

  • POST / utilisateur / xxx 
    // renommé en 
    POST / updateUser / xxx
    

    Publiez les informations de profil mises à jour au format JSON et mettez à jour les informations dans le backend. Renvoyer le succès / échec à l'appelant

momo
la source
3
Oui, si vous intégrez votre API REST à une application basée sur HTML (via Javascript et AJAX), vous bénéficierez d'énormes avantages car JSON est analysé nativement par Javascript. Dans Android / Java, JSON est également plus facile et plus simple à analyser que XML.
momo
15
GET / logout est dangereux. GET doit être idempotent. Les navigateurs aiment également pré-extraire <a> hrefs, ce qui vous déconnectera!
Kugel
4

Je crois que c'est une approche RESTful de l'authentification. Pour la connexion, vous utilisez HttpPut. Cette méthode HTTP peut être utilisée pour la création lorsque la clé est fournie et que les appels répétés sont idempotents. Pour LogOff, vous spécifiez le même chemin sous la HttpDeleteméthode. Aucun verbe utilisé. Pluralisation correcte de la collection. Les méthodes HTTP prennent en charge l'objectif.

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

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

Si vous le souhaitez, vous pouvez remplacer le courant par actif.

Chad Kuehn
la source
1

Je recommanderais d'utiliser une URL de compte utilisateur similaire à Twitter où l'URL du compte utilisateur serait quelque chose foo.com/myUserNamecomme vous pouvez accéder à mon compte Twitter avec l'URL https://twitter.com/joelbyler

Je ne suis pas d'accord sur la déconnexion nécessitant un POST. Dans le cadre de votre API, si vous allez maintenir une session, un identifiant de session sous la forme d'un UUID peut être quelque chose qui peut être utilisé pour garder une trace d'un utilisateur et confirmer que l'action entreprise est autorisée. Ensuite, même un GET peut transmettre l'ID de session à la ressource.

En bref, je vous recommande de rester simple, les URL doivent être courtes et mémorables.

joelbyler
la source
La question concerne les ressources API. Votre réponse concerne la couche de présentation.
Henno