Comprendre REST: verbes, codes d'erreur et authentification

602

Je cherche un moyen d'enrouler les API autour des fonctions par défaut dans mes applications Web, bases de données et CMS basés sur PHP.

J'ai regardé autour de moi et j'ai trouvé plusieurs frameworks "squelettes". En plus des réponses à ma question, il y a Tonic , un framework REST que j'aime 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 sur elle. J'essaie de comprendre les principes de base et je ne les ai pas encore complètement compris. Par conséquent, un certain nombre de questions.

1. Est-ce que je comprends bien?

Disons que j'ai une ressource "utilisateurs". Je pourrais configurer un certain nombre d'URI comme ceci:

/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 dans la pratique, j'aurai besoin de beaucoup plus de verbes. Je me rends compte que ce sont des choses qui pourraient être intégrées 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 toutes dans une seule action.

Certains qui me viennent à l'esprit dans l'exemple 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 de retour.

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

3. Comment renvoyer des messages d'erreur et des 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'état 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 quelle serait la façon de transmettre un code d'erreur propriétaire plus détaillé pour décrire ce qui s'est mal passé (par exemple, "échec de la connexion à la base de données" ou "connexion incorrecte à la base de données")? Si je le mets dans le corps avec le message, je dois l'analyser ensuite. Existe-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?
  • Y a-t-il des points forts contre l'utilisation de sessions lors de l'authentification d'un client REST, à part que c'est une violation flagrante du principe REST? :) (je ne plaisante qu'à moitié ici, l'authentification basée sur la session fonctionnerait bien avec mon infrastructure existante.)
Pekka
la source
13
@Daniel, merci pour la modification. "I more verbs" était un jeu de mots intentionnel, mais je le laisse tel quel, c'est plus facile à lire maintenant. :)
Pekka
1
BTW, sur la description de l'erreur. J'ai fini par mettre la description de l'erreur dans l'en-tête de la réponse. Ajoutez simplement un en-tête nommé «Description de l'erreur».
Andrii Muzychuk
Cela ressemble plus à des questions de sécurité des applications. La sécurité des applications n'est pas l'objet de REST.
Nazar Merza
@NazarMerza comment sont les questions de sécurité des applications 1., 2. et 3.?
Pekka

Réponses:

621

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


Point 1: est-ce que je comprends bien?

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


Lorsque vous traitez un URI de collection comme:http://example.com/resources/

  • GET : répertorie les membres de la collection, ainsi que leurs URI membres pour une navigation ultérieure. Par exemple, répertoriez toutes les voitures à vendre.

  • PUT : Signification définie comme "remplacer la collection entière par une autre collection".

  • POST : 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.

  • SUPPRIMER : Signification définie comme "supprimer toute la collection".


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

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

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

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

  • DELETE : 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 en fait signifier que vos ressources doivent être ré-identifiées. N'oubliez pas que dans REST, vous agissez toujours sur une ressource ou sur une collection de ressources. Ce que vous choisissez comme ressource est très important pour votre définition d'API.

Activer / Désactiver la connexion : Si vous créez une nouvelle session, vous souhaiterez peut-être considérer "la session" comme ressource. Pour créer une nouvelle session, utilisez POST to http://example.com/sessions/avec les informations d'identification dans le corps. Pour l'exirer, utilisez PUT ou DELETE (peut-être selon que 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 http://example.com/users/USER_IDavec les anciens et nouveaux mots de passe dans le corps. Vous agissez sur la ressource "l'utilisateur" et un changement de mot de passe est simplement une demande de mise à jour. Elle 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 fondamental: l'utilisation correcte des verbes HTTP. Toute demande GET ne doit jamais laisser d'effet secondaire.

Par exemple, une demande GET ne doit jamais créer de session sur 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 comme l'instruction SELECT dans un moteur de base de données. N'oubliez pas que la réponse à toute demande avec le verbe GET doit pouvoir être mise en cache lorsqu'elle est demandée avec les mêmes paramètres, tout comme lorsque vous demandez une page Web statique.


Point 3: comment renvoyer des messages d'erreur et des 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.

