Question Comprendre REST: Verbes, codes d'erreur et authentification


Je suis à la recherche d'un moyen d'intégrer les API autour des fonctions par défaut dans mes applications Web, bases de données et CMS.

J'ai regardé autour de moi et j'ai trouvé plusieurs cadres "squelettes". En plus des réponses à ma question, il y a Tonique, un cadre REST que j'apprécie car il est très léger.

J'aime REST le meilleur pour sa simplicité, et je voudrais créer une architecture API basée dessus. J'essaie de comprendre les principes de base et je ne l'ai pas encore bien compris. Par conséquent, un certain nombre de questions.

1. Est-ce que je comprends bien?

Dis que j'ai une ressource "utilisateurs". Je pourrais mettre en place un certain nombre d'URI comme ça:

/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

est-ce une représentation correcte d'une architecture RESTful jusqu'à présent?

2. J'ai besoin de plus de verbes

Créer, Mettre à jour et Supprimer peut être suffisant en théorie, mais en pratique, j'aurai besoin de beaucoup plus de verbes. Je réalise que ce sont des choses qui pourrait être intégré dans une demande de mise à jour, mais ce sont des actions spécifiques qui peuvent avoir des codes de retour spécifiques et je ne voudrais pas les jeter tous en une seule action.

Certains qui viennent à l'esprit dans l'exemple de l'utilisateur sont:

activate_login
deactivate_login
change_password
add_credit

Comment exprimer des actions telles que celles d'une architecture d'URL RESTful?

Mon instinct serait de faire un appel GET à une URL comme

/api/users/1/activate_login 

et attendez un code d'état.

Cela dévie de l'idée d'utiliser des verbes HTTP, cependant. Qu'est-ce que tu penses?

3. Comment retourner les messages d'erreur et les codes

Une grande partie de la beauté de REST provient de son utilisation des méthodes HTTP standard. En cas d'erreur, j'émets un en-tête avec un code d'erreur 3xx, 4xx ou 5xx. Pour une description détaillée de l'erreur, je peux utiliser le corps (non?). Jusqu'ici tout va bien. Mais quel serait le moyen de transmettre un code d'erreur propriétaire cela est plus détaillé dans la description de ce qui s'est mal passé (par exemple, «échec de connexion à la base de données» ou «erreur de connexion à la base de données»)? Si je le mets dans le corps avec le message, je dois l'analyser ensuite. Y a-t-il un en-tête standard pour ce genre de chose?

