Je construis une API où l'utilisateur peut demander au serveur d'effectuer plusieurs actions dans une requête HTTP. Le résultat est renvoyé sous forme de tableau JSON, avec une entrée par action.
Chacune de ces actions peut échouer ou réussir indépendamment l’une de l’autre. Par exemple, la première action peut réussir, l'entrée dans la deuxième action peut être mal formatée et ne pas être validée, et la troisième action peut provoquer une erreur inattendue.
S'il y avait une demande par action, je retournerais les codes d'état 200, 422 et 500 respectivement. Mais maintenant, quand il n'y a qu'une seule demande, quel code d'état dois-je renvoyer?
Quelques options:
- Toujours retourner 200 et donner des informations plus détaillées dans le corps.
- Peut-être suivre la règle ci-dessus uniquement quand il y a plus d'une action dans la demande?
- Peut-être retourner 200 si toutes les demandes aboutissent, sinon 500 (ou un autre code)?
- Utilisez juste une requête par action et acceptez les frais supplémentaires.
- Quelque chose de complètement différent?
Réponses:
La réponse courte et directe
Puisque la demande parle d'exécuter la liste des tâches (les tâches sont la ressource dont nous parlons ici), si le groupe de tâches a été déplacé vers l'exécution (c'est-à-dire quel que soit le résultat de l'exécution), il serait judicieux que le statut de la réponse sera
200 OK
. Sinon, si un problème empêchait l'exécution du groupe de tâches, tel qu'un échec de la validation des objets de tâche , ou qu'un service requis n'était pas disponible, le statut de la réponse devrait indiquer cette erreur. Après cela, lorsque l'exécution des tâches commence, étant donné que les tâches à exécuter sont répertoriées dans le corps de la demande, je m'attendrais alors à ce que les résultats de l'exécution soient répertoriés dans le corps de la réponse.La longue réponse philosophique
Vous rencontrez ce dilemme parce que vous vous détournez de ce pour quoi HTTP a été conçu. Vous ne l’interagissez pas pour gérer les ressources, vous l’utilisez plutôt comme moyen d’invocation de méthode à distance (ce qui n’est pas très étrange, mais fonctionne mal sans un schéma préconçu).
Compte tenu de ce qui précède et sans avoir le courage de transformer cette réponse en un long guide d’opinion, voici un schéma d’URI conforme à une approche de gestion des ressources:
/tasks
GET
liste toutes les tâches, paginéesPOST
ajoute une seule tâche/tasks/task/[id]
GET
répond avec l'objet d'état d'une seule tâcheDELETE
annule / supprime une tâche/tasks/groups
GET
liste tous les groupes de tâches, paginésPOST
ajoute un groupe de tâches/tasks/groups/group/[id]
GET
répond avec l'état d'un groupe de tâchesDELETE
annule / supprime le groupe de tâchesCette structure parle de ressources, pas de quoi en faire. Ce qui se fait avec les ressources concerne un autre service.
Un autre point important à souligner est qu’il est conseillé de ne pas bloquer trop longtemps dans un gestionnaire de requêtes HTTP. Tout comme l'interface utilisateur, une interface HTTP doit être réactive - dans une échelle de temps inférieure de quelques ordres de grandeur (car cette couche traite des entrées / sorties).
Passer à la conception d'une interface HTTP qui gère strictement les ressources est probablement aussi difficile que de déplacer le travail d'un thread d'interface utilisateur lorsqu'un bouton est cliqué. Cela nécessite que le serveur HTTP communique avec d'autres services pour exécuter des tâches plutôt que de les exécuter dans le gestionnaire de demandes. Ce n'est pas une implémentation superficielle, c'est un changement de direction.
Quelques exemples d'utilisation d'un tel schéma d'URI
Exécuter une tâche unique et suivre les progrès:
POST /tasks
avec la tâche à exécuterGET /tasks/task/[id]
jusqu'à ce que l'objet de réponsecompleted
ait une valeur positive tout en affichant l'état actuel / l'avancementExécution d'une tâche unique et en attente de son achèvement:
POST /tasks
avec la tâche à exécuterGET /tasks/task/[id]?awaitCompletion=true
Untilcompleted
a une valeur positive (probablement un délai d'expiration, c'est pourquoi il devrait être mis en boucle)Exécution d'un groupe de tâches et suivi des progrès:
POST /tasks/groups
avec le groupe de tâches à exécuterGET /tasks/groups/group/[groupId]
jusqu'à ce que lacompleted
propriété d' objet de réponse ait une valeur, indiquant le statut de la tâche individuelle (3 tâches terminées sur 5, par exemple)Demander une exécution pour un groupe de tâches et attendre son achèvement:
POST /tasks/groups
avec le groupe de tâches à exécuterGET /tasks/groups/group/[groupId]?awaitCompletion=true
jusqu'à ce que répond avec un résultat qui indique l'achèvement (probablement a un délai d'attente, c'est pourquoi devrait être mis en boucle)la source
Mon vote serait de scinder ces tâches en demandes séparées. Cependant, si trop d'allers et retours sont un sujet de préoccupation, j'ai rencontré le code de réponse HTTP 207 - Multi-Status
Copier / coller depuis ce lien:
la source
207
semble être ce que veut le PO, mais je tiens vraiment à souligner que c’est probablement une mauvaise idée d’avoir cette approche à demandes multiples en un. Si le souci est la performance, alors vous devriez concevoir des systèmes évolutifs horizontaux de style cloud (ce qui rend les systèmes basés sur HTTP excellents)Bien que le multi-statut soit une option, je retournerais 200 (tout va bien) si toutes les demandes aboutissent et une erreur (500 ou peut-être 207) sinon.
Le cas standard devrait normalement être 200 - tout fonctionne. Et les clients devraient seulement avoir à vérifier pour cela. Et ce n'est que si le cas d'erreur s'est produit que vous pouvez retourner un 500 (ou un 207). Je pense que le 207 est un choix valable dans le cas d'au moins une erreur, mais si vous voyez le paquet entier comme une transaction, vous pouvez aussi envoyer 500. - Le client voudra interpréter le message d'erreur dans un sens ou dans l'autre.
Pourquoi ne pas toujours envoyer 207? - Parce que les cas standards doivent être faciles et standard. Bien que des cas exceptionnels puissent être exceptionnels. Un client devrait seulement avoir à lire le corps de la réponse et à prendre d'autres décisions complexes, si une situation exceptionnelle le justifie.
la source
Une option serait de toujours renvoyer un code d'état 200, puis des erreurs spécifiques dans le corps du document JSON. C’est exactement comme cela que certaines API sont conçues (elles renvoient toujours un code d’état 200 et envoient l’erreur dans le corps). Pour plus de détails sur les différentes approches, voir http://archive.oreilly.com/pub/post/restful_error_handling.html.
la source
200
pour indiquer que tout va bien, que la demande est reçue et était valide , puis que le JSON fournit des détails sur ce qui s'est passé dans cette demande (c'est-à-dire le résultat des transactions).Je pense que neilsimp1 est correct, mais je recommanderais de revoir la conception des données envoyées de manière à pouvoir les envoyer
206 - Accepted
et les traiter ultérieurement. Peut-être avec des rappels.Le problème lié à l’envoi de plusieurs actions dans une même demande est précisément le fait que chaque action doit avoir son propre "statut"
En regardant importer un CSV (je ne sais pas vraiment ce qu'est le PO mais c'est une version simple). POST le fichier CSV et récupère un 206. Ensuite, le fichier CSV peut être importé et vous pouvez obtenir le statut de l'importation avec un élément GET (200) par rapport à une URL indiquant les erreurs par ligne.
Ce même motif peut être appliqué à de nombreuses opérations par lots
Le code qui gère le POST n'a qu'à vérifier que le format des données d'opération est valide. Ensuite, les opérations peuvent être exécutées ultérieurement. Dans un travailleur de fond, vous pouvez ainsi évoluer plus facilement, par exemple. Ensuite, vous pouvez vérifier l’état des opérations à tout moment. Vous pouvez utiliser l'interrogation ou des rappels, des flux ou autres, pour répondre au besoin de savoir quand un ensemble d'opérations est terminé.
la source
Déjà beaucoup de bonnes réponses ici, mais il manque un aspect:
Quel est le contrat que vos clients attendent?
Les codes de retour HTTP doivent indiquer au moins une distinction entre succès et échec et jouer ainsi le rôle d '"exceptions du pauvre". Ensuite, 200 signifie "contrat entièrement rempli", et 4xx ou 5xx indiquent un manquement.
Naïvement, je m'attendrais à ce que le contrat de votre demande d'actions multiples soit "effectue toutes mes tâches", et si l'une d'entre elles échoue, la demande n'aboutit pas (complètement). En général, en tant que client, je comprendrais 200 comme "tout va bien", et les codes des familles 400 et 500 me forcent à réfléchir aux conséquences d'un échec (partiel). Utilisez donc 200 pour "toutes les tâches effectuées" et 500 plus une réponse descriptive en cas d’échec partiel.
Un contrat hypothétique différent pourrait être "essayez de faire toutes les actions". Ensuite, le contrat est complètement conforme si (certaines) des actions échouent. Ainsi, vous retournerez toujours 200 plus un document de résultats dans lequel vous trouverez les informations de réussite / échec pour les tâches individuelles.
Alors, quel est le contrat que vous voulez suivre? Les deux sont valables, mais le premier (200 seulement si tout a été fait) est plus intuitif pour moi et mieux adapté aux modèles logiciels habituels. Et dans la majorité (espérons-le) cas où le service a exécuté toutes les tâches, il est facile pour le client de détecter ce cas.
Un dernier aspect important: comment communiquez-vous la décision de votre contrat à vos clients? Par exemple, en Java, j'utiliserais des noms de méthodes tels que "doAll ()" ou "tryToDoAll ()". Dans HTTP, vous pouvez nommer les URL de point de terminaison en conséquence, en espérant que les développeurs de votre client voient, lisent et comprennent le nommage (je ne parierais pas là-dessus). Une raison de plus pour choisir le contrat de moindre surprise.
la source
Répondre:
Un code d'état décrit le statut d'une opération. Il est donc logique d’avoir une opération par requête.
Plusieurs opérations indépendantes détruisent le principal sur lequel sont basés le modèle de requête-réponse et les codes d'état. Tu combats la nature.
HTTP / 1.1 et HTTP / 2 ont considérablement réduit le temps système lié aux requêtes HTTP. J’estime qu’il est recommandé de regrouper des demandes indépendantes par lot.
Cela dit,
(1) Vous pouvez effectuer plusieurs modifications avec une requête PATCH ( RFC 5789 ). Cependant, cela nécessite que les modifications ne soient pas indépendantes; ils sont appliqués de manière atomique (tout ou rien).
(2) D'autres ont signalé le code 207 Multi-Status. Toutefois, cela n'est défini que pour WebDAV ( RFC 4918 ), une extension de HTTP.
Une réponse 207 WebDAV XML serait aussi étrange qu'un canard dans une API non WebDAV. Ne fais pas ça.
la source
Si vous avez vraiment besoin de plusieurs actions dans une requête, pourquoi ne pas envelopper toutes les actions d'une transaction dans le backend? De cette façon, ils réussissent ou échouent tous.
En tant que client utilisant l'API, je peux gérer le succès ou l'échec complet d'un appel d'API. Le succès partiel est difficile à gérer, car je devrais gérer tous les états possibles.
la source