Impossible de se connecter à la base de données: / Connexion à la base de données incorrecte : En général, vous devez utiliser une erreur 500 pour ces types d'erreurs. Il s'agit d'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'elle réussisse une fois les problèmes du serveur résolus. Précisez les détails dans le corps, afin que le client soit en mesure de nous fournir un peu de 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 manière permanente. c'est-à-dire que le client doit changer quelque chose avant de retenter cette demande. Par exemple, les erreurs «Ressource non trouvée» (HTTP 404) ou «Demande mal formée» (HTTP 400) tomberaient dans cette catégorie.


Point 4: Comment faire l'authentification

Comme indiqué au point 1, au lieu d'authentifier un utilisateur, vous voudrez peut-être penser à 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 m'OBTENIR 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 comme paramètre, et à la déconnexion vous supprimez ou expirez la session.

Daniel Vassallo
la source
6
Très bien, mais votre utilisation de PUTpour changer un mot de passe est probablement incorrecte; PUTrequiert la totalité de la ressource, vous devez donc envoyer tous les attributs utilisateur afin de vous conformer à HTTP (et donc à HATEOAS REST). Au contraire, pour simplement changer le mot de passe, il faut utiliser PATCHou POST.
Lawrence Dol
1
Je pense que ce message serait parfait si vous développiez davantage sur ce que "POST: traite le membre adressé comme une collection à part entière et en crée un nouveau subordonné." veux dire. - J'ai trouvé ce que cela signifie par googler - c'est une exception à votre excellente réponse.
Martin Konecny
6
Je ne suis pas d'accord avec la toute dernière phrase. Vous expliquez comment REST est apatride. Se connecter pour créer une session, puis se déconnecter pour terminer la session après avoir effectué un travail est le meilleur exemple d'une API avec état.
Brandon
1
"Cela va à l'encontre d'un principe REST très fondamental: l'utilisation correcte des verbes HTTP. Toute demande GET ne devrait jamais laisser d'effet secondaire." - Et si vous souhaitez conserver un nombre de hits pour la ressource?
bobbyalex
1
Cet article devrait répondre à vos questions. saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
java_geek
79

Autrement dit, vous faites cela complètement à l'envers.

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

Pour citer Roy Fielding

Une API REST doit consacrer presque tout son effort descriptif à définir le ou les types de média utilisés pour représenter les ressources et piloter l'état de l'application, ou à définir des noms de relation étendus et / ou un balisage hypertexte pour les types de média standard existants. Tout effort consacré à décrire les méthodes à utiliser sur les URI d'intérêt devrait être entièrement défini dans le cadre des règles de traitement pour un type de support (et, dans la plupart des cas, déjà défini par les types de support existants). [L'échec ici implique que les informations hors bande conduisent à l'interaction plutôt qu'à l'hypertexte.]

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

Pour être honnête, beaucoup voient un tas d'URI et certains GET et PUT et POST et pensent que REST est facile. REST n'est pas facile. RPC sur HTTP est facile, déplacer des objets blob de données dans les deux sens via des charges utiles HTTP est facile. REST, cependant, va au-delà de cela. REST est indépendant du protocole. HTTP est juste très populaire et adapté aux systèmes REST.

REST réside dans les types de médias, leurs définitions et la manière dont l'application pilote les actions disponibles pour ces ressources via l'hypertexte (liens, efficacement).

Il existe différentes vues sur les types de supports dans les systèmes REST. Certains favorisent les charges utiles spécifiques à l'application, tandis que d'autres aiment élever les types de médias existants dans des rôles appropriés pour l'application. Par exemple, d'une part, vous avez des schémas XML spécifiques conçus adaptés à votre application par rapport à l'utilisation de quelque chose comme XHTML comme représentation, peut-être par le biais de microformats et d'autres mécanismes.

Je pense que les deux approches ont leur place, le XHTML fonctionnant très bien dans des scénarios qui chevauchent à la fois le web piloté par l'homme et le web, alors que les premiers 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 support que "application / xhtml + xml", car ce dernier peut s'appliquer à de nombreuses charges utiles qui peuvent ou non être quelque chose qui intéresse réellement un client de machine, et il ne peut pas non plus déterminer sans introspection.