4. Comment faire l'authentification

  • À quoi ressemblerait une authentification basée sur une clé API suivant les principes REST?
  • Existe-t-il des points forts contre l'utilisation de sessions lors de l'authentification d'un client REST, à part le fait qu'il s'agit d'une violation flagrante du principe REST? :) (seulement la moitié blague ici, l'authentification basée sur la session serait bien avec mon infrastructure existante.)

563
2018-01-04 19:55


origine


Réponses:


J'ai remarqué cette question avec quelques jours de retard, mais je pense que je peux ajouter quelque chose. J'espère que cela peut être utile pour votre entreprise RESTful.


Point 1: Est-ce que je comprends bien?

Vous avez bien compris. C'est une représentation correcte d'une architecture RESTful. Vous pouvez trouver la matrice suivante de Wikipédia très utile pour définir vos noms et verbes:


Lorsque vous traitez avec un Collection URI comme: http://example.com/resources/

  • OBTENIR: Listez les membres de la collection, avec leurs URI membres pour une navigation ultérieure. Par exemple, listez toutes les voitures à vendre.

  • METTRE: Définition définie comme "remplacer la collection entière par une autre collection".

  • POSTER: Créez une nouvelle entrée dans la collection où l'ID est attribué automatiquement par la collection. L'ID créé est généralement inclus dans les données renvoyées par cette opération.

  • EFFACER: Définition définie comme "supprimer la collection entière".


Lorsque vous traitez avec un Membre URI comme: http://example.com/resources/7HOU57Y

  • OBTENIR: Récupère une représentation du membre adressé de la collection exprimée dans un type MIME approprié.

  • METTRE: Mettez à jour le membre adressé de la collection ou créez-le avec l'ID spécifié.

  • POSTER: Traite le membre adressé comme une collection à part entière et en crée un nouveau subordonné.

  • EFFACER: Supprime le membre adressé de la collection.


Point 2: J'ai besoin de plus de verbes

En général, lorsque vous pensez avoir besoin de plus de verbes, cela peut signifier que vos ressources doivent être réidentifiées. Rappelez-vous que dans REST, vous agissez toujours sur une ressource ou sur une collection de ressources. Ce que vous choisissez comme ressource est assez important pour votre définition d'API.

Activer / désactiver la connexion: Si vous créez une nouvelle session, vous pouvez considérer "la session" comme la ressource. Pour créer une nouvelle session, utilisez POST pour http://example.com/sessions/ avec les pouvoirs dans le corps. Pour l'expirer, utilisez PUT ou un DELETE (peut-être en fonction de si vous avez l'intention de conserver un historique de session) à http://example.com/sessions/SESSION_ID.

Changer le mot de passe: Cette fois, la ressource est "l'utilisateur". Vous auriez besoin d'un PUT pour http://example.com/users/USER_ID avec les anciens et nouveaux mots de passe dans le corps. Vous agissez sur la ressource "l'utilisateur" et un mot de passe de modification est simplement une demande de mise à jour. C'est assez similaire à l'instruction UPDATE dans une base de données relationnelle.

Mon instinct serait de faire un appel GET   à une URL comme    /api/users/1/activate_login

Cela va à l'encontre d'un principe REST très central: l'utilisation correcte des verbes HTTP. Toute requête GET ne doit jamais laisser d'effets secondaires.

Par exemple, une requête GET ne doit jamais créer une session dans la base de données, renvoyer un cookie avec un nouvel ID de session ou laisser des résidus sur le serveur. Le verbe GET est similaire à l'instruction SELECT dans un moteur de base de données. Rappelez-vous que la réponse à toute requête avec le verbe GET doit pouvoir être mise en mémoire cache lorsqu'elle est demandée avec les mêmes paramètres, exactement comme lorsque vous demandez une page Web statique.


Point 3: Comment retourner les messages d'erreur et les codes 

Considérez les codes d'état HTTP 4xx ou 5xx comme des catégories d'erreur. Vous pouvez élaborer l'erreur dans le corps.

Échec de la connexion à la base de données: / Connexion de base de données incorrecte: En général, vous devez utiliser une erreur 500 pour ces types d'erreurs. C'est une erreur côté serveur. Le client n'a rien fait de mal. 500 erreurs sont normalement considérées comme "réessayables". c'est-à-dire que le client peut réessayer la même demande exacte, et s'attendre à ce qu'il réussisse une fois que les problèmes du serveur auront été résolus. Spécifiez les détails dans le corps, de sorte que le client sera en mesure de fournir un certain contexte pour nous les humains.

L'autre catégorie d'erreurs serait la famille 4xx, qui indique généralement que le client a fait quelque chose de mal. En particulier, cette catégorie d'erreurs indique normalement au client qu'il n'est pas nécessaire de réessayer la demande telle qu'elle est, car elle continuera à échouer de façon permanente. c'est-à-dire que le client doit changer quelque chose avant de réessayer cette demande. Par exemple, les erreurs "Resource not found" (HTTP 404) ou "Request malformé" (HTTP 400) appartiennent à cette catégorie.


Point 4: Comment faire l'authentification

Comme indiqué au point 1, au lieu d'authentifier un utilisateur, vous pouvez envisager de créer une session. Vous recevrez un nouvel "ID de session", ainsi que le code d'état HTTP approprié (200: Accès accordé ou 403: Accès refusé).

Vous demanderez alors à votre serveur RESTful: "Pouvez-vous me donner la ressource pour cet ID de session?".

Il n'y a pas de mode authentifié - REST est sans état: Vous créez une session, vous demandez au serveur de vous donner des ressources en utilisant cet ID de session en tant que paramètre, et à la déconnexion vous laissez tomber ou expirer la session.


589
2018-01-07 19:13



En termes simples, vous faites cela complètement en arrière.

Vous ne devriez pas aborder cela à partir des URL que vous devriez utiliser. Les URL seront effectivement "gratuitement" une fois que vous avez décidé quelles ressources sont nécessaires pour votre système et comment vous allez représenter ces ressources, et les interactions entre les ressources et l'état de l'application.

Citer Roy Fielding

Une API REST devrait dépenser presque tout   son effort descriptif dans la définition de la   type (s) de média utilisé (s) pour représenter   ressources et application de conduite   état, ou en définissant étendu   noms de relation et / ou   balisage hypertexte activé pour les existants   types de médias standard. Tout effort dépensé   décrivant quelles méthodes utiliser sur quoi   Les URI d'intérêt devraient être entièrement   défini dans le champ d'application   règles de traitement pour un type de média   (et, dans la plupart des cas, déjà défini   par les types de médias existants). [Échec   ici implique que hors bande   l'information est l'interaction de conduite   au lieu de l'hypertexte.]

Les gens commencent toujours par les URI et pensent que c'est la solution, et puis ils ont tendance à manquer un concept clé dans l'architecture REST, notamment, comme cité ci-dessus: «L'échec implique que les informations hors bande conduisent l'interaction au lieu de l'hypertexte. "

Pour être honnête, beaucoup voient une bande d'URI et quelques GET et PUT et POST et pensent que REST est facile. REST n'est pas facile. RPC sur HTTP est facile, déplacer des blobs de données de manière indirecte via les charges utiles HTTP est facile. REST, cependant, va au-delà de cela. REST est agnostique de protocole. HTTP est juste très populaire et approprié pour les systèmes REST.

REST vit dans les types de média, leurs définitions, et comment l'application pilote les actions disponibles pour ces ressources via l'hypertexte (liens, efficacement).

Il existe différentes vues sur les types de média dans les systèmes REST. Certains favorisent les charges utiles spécifiques aux applications, tandis que d'autres préfèrent les types de support existants aux rôles qui conviennent à l'application. Par exemple, d'une part, vous avez des schémas XML spécifiques conçus pour votre application, par opposition à l'utilisation de quelque chose comme XHTML comme représentation, peut-être à travers des microformats et d'autres mécanismes.

Les deux approches ont leur place, je pense, le XHTML fonctionnant très bien dans des scénarios qui chevauchent le web piloté par l'homme et la machine, tandis que les premiers, les types de données plus spécifiques me semblent mieux faciliter les interactions machine à machine. Je trouve que l'élévation des formats de produits peut rendre la négociation de contenu potentiellement difficile. "application / xml + yourresource" est beaucoup plus spécifique en tant que type de média que "application / xhtml + xml", car ce dernier peut s'appliquer à beaucoup de charges utiles qui peuvent ou non être quelque chose qui intéresse réellement un client machine. déterminer sans introspection.

Cependant, XHTML fonctionne très bien (évidemment) dans le web humain où les navigateurs web et le rendu sont très importants.

Votre application vous guidera dans ce genre de décisions.

Une partie du processus de conception d'un système REST consiste à découvrir les ressources de première classe dans votre système, ainsi que les ressources de support dérivées nécessaires pour prendre en charge les opérations sur les ressources primaires. Une fois les ressources découvertes, alors la représentation de ces ressources, ainsi que les diagrammes d'état montrant le flux de ressources via hypertexte au sein des représentations car le prochain défi.

Rappelons que chaque représentation d'une ressource, dans un système hypertexte, combine à la fois la représentation de la ressource réelle avec les transitions d'état disponibles pour la ressource. Considérez chaque ressource comme un nœud dans un graphique, les liens étant les lignes qui laissent ce nœud à d'autres états. Ces liens informent les clients non seulement de ce qui peut être fait, mais de ce qui est nécessaire pour qu'ils soient effectués (car un bon lien combine l'URI et le type de média requis).

Par exemple, vous pouvez avoir:

<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"/>

Votre documentation parlera du champ rel nommé "users", et du type de support "application / xml + youruser".

Ces liens peuvent sembler redondants, ils parlent tous à la même URI, à peu près. Mais ils ne le sont pas.

En effet, pour la relation "utilisateurs", ce lien parle de la collection d'utilisateurs, et vous pouvez utiliser l'interface uniforme pour travailler avec la collection (GET pour les récupérer, SUPPRIMER pour les supprimer, etc.)

Si vous POSTEZ à cette URL, vous devrez passer un document "application / xml + usercollection", qui ne contiendra probablement qu'une seule instance d'utilisateur dans le document afin que vous puissiez ajouter l'utilisateur, ou pas, peut-être, en ajouter plusieurs à une fois que. Peut-être que votre documentation suggérera que vous pouvez simplement passer un seul type d'utilisateur, au lieu de la collection.

Vous pouvez voir ce que l'application nécessite pour effectuer une recherche, telle que définie par le lien "search" et son mediatype. La documentation pour le type de média de recherche vous dira comment cela se comporte et à quoi s'attendre en tant que résultats.

Le point à retenir ici, cependant, est que les URI eux-mêmes sont fondamentalement sans importance. L'application contrôle les URI, pas les clients. Au-delà de quelques «points d'entrée», vos clients doivent s'appuyer sur les URI fournis par l'application pour son travail.

Le client doit savoir comment manipuler et interpréter les types de médias, mais il n'a pas besoin de se soucier de l'endroit où il va.

Ces deux liens sont sémantiquement identiques dans les yeux d'un client:

<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"/>

Alors, concentrez-vous sur vos ressources. Focus sur leurs transitions d'état dans l'application et comment cela est le mieux réalisé.


76
2018-01-08 02:39



re 1: Cela semble bien jusqu'à présent. N'oubliez pas de renvoyer l'URI de l'utilisateur nouvellement créé dans un en-tête "Location:" dans le cadre de la réponse à POST, ainsi qu'un code d'état "201 Created".

re 2: L'activation via GET est une mauvaise idée, et l'inclusion du verbe dans l'URI est une odeur de design. Vous pouvez envisager de renvoyer un formulaire sur un GET. Dans une application Web, il s'agit d'un formulaire HTML avec un bouton de soumission; Dans le cas d'utilisation de l'API, vous pouvez renvoyer une représentation contenant un URI vers PUT pour activer le compte. Bien sûr, vous pouvez également inclure cet URI dans la réponse sur POST à ​​/ users. L'utilisation de PUT garantit que votre demande est idempotente, c'est-à-dire qu'elle peut être envoyée de nouveau en toute sécurité si le client n'est pas sûr du succès. En général, pensez à quelles ressources vous pouvez transformer vos verbes en (sorte de "nounification de verbes"). Demandez-vous quelle est la méthode la plus proche de votre action spécifique. Par exemple. change_password -> PUT; désactiver -> probablement DELETE; add_credit -> éventuellement POST ou PUT. Dirigez le client vers les URI appropriés en les incluant dans vos représentations.

re 3. N'inventez pas de nouveaux codes de statut, à moins de croire qu'ils sont si génériques qu'ils méritent d'être standardisés à l'échelle mondiale. Essayez d'utiliser le code d'état le plus approprié disponible (lisez-les à propos de tous dans la RFC 2616). Inclure des informations supplémentaires dans le corps de la réponse. Si vous êtes vraiment, vraiment sûr de vouloir inventer un nouveau code d'état, détrompez-vous; Si vous le croyez toujours, assurez-vous de choisir au moins la bonne catégorie (1xx -> OK, 2xx -> informatif, 3xx -> redirection, 4xx-> erreur client, 5xx -> erreur serveur). Ai-je mentionné que l'invention de nouveaux codes de statut est une mauvaise idée?

re 4. Si possible, utilisez le cadre d'authentification intégré à HTTP. Découvrez comment Google procède à l'authentification dans GData. En général, ne mettez pas de clés API dans vos URI. Essayez d'éviter les sessions pour améliorer l'évolutivité et la prise en charge de la mise en cache - si la réponse à une requête diffère en raison de quelque chose qui s'est déjà produit, vous vous êtes généralement lié à une instance de processus serveur spécifique. Il est préférable de transformer l'état de la session en état client (par exemple, en faire partie des demandes suivantes) ou de le rendre explicite en le transformant en état de ressource (serveur), c'est-à-dire en lui donnant son propre URI.


29
2018-01-04 20:39



1. Vous avez la bonne idée sur la façon de concevoir vos ressources, à mon humble avis. Je ne changerais rien.

2. Plutôt que d'essayer d'étendre HTTP avec plus de verbes, considérez ce que vos verbes proposés peuvent être réduits en termes de méthodes et de ressources HTTP de base. Par exemple, au lieu d'un activate_login verbe, vous pouvez mettre en place des ressources comme: /api/users/1/login/active qui est un simple booléen. Pour activer un login, juste PUT un document qui dit «vrai» ou 1 ou quoi que ce soit. Pour désactiver, PUT un document qui est vide ou dit 0 ou faux.

De même, pour modifier ou définir des mots de passe, faites simplement PUTs à /api/users/1/password.

Chaque fois que vous avez besoin d'ajouter quelque chose (comme un crédit) pensez en termes de POSTs. Par exemple, vous pourriez faire un POST à une ressource comme /api/users/1/credits avec un corps contenant le nombre de crédits à ajouter. UNE PUT sur la même ressource pourrait être utilisé pour remplacer la valeur plutôt que d'ajouter. UNE POST avec un nombre négatif dans le corps serait soustraire, et ainsi de suite.

3. Je déconseille vivement d'étendre les codes de statut HTTP de base. Si vous ne trouvez pas celui qui correspond exactement à votre situation, choisissez le plus proche et placez les détails de l'erreur dans le corps de la réponse. Souvenez-vous également que les en-têtes HTTP sont extensibles. votre application peut définir tous les en-têtes personnalisés que vous aimez. Une application sur laquelle j'ai travaillé, par exemple, pourrait renvoyer un 404 Not Found dans de multiples circonstances. Plutôt que de faire analyser par le client le corps de la réponse pour la raison, nous avons simplement ajouté un nouvel en-tête, X-Status-Extended, qui contenait nos extensions de code d'état propriétaires. Vous pourriez donc voir une réponse comme:

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

De cette façon, un client HTTP comme un navigateur Web sait toujours quoi faire avec le code 404 standard, et un client HTTP plus sophistiqué peut choisir de regarder le X-Status-Extendeden-tête pour des informations plus spécifiques.

4. Pour l'authentification, je recommande d'utiliser l'authentification HTTP si vous le pouvez. Mais à mon humble avis, il n'y a rien de mal à utiliser l'authentification par cookie si c'est plus facile pour vous.


20
2018-01-04 21:28



Bases de REST

REST a une contrainte d'interface uniforme, qui stipule que le client REST doit s'appuyer sur des normes plutôt que sur des détails spécifiques au service REST réel, de sorte que le client REST ne subira pas de modifications mineures et sera probablement réutilisable.

Il existe donc un contrat entre le client REST et le service REST. Si vous utilisez HTTP comme protocole sous-jacent, les normes suivantes font partie du contrat:

  • HTTP 1.1
    • définitions de méthodes
    • définitions de code d'état
    • en-têtes de contrôle de cache
    • accepter et en-têtes de type de contenu
    • en-têtes auth
  • IRI (utf8 URI)
  • corps (en choisir un)
    • type MIME spécifique à une application enregistrée, par ex. labyrinthe + xml
    • type MIME spécifique au fournisseur, par ex. vnd.github + json
    • type MIME générique avec
      • vocabulaire RDF spécifique à l'application, par ex. ld + json & hydre, schema.org
      • profil spécifique à l'application, par ex. hal + json & profile link param (je suppose)
  • hyperliens
    • ce qui devrait les contenir (en choisir un)
      • envoyer en-têtes de lien
      • envoyer une réponse hypermédia, par ex. html, atome + xml, hal + json, ld + json et hydra, etc ...
    • sémantique
      • utiliser les relations de liens IANA et probablement les relations de liens personnalisés
      • utiliser un vocabulaire RDF spécifique à l'application

REST a une contrainte sans état, qui déclare que la communication entre le service REST et le client doit être sans état. Cela signifie que le service REST ne peut pas gérer les états du client, de sorte que vous ne pouvez pas avoir de stockage de session côté serveur. Vous devez authentifier chaque demande unique. Par exemple, HTTP basic auth (partie du standard HTTP) est correct, car il envoie le nom d'utilisateur et le mot de passe à chaque requête.

Pour répondre à vos questions

  1. Oui c'est possible.

    Pour ne rien dire, les clients ne se soucient pas de la structure de l'IRI, ils se soucient de la sémantique, car ils suivent des liens ayant des relations de lien ou des attributs de données liées (RDF).

    La seule chose importante à propos des IRI, c'est qu'un seul IRI doit identifier une seule ressource. Il est permis à une seule ressource, comme un utilisateur, d'avoir plusieurs IRI différents.

    C'est assez simple pourquoi nous utilisons de bons IRI comme /users/123/password; Il est beaucoup plus facile d'écrire la logique de routage sur le serveur lorsque vous comprenez l'IRI simplement en le lisant.

  2. Vous avez plus de verbes, comme PUT, PATCH, OPTIONS, et plus encore, mais vous n'en avez pas besoin de plus ... Au lieu d'ajouter de nouveaux verbes, vous devez apprendre à ajouter de nouvelles ressources.

    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: {}}

    (La connexion n'a pas de sens à partir de la perspective REST, en raison de la contrainte sans état.)

  3. Vos utilisateurs ne se soucient pas de savoir pourquoi le problème existe. Ils veulent savoir seulement s'il y a un succès ou une erreur, et probablement un message d'erreur qu'ils peuvent comprendre, par exemple: "Désolé, mais nous n'avons pas pu sauvegarder votre message", etc ...

    Les en-têtes d'état HTTP sont vos en-têtes standard. Tout le reste devrait être dans le corps, je pense. Un seul en-tête n'est pas suffisant pour décrire par exemple des messages d'erreur multilingues détaillés.

  4. La contrainte sans état (avec les contraintes de cache et de système en couches) garantit que le service évolue bien. Vous ne voulez sûrement pas maintenir des millions de sessions sur le serveur, quand vous pouvez faire la même chose sur les clients ...

    Le client tiers obtient un jeton d'accès si l'utilisateur y autorise l'accès à l'aide du client principal. Après cela, le client tiers envoie le jeton d'accès à chaque requête. Il existe des solutions plus complexes, par exemple vous pouvez signer toutes les requêtes, etc. Pour plus de détails, consultez le manuel OAuth.

Littérature liée


12
2017-09-17 22:24



Pour les exemples que vous avez indiqués, j'utiliserais ce qui suit:

activate_login

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

changer le mot de passe

PUT /passwords (ceci suppose que l'utilisateur est authentifié)

ajouter un crédit

POST /credits (ceci suppose que l'utilisateur est authentifié)

Pour les erreurs, vous retournerez l'erreur dans le corps dans le format dans lequel vous avez reçu la requête, donc si vous recevez:

DELETE /users/1.xml

Vous enverriez la réponse en XML, il en serait de même pour JSON etc ...

Pour l'authentification, vous devez utiliser l'authentification http.


11
2018-01-04 20:07



  1. Utilisez post quand vous ne savez pas à quoi ressemblera le nouvel URI de ressource (vous créez un nouvel utilisateur, l'application assignera le nouvel utilisateur à son identifiant), PUT pour mettre à jour ou créer des ressources que vous savez comment vont-ils être représentés (exemple : PUT /myfiles/thisismynewfile.txt)
  2. renvoie la description de l'erreur dans le corps du message
  3. Vous pouvez utiliser l'authentification HTTP (si c'est suffisant) Les services Web devraient être des statuts

6
2018-01-04 20:06