Devrais-je utiliser des codes d'état HTTP pour décrire les événements au niveau de l'application

54

Plusieurs serveurs auxquels j'ai eu affaire renverront HTTP 200 pour les demandes que le client devrait considérer comme un échec, avec quelque chose comme "succès: faux" dans le corps.

Cela ne me semble pas être une implémentation correcte des codes HTTP, en particulier en cas d'échec de l'authentification. J'ai lu succinctement les codes d'erreur HTTP comme suit: «4xx» indique que la demande ne doit pas être répétée tant qu'elle n'est pas modifiée, tandis que «5xx» indique que la demande peut être valide ou non et peut être réessayée, sans succès. Dans ce cas, 200: échec de la connexion, ou 200: impossible de trouver ce fichier, ou 200: paramètre manquant x, semble définitivement faux.

D'autre part, je pourrais voir l'argument avancé selon lequel «4xx» devrait uniquement indiquer un problème structurel avec la demande. Il est donc approprié de renvoyer 200: bad user / password (mauvais utilisateur / mot de passe) au lieu de 401 non autorisé, car le client est autorisé à effectuer la demande, mais elle s'avère incorrecte. Cet argument pourrait être résumé de la manière suivante: si le serveur était en mesure de traiter la demande et de prendre une décision, le code de réponse devait être 200 et le client devait consulter le corps pour plus d'informations.

Fondamentalement, cela semble être une question de préférence. Mais ce n'est pas satisfaisant, donc si quelqu'un a une raison pour laquelle l'un ou l'autre de ces paradigmes est plus correct, j'aimerais le savoir.

Kagan Mattson
la source
9
success: falseimplique que la demande a échoué et que vous le savez. Cela devrait être un 500. Quelque chose comme votre mauvais nom d'utilisateur / mot de passe serait un 401. Ce n'est pas si ambigu.
Pete
4
C’est une de ces questions qui peut invoquer des guerres de religion, je pense. Pour une API RESTful, la réponse est claire, mais il existe d'autres types d'API dans lesquelles HTTP est traité uniquement comme une couche de transport. Dans ce cas, les erreurs d'application ne doivent pas être saignées par cette couche.
Gort le robot
5
Quand je ne suis vraiment pas sûr du statut http à renvoyer, il est toujours tentant de choisir 418 "Je suis une théière".
Joshp
1
Un exemple est plusieurs demandes et réponses (en lot) . Le traitement en lots n'est pas une chose reposante; mais les problèmes d'efficacité pratiques nécessitent souvent un certain soutien pour les problèmes d'ordre plus élégant.
Rwong

Réponses:

35

Question interessante.

Fondamentalement, nous pouvons réduire cela à la bonne manière de classer les choses en termes analogues aux couches OSI. HTTP est généralement défini comme un protocole de niveau application et HTTP est en fait un protocole générique Client / Serveur.

Cependant, dans la pratique, le serveur est presque toujours un périphérique de relais et le client est un navigateur Web, chargé de l’interprétation et du rendu du contenu: le serveur ne fait que transmettre des éléments à une application quelconque et ces applications renvoient des scripts arbitraires que le navigateur est responsable de l'exécution. L'interaction HTTP elle-même (formulaires de demande / réponse, codes d'état, etc.) explique principalement comment demander, servir et rendre du contenu arbitraire aussi efficacement que possible, sans gêner. De nombreux codes de statut et en-têtes sont en effet conçus à ces fins.

Le problème pour essayer de superposer le protocole HTTP pour la gestion des flux spécifiques à l’application est qu’il ne vous reste plus qu’une des deux options suivantes: 1) Vous devez faire de votre logique de requête / réponse un sous-ensemble des règles HTTP; ou 2) Vous devez réutiliser certaines règles, puis la séparation des préoccupations a tendance à devenir floue. Cela peut paraître beau et net au début, mais je pense que c'est l'une de ces décisions de conception que vous finissez par regretter à mesure que votre projet évolue.

Par conséquent, je dirais qu'il vaut mieux être explicite sur la séparation des protocoles. Laissez le serveur HTTP et le navigateur Web agir chacun de leur côté, et laissez l'application le faire elle- même. L'application doit être capable de faire des demandes, et elle a besoin des réponses - et sa logique quant à la manière de demander, comment interpréter les réponses, peut être plus (ou moins) complexe que la perspective HTTP.

L’autre avantage de cette approche, qui mérite d’être mentionné, est que les applications ne doivent généralement pas dépendre d’un protocole de transport sous-jacent (d’un point de vue logique). HTTP lui-même a changé dans le passé et nous avons maintenant HTTP 2, suivant SPDY. Si vous ne considérez votre application que comme un plug-in de fonctionnalité HTTP, vous risquez de rester bloqué lorsque de nouvelles infrastructures prendront la relève.