Cependant, XHTML fonctionne très bien (évidemment) sur 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 de votre système, ainsi que les ressources de support dérivées nécessaires pour prendre en charge les opérations sur les ressources principales. Une fois les ressources découvertes, puis la représentation de ces ressources, ainsi que les diagrammes d'état montrant le flux des ressources via l'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 réelle de la ressource et les transitions d'état disponibles pour la ressource. Considérez chaque ressource comme un nœud dans un graphique, les liens étant les lignes laissant ce nœud à d'autres états. Ces liens informent les clients non seulement de ce qui peut être fait, mais aussi de ce qui est nécessaire pour cela (comme un bon lien combine l'URI et le type de support 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é "utilisateurs" et du type de support "application / xml + youruser".

Ces liens peuvent sembler redondants, ils parlent tous à peu près au même URI. Mais ce n'est pas le cas.

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 tous, DELETE pour les supprimer tous, 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 non, peut-être, pour 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 dont l'application a besoin pour effectuer une recherche, comme défini par le lien "recherche" et son type de médiatisation. La documentation pour le type de support 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 a besoin de savoir comment manipuler et interpréter les types de médias, mais n'a pas grand-chose à faire où il va.

Ces deux liens sont sémantiquement identiques aux yeux des clients:

<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. Concentrez-vous sur leurs transitions d'état dans l'application et sur la meilleure façon d'y parvenir.

Will Hartung
la source
1
Merci Will pour cette réponse très profonde. Plusieurs points pris. Je me rends compte que la planification à partir de "l'apparence de l'URL", c'est l'inverse, et je planifie également du côté des ressources. Avoir des URL avec lesquelles jouer me permet simplement de comprendre le concept plus facilement. Il se pourrait que mes exigences puissent être satisfaites avec un système qui ne respecte pas à 100% les principes REST tels que vous les définissez ici. Je vais dresser une liste complète des exigences pour chaque type de ressource, je suppose que je pourrai alors décider. À votre santé.
Pekka
30

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, avec 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 conception. Vous voudrez peut-être envisager de retourner un formulaire sur un GET. Dans une application Web, ce serait un formulaire HTML avec un bouton d'envoi; dans le cas d'utilisation de l'API, vous souhaiterez peut-être renvoyer une représentation contenant un URI à 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 garantira que votre demande est idempotente, c'est-à-dire qu'elle peut être renvoyée en toute sécurité si le client n'est pas sûr du succès. En général, pensez aux ressources dans lesquelles vous pouvez transformer vos verbes (sorte de "dénonciation des verbes"). Demandez-vous à quelle méthode votre action spécifique est le plus étroitement alignée. Par exemple, change_password -> PUT; désactiver -> probablement SUPPRIMER; add_credit -> éventuellement POST ou PUT.

re 3. N'inventez pas de nouveaux codes de statut, sauf si vous pensez qu'ils sont si génériques qu'ils méritent d'être normalisés à l'échelle mondiale. Essayez d'utiliser le code d'état le plus approprié disponible (lisez-les tous dans la RFC 2616). Inclure des informations supplémentaires dans le corps de 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 au moins de choisir la bonne catégorie (1xx -> OK, 2xx -> informatif, 3xx -> redirection; 4xx-> erreur client, 5xx -> erreur serveur). Ai-je mentionné qu'inventer 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 prendre en charge la mise en cache - si la réponse à une demande diffère en raison de quelque chose qui s'est produit auparavant, vous vous êtes généralement lié à une instance de processus serveur spécifique. Il est préférable de transformer l'état de session en un état client (par exemple, en faire partie des requêtes suivantes) ou de le rendre explicite en le transformant en état de ressource (serveur), c'est-à-dire en lui donnant son propre URI.

Stefan Tilkov
la source
Pouvez-vous expliquer pourquoi ne pas mettre les clés d'API dans les URL? Est-ce parce qu'ils sont visibles dans les journaux proxy? Que faire si les clés sont transitoires et temporelles? Et si HTTPS est utilisé?
MikeSchinkel
4
En plus de violer l'esprit (les URI doivent identifier les choses), la principale conséquence est qu'elle ruine la mise en cache.
Stefan Tilkov
22

1. Vous avez la bonne idée de 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, pensez à 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_loginverbe, vous pouvez configurer des ressources comme: /api/users/1/login/activequi est un simple booléen. Pour activer une connexion, juste PUTun document qui dit «vrai» ou 1 ou autre chose. Pour désactiver, PUTun document là-bas est vide ou dit 0 ou faux.

De même, pour modifier ou définir des mots de passe, il suffit de faire PUTs pour /api/users/1/password.

Chaque fois que vous devez ajouter quelque chose (comme un crédit), pensez à l' POSTart. Par exemple, vous pourriez faire une POSTà une ressource comme /api/users/1/creditsavec un corps contenant le nombre de crédits à ajouter. Un PUTsur la même ressource pourrait être utilisé pour remplacer la valeur plutôt que d'ajouter. Un POSTavec un nombre négatif dans le corps soustrait, et ainsi de suite.

3. Je déconseille fortement d'étendre les codes d'état HTTP de base. Si vous ne trouvez pas celui qui correspond exactement à votre situation, choisissez le plus proche et mettez les détails de l'erreur dans le corps de la réponse. N'oubliez pas non plus 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 Foundsous plusieurs circonstances. Plutôt que de demander au client d'analyser le corps de la réponse pour la raison, nous venons d'ajouter 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 saura toujours quoi faire avec le code 404 normal, et un client HTTP plus sophistiqué peut choisir de regarder l'en- X-Status-Extendedtê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 basée sur les cookies si c'est plus facile pour vous.

friedo
la source
4
Bonne idée d'utiliser des ressources "étendues" pour faire des choses à de plus petites portions d'une plus grande ressource.
womble
1
Les cookies sont valides dans HTTP / REST, mais le serveur ne doit pas stocker le cookie en tant qu'état (donc pas en tant que session). Le cookie peut toutefois stocker une valeur comme un HMAC, qui peut être démontée sans rechercher l'état ailleurs.
Bruce Alderson
14

Bases REST

REST a une contrainte d'interface uniforme, qui stipule que le client REST doit s'appuyer sur des normes au lieu des détails spécifiques à l'application du service REST réel, de sorte que le client REST ne se cassera pas par des modifications mineures et qu'il 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éthode
    • définitions des codes d'état
    • en-têtes de contrôle du cache
    • accepter et en-têtes de type de contenu
    • en-têtes d'authentification
  • IRI ( URI utf8 )
  • corps (en choisir un)
    • type MIME spécifique à l'application enregistrée, par exemple labyrinthe + xml
    • type MIME spécifique au vendeur, par exemple vnd.github + json
    • type MIME générique avec
      • vocabulaire RDF spécifique à l'application, par exemple ld + json & hydra , schema.org
      • profil spécifique à l'application, par exemple hal + json & profile link param (je suppose)
  • hyperliens
    • ce qui devrait les contenir (choisissez-en un)
      • envoi d'en -têtes de lien
      • envoi d'une réponse hypermédia, par exemple html, atom + xml, hal + json, ld + json & hydra, etc ...
    • sémantique
      • utiliser les relations de lien IANA et probablement les relations de lien personnalisées
      • 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 conserver les états du client, vous ne pouvez donc pas disposer d'un stockage de session côté serveur. Vous devez authentifier chaque demande. Ainsi, par exemple, l'authentification de base HTTP (faisant partie de la norme HTTP) est correcte, car elle envoie le nom d'utilisateur et le mot de passe à chaque demande.

Pour répondre à vos questions

  1. Oui c'est possible.

    Juste pour mentionner, les clients ne se soucient pas de la structure 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 ne doit identifier qu'une seule ressource. Il est permis à une seule ressource, comme un utilisateur, d'avoir de nombreux 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 encore plus, 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 du point de vue REST, en raison de la contrainte sans état.)

  3. Vos utilisateurs ne se soucient pas de la raison du problème. Ils veulent seulement savoir s'il y a du 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 enregistrer votre message.", Etc ...

    Les en-têtes d'état HTTP sont vos en-têtes standard. Je pense que tout le reste devrait être dans le corps. Un seul en-tête ne suffit pas pour décrire par exemple des messages d'erreur multilingues détaillés.

  4. La contrainte sans état (ainsi que les contraintes de cache et de système en couches) garantit une bonne évolutivité du service. Vous ne voulez sûrement pas maintenir des millions de sessions sur le serveur, alors que vous pouvez faire de même sur les clients ...

    Le client tiers obtient un jeton d'accès si l'utilisateur lui accorde l'accès à l'aide du client principal. Après cela, le client tiers envoie le jeton d'accès à chaque demande. Il existe des solutions plus compliquées, par exemple, vous pouvez signer chaque demande, etc. Pour plus de détails, consultez le manuel OAuth.

