web-dev-qa-db-fra.com

Code de réponse HTTP pour POST lorsque la ressource existe déjà

Je construis un serveur qui permet aux clients de stocker des objets. Ces objets sont entièrement construits côté client, avec des ID d'objet permanents pour toute la durée de vie de l'objet.

J'ai défini l'API pour que les clients puissent créer ou modifier des objets à l'aide de PUT:

PUT /objects/{id} HTTP/1.1
...

{json representation of the object}

Le {id} est l'ID de l'objet, il fait donc partie de l'URI de la demande.

Maintenant, je pense aussi à permettre aux clients de créer l'objet à l'aide de POST:

POST /objects/ HTTP/1.1
...

{json representation of the object, including ID}

Puisque POST est conçu comme une opération "Ajouter", je ne sais pas quoi faire si l'objet est déjà là. Devrais-je traiter la demande en tant que demande de modification ou dois-je retourner un code d'erreur (lequel)?

690
vmj

Je pense que 409 Conflict est le plus approprié, mais rarement vu à l'état sauvage:

La demande n'a pas pu être complétée en raison d'un conflit avec l'état actuel de la ressource. Ce code n'est autorisé que dans les cas où l'utilisateur devrait pouvoir résoudre le conflit et soumettre à nouveau la demande. Le corps de la réponse DEVRAIT inclure suffisamment d'informations pour que l'utilisateur puisse reconnaître la source du conflit. Idéalement, l'entité de réponse inclurait suffisamment d'informations pour permettre à l'utilisateur ou à l'agent d'utilisateur de résoudre le problème. cependant, cela pourrait ne pas être possible et n'est pas requis.

Les conflits sont plus susceptibles de se produire en réponse à une demande PUT. Par exemple, si le contrôle de version était utilisé et que l'entité PUT incluait des modifications dans une ressource incompatibles avec celles effectuées par une requête précédente (tierce partie), le serveur pourrait utiliser la réponse 409 pour indiquer qu'il ne pouvait pas terminer la demande. . Dans ce cas, l'entité de réponse contiendrait probablement une liste des différences entre les deux versions dans un format défini par la réponse Content-Type.

856
Wrikken

Selon RFC 7231 , un 303 Voir autre PEUT être utilisé Si le résultat du traitement d'un POST serait équivalent à une représentation d'une ressource existante .

75
Nullius

Personnellement, je vais avec l'extension WebDAV 422 Unprocessable Entity.

selon RFC 4918