Yam Marcovic
la source
8
Très perspicace. L'argument le plus fort ici est l'inadéquation (impédance) entre les codes d'état HTTP et les valeurs de retour de votre application. Cela peut devenir un cauchemar à long terme. De plus, je soutiens fortement la séparation des préoccupations entre le transport (HTTP) et la charge utile (données de l'application). Si vous vous trompez dans l'URL d'un point de terminaison de service, vous obtenez un 404. Si vous demandez au service un élément inexistant, vous recevez un message spécifique à l'application (vous pouvez éventuellement utiliser des informations supplémentaires pour résoudre le problème).
2
Si vous entrez une URL de manière incorrecte, vous risquez même de ne pas vous retrouver sur le bon serveur, et tout peut arriver.
gnasher729
C'est un regard bien nuancé. Je pense que la question de savoir si HTTP devient une couche de pseudo-transport est le véritable problème de la détermination. La plupart du temps, je rencontre moi-même cette question lorsque vous avez un serveur nginx ou apache servant de proxy à un serveur nodejs, où le proxy a déjà des règles pour l'envoi de ces codes, et la question est de savoir s'il est approprié que le backend soit conforme à la norme. Dans certains de ces cas, il peut y avoir une raison de ne pas envoyer de code d'erreur, car nginx peut l'interpréter comme un "backend down".
Kagan Mattson
4
Je suis d'accord. Il n'y a rien de mal à ce qu'une erreur de couche d'application soit rapportée dans une réponse HTTP 200. Le 200 indique que la requête / réponse HTTP elle-même a abouti, sans rien dire de son contenu ni de la sémantique de la couche application invoquée à ce moment-là.
Courses de légèreté avec Monica
22

Cette question est un peu basée sur l'opinion, mais dans les deux cas.

Comme je le vois, 200 peuvent servir "d'erreurs logicielles". Quand il s’agit de construire des API, j’essaie de faire la distinction entre ces erreurs et les "erreurs commises".

"Les erreurs logicielles" seront signalées avec un code d'état de 200, mais contiendront une description de l'erreur et un statut de réussite de false. Les "erreurs logicielles" ne se produiront que lorsque le résultat sera "comme prévu", mais pas un succès au sens strict.

Il est important de noter que les "erreurs logicielles" sont davantage un indice pour le réalisateur. Par conséquent, il est important de fournir également plus d'informations sur l'erreur, telles qu'un message d'erreur lisible par l'homme et / ou une sorte de code pouvant être utilisé pour fournir à l'utilisateur final des informations en retour. Ces erreurs fournissent à l'implémenteur (et à l'utilisateur final) plus d'informations sur ce qui s'est passé du côté serveur .

Par exemple, supposons que vous ayez une API avec une fonction de recherche, mais aucun résultat n’est généré lors d’une recherche. Ce n'est pas une erreur, mais ce n'est pas non plus un "succès", pas au sens le plus strict de la définition.

Exemple formaté en JSON:

{
    "meta" {
        "success": false,
        "message": "Search yielded no results",
        "code": "NORESULTS"
    }
    "data": []
}

"Les erreurs matérielles" d’autre part, recevront un code d’état recommandé pour l’erreur. Utilisateur non connecté? - 403 / 401. Entrée mal formée? - 400. Erreur du serveur? - 50X. Etc.

Encore une fois, c'est un peu basé sur l'opinion. Certaines personnes veulent traiter toutes les erreurs de manière égale, "erreur dure" tout. Aucun résultat trouvé? C'est un 404! De l'autre côté de la médaille, pas de résultats de recherche? - Ceci est comme prévu, pas d'erreur.

Un autre facteur important à prendre en compte est votre architecture, par exemple; si vous interagissez avec votre API à l'aide de requêtes JavaScript XHR et de jQuery ou AngularJS. Ces "erreurs matérielles" devront être traitées avec un rappel séparé, alors que les "erreurs logicielles" peuvent être traitées avec le "succès". Ne rien casser, le résultat est toujours "comme prévu". Le code côté client peut alors examiner l'état de réussite et le code (ou le message). Et imprimez-le à l'utilisateur final.