Littérature liée

inf3rno
la source
11

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 (cela suppose que l'utilisateur est authentifié)

ajouter un crédit

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

Pour les erreurs, vous renverriez l'erreur dans le corps au format dans lequel vous avez reçu la demande, donc si vous recevez:

DELETE /users/1.xml

Vous renverriez la réponse en XML, la même chose serait vraie pour JSON etc ...

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

jonnii
la source
1
Je n'utiliserais pas createdans le cadre de l'URI (rappelez-vous que les URI devraient être des noms et que les méthodes HTTP devraient être des verbes qui fonctionnent sur ces noms.) Au lieu de cela, j'aurais une ressource comme celle /users/1/activequi peut être un simple booléen, et elle peut être défini en mettant un 1 ou 0 à cette ressource.
friedo
Vous avez raison, j'ai sorti le / create. Il ne devrait s'agir que d'un message vers la ressource singleton.
jonnii
3
Je ne l'utiliserais pas non plus activationsur l'URI, sauf si vous manipulez et gérez explicitement une ressource par le nom de /users/1/activation. Que fait un GET à ce sujet? Que fait un PUT? J'ai l'impression que vous verbalisez l'URI. En outre, comme pour la négociation de type de contenu, il est souvent préférable de l'ignorer et de l'insérer dans les en-têtes, comme Accept.
Cheeso
6
  1. Utilisez post lorsque vous ne savez pas à quoi ressemblerait l'URI de la nouvelle ressource (vous créez un nouvel utilisateur, l'application attribuerait au nouvel utilisateur son identifiant), PUT pour la mise à jour ou la création de ressources dont vous savez comment elles seront représentées (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 doivent être des stateles
Arjan
la source
5

Je suggérerais (comme première passe) qui PUTne devrait être utilisé que pour la mise à jour des entités existantes. POSTdevrait être utilisé pour en créer de nouveaux. c'est à dire

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

ne me semble pas juste. Cependant, le reste de votre première section (utilisation du verbe) semble logique.

Brian Agnew
la source
quelqu'un a probablement pensé que ce n'était pas vraiment une réponse à sa question
lubos hasko
6
Mon point de vue sur PUT contre POST pour créer de nouvelles entités est d'utiliser PUT lorsque l'appelant contrôle le nom de la ressource, vous pouvez donc PUT vers la ressource exacte et POST lorsque l'appelé contrôle le nouveau nom de la ressource (comme dans l'exemple ici).
SteveD
5