Le code d'état 422 Unprocessable Entity signifie que le serveur comprend le type de contenu de l'entité de la requête (un code d'état 415 Unsupported Media Type est donc inapproprié) et que la syntaxe de l'entité de la requête est correcte (un code d'état 400 Bad Request est donc inapproprié), mais n'a pas pu traiter le contenu. instructions.

69
Gareth

En retard au jeu peut-être, mais je suis tombé sur ce problème de sémantique en essayant de créer une API REST.

Pour approfondir un peu la réponse de Wrikken, je pense que vous pouvez utiliser soit 409 Conflict ou 403 Forbidden en fonction de la situation. En bref, utilisez une erreur 403 lorsque l'utilisateur ne peut absolument rien faire pour résoudre le conflit et terminer la demande envoyez une demande DELETE pour supprimer explicitement la ressource) ou utilisez 409 si quelque chose pouvait être fait.

10.4.4 403 Interdit

Le serveur a compris la demande, mais refuse de l'exécuter. L'autorisation n'aidera pas et la demande NE DEVRAIT PAS être répétée. Si la méthode de la demande n'était pas HEAD et que le serveur souhaite rendre publique la raison pour laquelle la demande n'a pas été remplie, il DEVRAIT décrire le motif du refus dans l'entité. Si le serveur ne souhaite pas mettre cette information à la disposition du client, vous pouvez utiliser le code d'état 404 (Introuvable).

De nos jours, quelqu'un dit "403" et un problème d'autorisations ou d'authentification vient à l'esprit, mais la spécification indique que c'est essentiellement le serveur qui dit au client qu'il ne le fera pas, ne le demandez plus, et voici pourquoi le client ne devrait pas le faire. 't.

En ce qui concerne PUT vs. POST... POST devrait être utilisé pour créer une nouvelle instance d'une ressource lorsque l'utilisateur n'a aucun moyen de créer ou ne devrait pas créer d'identificateur pour la ressource. PUT est utilisé lorsque l'identité de la ressource est connue.

9.6 PUT

...

La différence fondamentale entre les demandes POST et PUT est reflétée 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 acceptant les données, une passerelle vers un autre protocole ou une entité distincte acceptant les annotations. En revanche, l'URI dans une demande PUT identifie l'entité jointe à la demande - l'agent d'utilisateur sait ce que l'URI est destiné et le serveur NE DOIT PAS tenter d'appliquer la demande à une autre ressource. Si le serveur souhaite que la requête soit appliquée à un autre URI,

il DOIT envoyer une réponse 301 (déplacé de façon permanente); l'agent utilisateur PEUT alors prendre sa propre décision quant à l'opportunité de rediriger ou non la demande.

21
p0lar_bear

"302 Found" semble logique pour moi. Et le RFC 2616 indique qu'il PEUT être répondu à des demandes autres que GET et HEAD (et cela inclut sûrement le POST)

Mais il garde toujours le visiteur allant à cette URL pour obtenir cette ressource "trouvé", par le RFC. Pour le rendre directement à l'URL "Trouvé", il faut utiliser "303 Voir autre", ce qui a du sens, mais force un autre appel à obtenir son URL suivante. D'un autre côté, cet objet GET peut être mis en cache.

Je pense que j'utiliserais "303 See Other" . Je ne sais pas si je peux répondre avec la "chose" trouvée dans le corps, mais j'aimerais le faire pour enregistrer un aller-retour sur le serveur.

UPDATE: Après avoir relu le RFC, je pense toujours qu'un inexistant Le code "4XX + 303 trouvé" doit être correct. Cependant, le "conflit 409" est le meilleur code de réponse existant (indiqué par @ Wrikken), éventuellement avec un en-tête Location pointant vers la ressource existante.

13
alanjds

Et si on retournait un 418?

Parce que le client demande de conserver une entité qui existe déjà sur le serveur, le serveur se met finalement en colère et pense qu'il est une théière et renvoie: 418 I'm a teapot.

Références:

13
рüффп

Je ne pense pas que tu devrais faire ça.

Comme vous le savez, le POST modifie la collection et sert à CRÉER un nouvel élément. Donc, si vous envoyez l’identifiant (je pense que ce n’est pas une bonne idée), vous devez modifier la collection, c’est-à-dire modifier l’article, mais c’est déroutant.

Utilisez-le pour ajouter un article sans identifiant. C'est la meilleure pratique.

Si vous souhaitez capturer une contrainte UNIQUE (pas l'id), vous pouvez répondre 409, comme vous pouvez le faire dans les demandes PUT. Mais pas l'ID.

9
Alfonso Tienda

Il s’agit du contexte ) , ainsi que de la responsabilité des doublons dans les demandes (serveur ou client ou les deux)

Si le serveur ne fait que pointer le doublon, regardez 4xx:

  • 400 Bad Request - lorsque le serveur ne traite pas la demande car il s'agit d'un défaut évident du client
  • 409 Conflit - si le serveur ne traite pas la demande, mais que la raison en est pas la faute du client
  • ...

Pour le traitement implicite des doublons, regardez 2XX:

  • 200 OK
  • 201 créées
  • ...

si le serveur est devrait retourner quelque chose, regardez 3XX:

  • 302 trouvés
  • 303 Voir Autre
  • ...

lorsque le serveur est capable de pointer la ressource existante, cela implique une redirection.

Si ce qui précède ne suffit pas, il est toujours recommandé de préparer un message d'erreur dans le corps de la réponse.

8
Sławomir Lenart

Je pense que pour REST, il vous suffit de prendre une décision sur le comportement de ce système particulier, auquel cas, je pense que la "bonne" réponse serait l’une des réponses données ci-dessous. Si vous voulez que la demande s'arrête et se comporte comme si le client avait commis une erreur qu'il fallait corriger avant de continuer, utilisez 409. Si le conflit n'a vraiment pas d'importance et si vous souhaitez conserver la demande, répondez en redirigeant le message. client à l'entité trouvée. Je pense que les API REST appropriées doivent être redirigées (ou au moins en fournissant l'en-tête d'emplacement) vers le point de terminaison GET pour cette ressource après un POST, de sorte que ce comportement donnerait une expérience cohérente.

EDIT: Il est également intéressant de noter que vous devriez envisager un PUT puisque vous fournissez l'ID. Ensuite, le comportement est simple: "Je me moque de ce qui est là maintenant, mettez ce truc là." Sens, si rien n'est là, il sera créé; si quelque chose est là, il sera remplacé. Je pense qu'un POST est plus approprié lorsque le serveur gère cet ID. Séparer les deux concepts vous indique en gros comment le gérer (c’est-à-dire que PUT est idempotent; il devrait donc toujours fonctionner aussi longtemps que la charge utile est validée, POST crée toujours. Ainsi, en cas de collision d’ID 409 décrirait ce conflit).

6
Sinaesthetic

Pourquoi pas un 202 accepté ? C'est une demande OK (200), il n'y a pas d'erreur de client (400), en soi.

De 10 définitions de code d'état :

"202 Accepté. La demande a été acceptée pour traitement, mais le traitement n'est pas terminé."

... parce qu'il n'avait pas besoin d'être complété, car il existait déjà. Le client ne sait pas qu'il existe déjà, il n'a rien fait de mal.

Je compte lancer un 202 et renvoyer un contenu similaire à celui que GET /{resource}/{id} aurait renvoyé.

5
Phillip Harrington

J'irais avec 422 Unprocessable Entity, qui est utilisé lorsqu'une demande est invalide mais que le problème ne réside pas dans la syntaxe ou l'authentification.

En tant qu'argument contre d'autres réponses, utiliser un code d'erreur non -4xx impliquerait que ce n'est pas une erreur du client, et c'est évidemment le cas. Utiliser un code d'erreur non -4xx pour représenter une erreur client n'a aucun sens.

Il semble que 409 Conflict soit la réponse la plus courante ici, mais selon la spécification, cela implique que la ressource existe déjà et que les nouvelles données que vous lui appliquez sont incompatibles avec son état actuel. Si vous envoyez une demande POST, avec, par exemple, un nom d'utilisateur déjà utilisé, cela ne crée pas de conflit avec la ressource cible car celle-ci n'a pas encore été publiée. C'est une erreur spécifiquement pour le contrôle de version, quand il y a un conflit entre la version de la ressource stockée et la version de la ressource demandée. C'est très utile à cet effet, par exemple lorsque le client a mis en cache une ancienne version de la ressource et envoie une requête basée sur cette version incorrecte qui ne serait plus conditionnellement valide. "Dans ce cas, la représentation de la réponse contiendrait probablement des informations utiles pour la fusion des différences en fonction de l'historique de révision." La demande de création d’un autre utilisateur avec ce nom d’utilisateur est tout simplement irréversible, elle n’a rien à voir avec le contrôle de version.

Pour mémoire, 422 est également le code de statut utilisé par GitHub lorsque vous essayez de créer un référentiel sous un nom déjà utilisé.

5
Grant Gryczan

Un autre traitement potentiel consiste à utiliser PATCH après tout. Un PATCH est défini comme quelque chose qui modifie l'état interne et n'est pas limité à l'ajout.

PATCH résoudrait le problème en vous permettant de mettre à jour des éléments existants. Voir: RFC 5789: PATCH

4
Martin Kersten

Qu'en est-il de 208 - http://httpstatusdogs.com/208-already-reported ? Est-ce une option?

À mon avis, si la seule chose est une ressource de répétition, aucune erreur ne devrait être signalée. Après tout, il n'y a pas d'erreur ni du côté client ni du côté serveur.

2
Fernando Ferreira

Nous sommes tombés sur cette question tout en vérifiant que le code est correct pour un enregistrement en double.

Pardonnez mon ignorance mais je ne comprends pas pourquoi tout le monde ignore le code "300" qui dit clairement "choix multiple" ou "ambigu"

À mon avis, ce serait le code parfait pour construire un système non standard ou particulier pour votre propre usage. Je peux aussi me tromper!

https://tools.ietf.org/html/rfc7231#section-6.4.1

2
Hitin

Plus probablement, il s'agit de 400 Bad Request

6.5.1. 400 requêtes incorrectes


Le code d’état 400 (demande incorrecte) indique que le serveur ne peut pas ou ne traitera pas la demande en raison d’une erreur perçue par le client (par exemple, une syntaxe de demande mal formée, un cadrage du message de demande invalide ou une demande trompeuse). routage).

Comme la demande contient une valeur en double (valeur déjà existante), elle peut être perçue comme une erreur du client. Besoin de changer la demande avant le prochain essai.
En considérant ces faits, nous pouvons conclure que HTTP STATUS 400 Bad Request.

2
Mohammed Safeer