maus maus
la source
2
En fait, classer cela comme une erreur au niveau de l'API est une décision curieuse. Même si le client peut, à sa discrétion, le classer comme inattendu au niveau de l'utilisateur.
Déduplicateur
1
Il y a beaucoup de choses à prendre en compte. Tout dépend de la mise en œuvre de l'API. C'est encore une fois, un peu basé sur l'opinion et aussi sur ce que l'API définit comme "succès" et / ou "erreur". Le "success": false-flag est plus un indice pour le réalisateur que quelque chose ne va pas. Habituellement, cela devrait aller avec un code de statut interne . Soit "code": "NORESULTS"un code numérique, quel que soit le créateur de l’API. C'est la plupart du temps là-bas pour que celui qui implémente l'API puisse déduire des informations sur ce qui s'est passé sur le serveur.
die maus
15

Une API comporte deux aspects: l’effort d’implémentation de l’API et l’effort de tous les clients d’utiliser correctement l’API.

En tant qu'auteur du client, je sais que lorsque j'envoie une demande à un serveur Web, je peux soit obtenir une erreur (ne jamais parler correctement au serveur), soit une réponse avec un code d'état. Je dois gérer les erreurs. Je dois gérer une bonne réponse. Je dois gérer les réponses attendues, documentées et "mauvaises". Je dois gérer tout ce qui revient.

En concevant l'API, vous devez rechercher le processus le plus facile à traiter pour le client. Si le client envoie une demande bien formée et que vous pouvez faire ce que la demande vous demande de faire, vous devez donner une réponse dans la plage 200 (il existe parfois des cas où un nombre autre que 200 dans cette plage convient).

Si le client demande "donnez-moi tous les enregistrements comme ...", et qu'il y en ait zéro, un 200 avec succès et un tableau d'enregistrements nuls sont tout à fait appropriés. Les cas que vous mentionnez:

"Échec de la connexion" devrait normalement être un 401. "Impossible de trouver le fichier" devrait être un 404. "Le paramètre manquant x" devrait être environ 500 (en fait, 400 si le serveur détermine que la requête est incorrecte et 500 si le serveur est totalement dérouté par ma demande et n’a aucune idée de ce qui se passe). Retourner 200 dans ces cas est inutile. Cela signifie simplement qu'en tant qu'auteur d'un client, je ne peux pas me contenter de regarder le code d'état, je dois également étudier la réponse. Je ne peux pas simplement dire "statut 200, génial, voici les données".

Surtout le "paramètre manquant" - ce n'est pas quelque chose que je ne pourrais jamais gérer . Cela signifie que ma demande est incorrecte. Si ma demande est incorrecte, je n'ai pas de solution de secours pour résoudre cette demande incorrecte. J'enverrais une demande correcte pour commencer. Maintenant, je suis obligé de le gérer. Je reçois un 200 et dois vérifier s'il y a une réponse "paramètre manquant". C'est terrible.

En fin de compte, il existe une douzaine ou deux codes d’état permettant de gérer de nombreuses situations différentes, et vous devez les utiliser.

gnasher729
la source
3
Lors de la connexion à une API, personnellement, je préférerais obtenir 200 sur un "fichier non trouvé" lors de la connexion à un point de terminaison valide, car ma gestion HTTP n'aura donc pas à se fondre dans la couche qui gère l'API par-dessus.
Whatsisname
4
"Le paramètre manquant x" doit être un 400 BAD_REQUEST car c'est le client qui fait quelque chose de mal. INTERNAL_SERVER_ERROR doit être réservé pour les cas où le serveur fait la mauvaise chose. Un 500 implique que le client peut éventuellement essayer à nouveau. Un 400 implique que quelqu'un doit aller réparer le client.
Gort le robot
1
Si vous écrivez une interface RESTful, l'URL identifie un objet spécifique et un fichier 404 est donc approprié. Conceptuellement, la même chose /customers/premium/johndoe.jsonfait référence à un client qui ne se trouve pas dans la base de données et s’il /files/morefiles/customers.htmlfait référence à une page ne se trouvant pas sur le système de fichiers.
Gort le robot
@whatsisname Ce que vous dites est logique, car on ne sait pas si c'est le noeud final qui est mauvais ou si la ressource n'existe pas. Vous pouvez également affirmer que, que le noeud final soit valide ou non, aucune ressource n'existe à cette adresse, donc a 404est correct dans les deux cas.
Pete
2
Une chose que je n'ai pas vue mentionnée, c'est que lorsque vous reliez des erreurs d'application aux codes d'état HTTP, vous risquez de perdre des informations. Si l'application retourne simplement un 404 et rien d'autre, vous ne savez pas si c'est parce que votre API a renvoyé un 404 ou parce que le serveur n'a pas pu trouver le fichier. Cela pourrait ajouter une étape supplémentaire à votre débogage.
AmadeusDrZaius