Verbose, mais copié à partir de la spécification de la méthode HTTP 1.1 à http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

9.3 OBTENIR

La méthode GET signifie récupérer toutes les informations (sous la forme d'une entité) identifiées par l'URI de demande. Si l'URI de demande fait référence à un processus de production de données, ce sont les données produites qui doivent être renvoyées en tant qu'entité dans la réponse et non le texte source du processus, sauf si ce texte se trouve être la sortie du processus.

La sémantique de la méthode GET devient un "GET conditionnel" si le message de demande comprend un champ d'en-tête If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match ou If-Range. Une méthode GET conditionnelle demande que l'entité soit transférée uniquement dans les circonstances décrites par le ou les champs d'en-tête conditionnels. La méthode GET conditionnelle est destinée à réduire l'utilisation inutile du réseau en permettant aux entités mises en cache d'être actualisées sans nécessiter plusieurs demandes ou transférer des données déjà détenues par le client.

La sémantique de la méthode GET passe à un "GET partiel" si le message de demande comprend un champ d'en-tête Range. Un GET partiel demande que seule une partie de l'entité soit transférée, comme décrit dans la section 14.35. La méthode GET partielle est destinée à réduire l'utilisation inutile du réseau en permettant aux entités partiellement récupérées de se terminer sans transférer les données déjà détenues par le client.

La réponse à une demande GET peut être mise en cache si et seulement si elle répond aux exigences de mise en cache HTTP décrites dans la section 13.

Voir la section 15.1.3 pour les considérations de sécurité lors de l'utilisation pour les formulaires.

9.5 POST

La méthode POST est utilisée pour demander au serveur d'origine d'accepter l'entité incluse dans la demande en tant que nouveau subordonné de la ressource identifiée par l'URI de demande dans la ligne de demande. POST est conçu pour permettre à une méthode uniforme de couvrir les fonctions suivantes:

  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

La fonction réelle exécutée par la méthode POST est déterminée par le serveur et dépend généralement de l'URI de demande. L'entité publiée est subordonnée à cet URI de la même manière qu'un fichier est subordonné à un répertoire qui le contient, un article de presse est subordonné à un groupe de discussion auquel il est publié ou un enregistrement est subordonné à une base de données.

L'action effectuée par la méthode POST peut ne pas aboutir à une ressource qui peut être identifiée par un URI. Dans ce cas, 200 (OK) ou 204 (Pas de contenu) est le statut de réponse approprié, selon que la réponse inclut ou non une entité qui décrit le résultat.

Si une ressource a été créée sur le serveur d'origine, la réponse DEVRAIT être 201 (créée) et contenir une entité qui décrit le statut de la demande et fait référence à la nouvelle ressource, et un en-tête Location (voir section 14.30).

Les réponses à cette méthode ne peuvent pas être mises en cache, sauf si la réponse inclut des champs d'en-tête Cache-Control ou Expires appropriés. Cependant, la réponse 303 (Voir Autre) peut être utilisée pour demander à l'agent utilisateur de récupérer une ressource pouvant être mise en cache.

Les demandes POST DOIVENT respecter les exigences de transmission des messages énoncées au paragraphe 8.2.

Voir la section 15.1.3 pour des considérations de sécurité.

9.6 PUT

La méthode PUT demande que l'entité incluse soit stockée sous l'URI de demande fourni. Si l'URI de demande fait référence à une ressource déjà existante, l'entité incluse DEVRAIT être considérée comme une version modifiée de celle résidant sur le serveur d'origine. Si l'URI de demande ne pointe pas vers une ressource existante et que cet URI peut être défini comme nouvelle ressource par l'agent utilisateur demandeur, le serveur d'origine peut créer la ressource avec cet URI. Si une nouvelle ressource est créée, le serveur d'origine DOIT en informer l'agent utilisateur via la réponse 201 (créée). Si une ressource existante est modifiée, les codes de réponse 200 (OK) ou 204 (sans contenu) DEVRAIENT être envoyés pour indiquer la réussite de la demande. Si la ressource n'a pas pu être créée ou modifiée avec l'URI de demande, Une réponse d'erreur DEVRAIT être donnée qui reflète la nature du problème. Le destinataire de l'entité NE DOIT PAS ignorer les en-têtes Content- * (par exemple Content-Range) qu'il ne comprend pas ou n'implémente pas et DOIT retourner une réponse 501 (non implémentée) dans de tels cas.

Si la demande passe par un cache et que l'URI de demande identifie une ou plusieurs entités actuellement mises en cache, ces entrées DEVRAIENT être traitées comme périmées. Les réponses à cette méthode ne peuvent pas être mises en cache.

La différence fondamentale entre les requêtes POST et PUT se reflète dans la signification différente de l'URI de demande. L'URI dans une demande POST identifie la ressource qui gérera l'entité incluse. Cette ressource peut être un processus d'acceptation de données, une passerelle vers un autre protocole ou une entité distincte qui accepte les annotations. En revanche, l'URI dans une demande PUT identifie l'entité jointe à la demande - l'agent utilisateur sait quel URI est prévu et le serveur NE DOIT PAS tenter d'appliquer la demande à une autre ressource. Si le serveur souhaite que la demande soit appliquée à un URI différent,

il DOIT envoyer une réponse 301 (déplacée en permanence); l'agent utilisateur PEUT alors prendre sa propre décision concernant la redirection ou non de la demande.

Une seule ressource PEUT être identifiée par de nombreux URI différents. Par exemple, un article peut avoir un URI pour identifier "la version actuelle" qui est distincte de l'URI identifiant chaque version particulière. Dans ce cas, une demande PUT sur un URI général peut entraîner la définition de plusieurs autres URI par le serveur d'origine.

HTTP / 1.1 ne définit pas comment une méthode PUT affecte l'état d'un serveur d'origine.

Les demandes PUT DOIVENT respecter les exigences de transmission des messages énoncées au paragraphe 8.2.

Sauf indication contraire pour un en-tête d'entité particulier, les en-têtes d'entité dans la demande PUT DEVRAIENT être appliqués à la ressource créée ou modifiée par le PUT.

9.7 SUPPRIMER

La méthode DELETE demande au serveur d'origine de supprimer la ressource identifiée par l'URI de demande. Cette méthode PEUT être remplacée par une intervention humaine (ou tout autre moyen) sur le serveur d'origine. Le client ne peut pas garantir que l'opération a été effectuée, même si le code d'état renvoyé par le serveur d'origine indique que l'action s'est terminée avec succès. Cependant, le serveur NE DEVRAIT PAS indiquer de succès sauf si, au moment où la réponse est donnée, il a l'intention de supprimer la ressource ou de la déplacer vers un emplacement inaccessible.

Une réponse réussie DEVRAIT être 200 (OK) si la réponse comprend une entité décrivant l'état, 202 (Accepté) si l'action n'a pas encore été exécutée, ou 204 (Pas de contenu) si l'action a été exécutée mais la réponse ne comprend pas une entité.

Si la demande passe par un cache et que l'URI de demande identifie une ou plusieurs entités actuellement mises en cache, ces entrées DEVRAIENT être traitées comme périmées. Les réponses à cette méthode ne peuvent pas être mises en cache.

gahooa
la source
2

À propos des codes de retour REST: il est incorrect de mélanger les codes de protocole HTTP et les résultats REST.

Cependant, j'ai vu de nombreuses implémentations les mélanger, et de nombreux développeurs peuvent ne pas être d'accord avec moi.

Les codes de retour HTTP sont liés à HTTP Requestlui - même. Un appel REST est effectué à l'aide d'une demande de protocole de transfert hypertexte et il fonctionne à un niveau inférieur à la méthode REST invoquée elle-même. REST est un concept / une approche, et sa sortie est un résultat métier / logique , tandis que le code de résultat HTTP est un transport .

Par exemple, renvoyer "404 Not found" lorsque vous appelez / users / est confus, car cela peut signifier:

  • L'URI est incorrect (HTTP)
  • Aucun utilisateur trouvé (REST)

"403 Interdit / Accès refusé" peut signifier:

  • Autorisation spéciale nécessaire. Les navigateurs peuvent le gérer en demandant à l'utilisateur / mot de passe. (HTTP)
  • Autorisations d'accès incorrectes configurées sur le serveur. (HTTP)
  • Vous devez être authentifié (REST)

Et la liste peut continuer avec '500 Server error "(une erreur HTTP Apache / Nginx lancée ou une erreur de contrainte métier dans REST) ​​ou d'autres erreurs HTTP, etc.

À partir du code, il est difficile de comprendre quelle était la raison de l'échec, un échec HTTP (transport) ou un échec REST (logique).

Si la requête HTTP a été exécutée physiquement avec succès, elle doit toujours renvoyer 200 codes, quel que soit le ou les enregistrements trouvés ou non. Parce que la ressource URI est trouvée et a été gérée par le serveur http. Oui, il peut renvoyer un ensemble vide. Est-il possible de recevoir une page Web vide avec 200 comme résultat http, non?

Au lieu de cela, vous pouvez renvoyer 200 codes HTTP et simplement un JSON avec un tableau / objet vide, ou utiliser un indicateur de résultat / succès booléen pour informer sur le statut de l'opération effectuée.

De plus, certains fournisseurs d'accès Internet peuvent intercepter vos demandes et vous renvoyer un code http 404. Cela ne signifie pas que vos données ne sont pas trouvées, mais que quelque chose ne va pas au niveau du transport.

De Wiki :

En juillet 2004, le fournisseur britannique de télécommunications BT Group a déployé le système de blocage de contenu Cleanfeed, qui renvoie une erreur 404 à toute demande de contenu identifié comme potentiellement illégal par Internet Watch Foundation. D'autres FAI renvoient une erreur HTTP 403 "interdite" dans les mêmes circonstances. La pratique consistant à utiliser de fausses erreurs 404 comme moyen de dissimuler la censure a également été signalée en Thaïlande et en Tunisie. En Tunisie, où la censure était sévère avant la révolution de 2011, les gens ont pris conscience de la nature des fausses erreurs 404 et ont créé un personnage imaginaire nommé "Ammar 404" qui représente "la censure invisible".

Marcodor
la source