API RESTful: Verbes HTTP avec URL partagées ou spécifiques?
25
Lors de la création d'une API RESTful , dois-je utiliser des verbes HTTP sur la même URL (lorsque c'est possible) ou dois-je créer une URL spécifique par action?
Par exemple:
GET /items # Read all items
GET /items/:id # Read one item
POST /items # Create a new item
PUT /items/:id # Update one item
DELETE /items/:id # Delete one item
Ou avec des URL spécifiques comme:
GET /items # Read all items
GET /item/:id # Read one item
POST /items/new # Create a new item
PUT /item/edit/:id # Update one item
DELETE /item/delete/:id # Delete one item
Dans votre dernier schéma, vous conservez les verbes dans les URL de vos ressources. Cela doit être évité car les verbes HTTP doivent être utilisés à cette fin. Adoptez le protocole sous-jacent au lieu de l'ignorer, de le dupliquer ou de le remplacer.
Regardez DELETE /item/delete/:id, vous placez deux fois la même information dans la même demande. Ceci est superflu et doit être évité. Personnellement, je serais confus avec ça. L'API prend-elle réellement en charge les DELETEdemandes? Que faire si je place deletedans l'URL et utilise un verbe HTTP différent à la place? Cela correspondra-t-il à quelque chose? Si oui, lequel sera choisi? En tant que client d'une API correctement conçue, je ne devrais pas avoir à poser de telles questions.
Peut-être en avez-vous besoin pour prendre en charge les clients qui ne peuvent pas émettre DELETEou PUTdemander. Si tel est le cas, je transmettrais ces informations dans un en-tête HTTP. Certaines API utilisent un en- X-HTTP-Method-Overridetête à cette fin spécifique (ce qui, je pense, est assez moche de toute façon). Je ne placerais certainement pas les verbes dans les chemins.
Allez pour
GET /items # Read all items
GET /items/:id # Read one item
POST /items # Create a new item
PUT /items/:id # Update one item
DELETE /items/:id # Delete one item
Ce qui est important à propos des verbes, c'est qu'ils sont déjà bien définis dans la spécification HTTP et rester en ligne avec ces règles vous permet d'utiliser des caches, des proxys et éventuellement d'autres outils externes à votre application qui comprennent la sémantique de HTTP mais pas la sémantique de votre application . Veuillez noter que la raison pour laquelle vous devez éviter de les avoir dans vos URL ne concerne pas les API RESTful nécessitant des URL lisibles. Il s'agit d'éviter toute ambiguïté inutile.
De plus, une API RESTful peut mapper ces verbes (ou n'importe quel sous-ensemble de ceux-ci) à n'importe quel ensemble de sémantique d'application, tant qu'elle ne va pas à l'encontre de la spécification HTTP. Par exemple, il est parfaitement possible de créer une API RESTful qui utilise uniquement les requêtes GET si toutes les opérations qu'il autorise sont à la fois sûres et idempotentes . Le mappage ci-dessus n'est qu'un exemple qui correspond à votre cas d'utilisation et est conforme à la spécification. Cela ne doit pas nécessairement être comme ça.
Veuillez également noter qu'une API véritablement RESTful ne devrait jamais nécessiter qu'un programmeur lise une documentation complète des URL disponibles tant que vous vous conformez au principe HATEOAS (Hypertext as the Engine of Application State), qui est l'une des hypothèses de base de REST . Les liens peuvent être totalement incompréhensibles pour les humains tant que l'application cliente peut les comprendre et les utiliser pour déterminer les transitions d'état possibles de l'application.
En l'absence de PUTet DELETE, je préférerais l'ajouter au chemin, pas le différencier avec une chaîne de requête. Ce n'est pas une modification de chaîne de requête à une opération existante; c'est une opération distincte.
Robert Harvey
4
@RobertHarvey dans ce cas, je dirais quand même que c'est un hack. Comme vous le dites, c'est une opération et ce n'est pas quelque chose que je mettrais sur le chemin lors de la conception d'une API qui vise à être RESTful. Le placer dans la chaîne de requête semble tout simplement moins invasif. Cela empêche la mise en cache, mais je ne pense pas que les réponses à ce type de demandes devraient être mises en cache de toute façon. Il permet également au consommateur de l'API d'indiquer facilement la méthode sans analyser ni construire l'URL. Idéalement, une API véritablement RESTful devrait fournir les hyperliens sans obliger les clients à créer eux-mêmes des URL.
toniedzwiedz
Si vous n'avez pas tous les verbes, ce n'est pas complètement RESTful de toute façon, n'est-ce pas?
Robert Harvey
@RobertHarvey true mais je les traite comme un repli, pas comme la conception voulue. J'imagine que l'API devrait prendre en charge les méthodes HTTP réelles et si certains clients ne peuvent pas les implémenter pour une raison quelconque, ils peuvent simplement remplacer leur utilisation par ces paramètres de requête. Un proxy pourrait même les saisir à la volée et transformer les requêtes en requêtes à l'aide de verbes HTTP authentiques afin que le serveur n'ait même pas besoin de s'en soucier. Peu d'API sont vraiment RESTful. En ce qui concerne les API Web génériques, c'est vraiment une question de goût. Personnellement, j'opterais pour des URL propres. Plus facile à comprendre à mon humble avis.
toniedzwiedz
1
@RobertHarvey, comme expliqué, ce n'est pas la manière prévue de les utiliser. Je trouve que c'est le moindre de deux maux lorsque vous devez surmonter les limites du client. Je me souviens avoir lu une documentation pour une telle API, mais je devrai faire des fouilles dans l'historique / les signets de mon navigateur pour le trouver. Maintenant que j'y pense, un en-tête pourrait être mieux dans ce cas. Accepteriez-vous?
toniedzwiedz
14
Le premier.
Un URI / URL est un identifiant de ressource (indice dans le nom: identifiant de ressource uniforme). Avec la première convention, la ressource dont vous parlez lorsque vous effectuez "GET / user / 123" et la ressource dont vous parlez lorsque vous effectuez "DELETE / user / 123" est clairement la même ressource car ils ont la même URL.
Avec la deuxième convention, vous ne pouvez pas être sûr que "GET / user / 123" et "DELETE / user / delete / 123" sont en fait la même ressource, et cela semble impliquer que vous supprimez une ressource associée plutôt que la ressource lui-même, il serait donc plutôt surprenant que la suppression /user/delete/123supprime réellement /user/123. Si toutes vos opérations fonctionnent sur des URL différentes, l'URI ne fonctionne plus comme identifiant de ressource.
Lorsque vous dites DELETE /user/123, vous dites "supprimer" l'enregistrement utilisateur avec l'ID 123 "". Alors que si vous dites DELETE /user/delete/123, ce que vous semblez impliquer est de "supprimer" l'enregistrement de suppression d'utilisateur avec l'ID 123 "", ce qui n'est probablement pas ce que vous voulez dire. Et même si vous utilisez le verbe le plus correct dans cette situation: "POST / user / delete / 123" qui dit "faites l'opération attachée à 'user deletor with id 123'", c'est toujours un moyen détourné de dire supprimer un enregistrement (cela s'apparente à la dénonciation d'un verbe en anglais).
Une façon de penser à l'URL est de la traiter comme des pointeurs vers des objets et des ressources comme des objets dans la programmation orientée objet. Quand vous faites GET /user/123, DELETE /user/123vous pouvez penser à les considérer comme des méthodes de l'objet: [/user/123].get(), [/user/123].delete()où []est comme un pointeur opérateur de déréférencement , mais pour les URL (si vous connaissez un langage qui ont des pointeurs). L'un des principes sous-jacents de REST est l'interface uniforme, c'est-à-dire d'avoir un petit ensemble limité de verbes / méthodes qui fonctionne pour tout dans un vaste réseau de ressources / objets.
Par conséquent, le premier est meilleur.
PS: bien sûr, cela regarde REST de la manière la plus pure. Parfois, la praticité l'emporte sur la pureté, et vous devez faire des concessions pour les clients ou le framework qui meurent au cerveau, ce qui rend difficile de faire un REST approprié.
(désolé, ma première fois, j'ai raté le / modifier / et / supprimer / dans (2) ...)
L'idée de l'URI est qu'il s'agit d'un identifiant d'une ressource adressable , plutôt que d'un appel de méthode . L'URI doit donc pointer vers une ressource spécifique. Et si vous respectez l'URI, vous devriez toujours obtenir la même ressource.
Autrement dit, vous devez penser aux URI de la même manière que vous pensez à la clé primaire d'une ligne dans une base de données. Il identifie de façon unique quelque chose: Universal Resource Identifier.
Donc, que vous utilisiez le pluriel ou le singulier, l'URI doit être un identifiant plutôt qu'une invocation . Ce que vous essayez de faire va dans la méthode, à savoir: GET (get), PUT (create / update), DELETE (delete) ou POST (tout le reste).
Donc "/ item / delete / 123" rompt REST car il ne pointe pas vers une ressource, il s'agit plus d'un appel de méthode.
(En outre, juste sémantiquement, vous devriez pouvoir obtenir un URI, décider qu'il est obsolète, puis SUPPRIMER le même URI - car il s'agit d'un identifiant. Si l'URL GET n'a pas "/ delete /" et DELETE en a, alors cela va à l'encontre de la sémantique HTTP. Vous diffusez 2 URI ou plus par ressource où 1 fera l'affaire.)
Maintenant, la triche est la suivante: il n'y a pas de véritable définition claire de ce qui est et n'est pas une ressource, donc l'esquive commune dans REST est de définir un "nom de traitement" et de pointer l'URI vers cela. C'est à peu près un jeu de mots, mais cela satisfait la sémantique.
Donc, si, par exemple, vous ne pouviez vraiment pas l'utiliser pour une raison quelconque:
DELETE /items/123
vous pouvez déclarer au monde que vous disposez d'une ressource de traitement "suppresseur" et utiliser
POST /items/deletor { id: 123 }
Maintenant, cela ressemble beaucoup à RPC (Remote Procedure Call), mais cela passe par la vaste échappatoire de la clause de "traitement des données" de la spécification POST nommée dans la spécification HTTP.
Cependant, cela est un peu exceptionnel, et si vous pouvez utiliser le PUT commun pour créer / mettre à jour, DELETE pour supprimer et POST pour ajouter, créer et tout le reste, vous devriez , car c'est une utilisation plus standard de HTTP. Mais si vous avez un cas délicat comme "commit" ou "publish" ou "redact", alors le cas de l'utilisation d'un nom de processeur satisfait les puristes REST et vous donne toujours la sémantique dont vous avez besoin.
PUT
etDELETE
, je préférerais l'ajouter au chemin, pas le différencier avec une chaîne de requête. Ce n'est pas une modification de chaîne de requête à une opération existante; c'est une opération distincte.Le premier.
Un URI / URL est un identifiant de ressource (indice dans le nom: identifiant de ressource uniforme). Avec la première convention, la ressource dont vous parlez lorsque vous effectuez "GET / user / 123" et la ressource dont vous parlez lorsque vous effectuez "DELETE / user / 123" est clairement la même ressource car ils ont la même URL.
Avec la deuxième convention, vous ne pouvez pas être sûr que "GET / user / 123" et "DELETE / user / delete / 123" sont en fait la même ressource, et cela semble impliquer que vous supprimez une ressource associée plutôt que la ressource lui-même, il serait donc plutôt surprenant que la suppression
/user/delete/123
supprime réellement/user/123
. Si toutes vos opérations fonctionnent sur des URL différentes, l'URI ne fonctionne plus comme identifiant de ressource.Lorsque vous dites
DELETE /user/123
, vous dites "supprimer" l'enregistrement utilisateur avec l'ID 123 "". Alors que si vous ditesDELETE /user/delete/123
, ce que vous semblez impliquer est de "supprimer" l'enregistrement de suppression d'utilisateur avec l'ID 123 "", ce qui n'est probablement pas ce que vous voulez dire. Et même si vous utilisez le verbe le plus correct dans cette situation: "POST / user / delete / 123" qui dit "faites l'opération attachée à 'user deletor with id 123'", c'est toujours un moyen détourné de dire supprimer un enregistrement (cela s'apparente à la dénonciation d'un verbe en anglais).Une façon de penser à l'URL est de la traiter comme des pointeurs vers des objets et des ressources comme des objets dans la programmation orientée objet. Quand vous faites
GET /user/123
,DELETE /user/123
vous pouvez penser à les considérer comme des méthodes de l'objet:[/user/123].get()
,[/user/123].delete()
où[]
est comme un pointeur opérateur de déréférencement , mais pour les URL (si vous connaissez un langage qui ont des pointeurs). L'un des principes sous-jacents de REST est l'interface uniforme, c'est-à-dire d'avoir un petit ensemble limité de verbes / méthodes qui fonctionne pour tout dans un vaste réseau de ressources / objets.Par conséquent, le premier est meilleur.
PS: bien sûr, cela regarde REST de la manière la plus pure. Parfois, la praticité l'emporte sur la pureté, et vous devez faire des concessions pour les clients ou le framework qui meurent au cerveau, ce qui rend difficile de faire un REST approprié.
la source
(désolé, ma première fois, j'ai raté le / modifier / et / supprimer / dans (2) ...)
L'idée de l'URI est qu'il s'agit d'un identifiant d'une ressource adressable , plutôt que d'un appel de méthode . L'URI doit donc pointer vers une ressource spécifique. Et si vous respectez l'URI, vous devriez toujours obtenir la même ressource.
Autrement dit, vous devez penser aux URI de la même manière que vous pensez à la clé primaire d'une ligne dans une base de données. Il identifie de façon unique quelque chose: Universal Resource Identifier.
Donc, que vous utilisiez le pluriel ou le singulier, l'URI doit être un identifiant plutôt qu'une invocation . Ce que vous essayez de faire va dans la méthode, à savoir: GET (get), PUT (create / update), DELETE (delete) ou POST (tout le reste).
Donc "/ item / delete / 123" rompt REST car il ne pointe pas vers une ressource, il s'agit plus d'un appel de méthode.
(En outre, juste sémantiquement, vous devriez pouvoir obtenir un URI, décider qu'il est obsolète, puis SUPPRIMER le même URI - car il s'agit d'un identifiant. Si l'URL GET n'a pas "/ delete /" et DELETE en a, alors cela va à l'encontre de la sémantique HTTP. Vous diffusez 2 URI ou plus par ressource où 1 fera l'affaire.)
Maintenant, la triche est la suivante: il n'y a pas de véritable définition claire de ce qui est et n'est pas une ressource, donc l'esquive commune dans REST est de définir un "nom de traitement" et de pointer l'URI vers cela. C'est à peu près un jeu de mots, mais cela satisfait la sémantique.
Donc, si, par exemple, vous ne pouviez vraiment pas l'utiliser pour une raison quelconque:
vous pouvez déclarer au monde que vous disposez d'une ressource de traitement "suppresseur" et utiliser
Maintenant, cela ressemble beaucoup à RPC (Remote Procedure Call), mais cela passe par la vaste échappatoire de la clause de "traitement des données" de la spécification POST nommée dans la spécification HTTP.
Cependant, cela est un peu exceptionnel, et si vous pouvez utiliser le PUT commun pour créer / mettre à jour, DELETE pour supprimer et POST pour ajouter, créer et tout le reste, vous devriez , car c'est une utilisation plus standard de HTTP. Mais si vous avez un cas délicat comme "commit" ou "publish" ou "redact", alors le cas de l'utilisation d'un nom de processeur satisfait les puristes REST et vous donne toujours la sémantique dont vous avez besoin.
la source