web-dev-qa-db-fra.com

REST Structure de retour des codes d'erreur de l'API

J'écris une API REST et je suis tombé sur un problème. Quel est le meilleur moyen de renvoyer les erreurs de validation?.

Jusqu'à présent, je renvoyais les messages d'erreur dans un code d'erreur général (disons une mauvaise demande, par exemple).

{
    "status": 400,
    "error": {
        "code": 1, // General bad request code
        "message": [
                "The Key \"a\" is missing",
                "The Key \"b\" is missing",
                "The Key \"c\" is missing",
                "Incorrect Format for field \"y\""
         ]
    }

)

J'ai étudié un peu plus à quoi devrait ressembler une bonne réponse API et j'ai pensé aux options suivantes:

  1. Arrêtez-vous à la première erreur rencontrée et renvoyez une réponse avec le code d'erreur spécifique

    {
       "status": 400, //Same as the HTTP header returned
       "error" {
            "code": 1, // Specific field validation error code
            "message": "Field \"x\" is missing from the array structure",
            "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            "more_info" => "www.api.com/help/errors/1"
        }
    )
    
  2. Analyser toutes les données de la demande et renvoyer plusieurs erreurs de validation de champ.

    {
      "status": 400,
      "error": {
        "code": 1 //General bad Request code
        "message": "Bad Request",
        "developer_message": "Field validation errors."
        "more_info": "www.api.com/help/errors/1",
        "error_details": {
                0: {
                        "code": 2 // Specific field validation error code
                        "message": "Field \"x\" is missing from the array structure",
                        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                        "more_info": "www.api.com/help/errors/2"
                    },
    
                1: {
                        "code": 3 // Specific field validation error code
                        "message": "Incorrect Format for field \"y\"",
                        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                        "more_info": "www.api.com/help/errors/3"
                   }
                       }
          }
      }
    

A mon avis, l'option 2 serait la bonne solution (elle donne plus d'informations utiles aux développeurs/utilisateurs finaux et la charge du serveur peut être plus faible (moins de demandes/pas besoin de revalider des données valides/pas besoin de calculer la signature et d'authentifier l'utilisateur). )), mais je me demande quelles sont les meilleures pratiques et s’il existe un autre moyen de traiter ce type de problèmes.

De plus, je pense que l’option 1 est toujours valable si j’obtiens une seule erreur fatale dans le déroulement du script (pas d’erreurs de validation).

Veuillez noter que le code est juste un tableau simple, il est donc plus facile à suivre. Le format de réponse sera JSON ou XML.

26
Sekundes

Regardons l'API graphique de Facebook . C'est durement touché, et de nombreuses erreurs sont probablement générées. Voici ce que Facebook renvoie sur une erreur d'API:

 {
   "error": {
     "message": "Message describing the error", 
     "type": "OAuthException", 
     "code": 190,
     "error_subcode": 460,
     "error_user_title": "A title",
     "error_user_msg": "A message"
   }
 }

Ils essaient de rendre l’API Graph aussi utile que possible, mais ils semblent renvoyer une erreur spécifique avec un code et un sous-code ( Ref ). Le fait que chaque erreur ait son propre code signifie qu'il est plus facile de rechercher ledit code ou message en tant que point de départ pour le débogage. C'est probablement pourquoi ils n'accumulent pas de messages d'erreur dans leur réponse d'erreur officielle. Si c'est assez bon et pratique pour Facebook, c'est probablement assez bien pour nous.

Exemples de réponses d'erreur:

{
  "error": {
    "message": "(#200) Must have a valid access_token to access this endpoint", 
    "type": "OAuthException", 
    "code": 200
  }
}

et

"error": {
  "message": "(#604) Your statement is not indexable. The WHERE clause must contain 
   an indexable column. Such columns are marked with * in the tables linked from
   http://developers.facebook.com/docs/reference/fql ", 
  "type": "OAuthException", 
  "code": 604
}

Ensuite, il y a JSend qui "est une spécification qui établit certaines règles pour la mise en forme des réponses JSON des serveurs Web". Leur objectif est:

Il existe de nombreux services Web fournissant des données JSON et chacun a sa propre manière de formater les réponses. En outre, les développeurs qui écrivent pour des interfaces JavaScript JavaScript réinventent continuellement la roue de la communication de données depuis leurs serveurs. Bien qu'il existe de nombreux modèles courants pour structurer ces données, il n'y a pas de cohérence dans des éléments tels que la dénomination ou les types de réponses. En outre, cela aide à promouvoir le bonheur et l’unité entre les développeurs backend et les concepteurs frontend, tout le monde pouvant espérer une approche commune pour interagir les uns avec les autres.

Voici un exemple de message d'erreur:

{
    "status" : "fail",
    "data" : { "title" : "A title is required" }
}

Il semble que Facebook et ce groupe essayant de se positionner comme un standard de l'industrie optent pour votre choix n ° 1.


Question de prime

En réponse à la demande de prime de "si quelqu'un a n ° 2 et qu'il a peut-être des améliorations?", Il existe un modèle de conception de Pragmatic RESTful API qui indique:

Les erreurs de validation nécessiteront une ventilation par champ. Ceci est mieux modélisé en utilisant un code d'erreur de niveau supérieur fixe pour les échecs de validation et en fournissant les erreurs détaillées dans un champ d'erreur supplémentaire, comme ceci:

{
  "code" : 1024,
  "message" : "Validation Failed",
  "errors" : [
    {
      "code" : 5432,
      "field" : "first_name",
      "message" : "First name cannot have fancy characters"
    },
    {
       "code" : 5622,
       "field" : "password",
       "message" : "Password cannot be blank"
    }
  ]
}
21
Drakes

J'ai moi-même utilisé la n ° 2 plusieurs fois. Est-ce mieux que n ° 1? Je pense que cela dépend de l'utilisation de votre API.

J'apprécie # 2 car cela donne à un développeur qui teste l'API avec quelques appels de test un aperçu rapide de toutes les erreurs/erreurs qu'il a commises dans une requête. Il sait donc immédiatement quelles erreurs/erreurs il doit corriger pour que cette requête soit valide. . Si vous renvoyez les erreurs une à une (comme dans le cas n ° 1), vous devez réessayer de nouveau la demande et vous croiser les doigts en espérant qu'elle sera valide cette fois.

Mais comme je l'ai dit, le n ° 2 est très utile pour les développeurs, mais les raisons ne s'appliquent pas vraiment aux utilisateurs finaux. Les utilisateurs finaux ne se soucient généralement pas de la manière dont elle est mise en œuvre Indique si le logiciel effectue 1 demande qui renvoie 5 erreurs ou 5 demandes ultérieures qui renvoie 1 erreur chacune.
Tant que cela est géré correctement dans le client, l'utilisateur final ne devrait pas remarquer la différence. Bien sûr, la manière de gérer cela dépend beaucoup de ce que le client est .

En plus d’accélérer le développement, un autre avantage du n ° 2 (en production) est qu’il nécessite moins de demandes, ce qui réduit bien entendu la charge du serveur.


Je voudrais savoir si quelqu'un est allé n ° 2 et peut-être avoir des améliorations à ce sujet, alors j'ai ouvert une prime.

Bien sûr, il y a des améliorations à faire. Dans l'état actuel des choses, certaines données du corps peuvent être omises.

{
  "status": 400,
  "error": {
    "code": 1 //General bad Request code
    "message": "Bad Request",
    "developer_message": "Field validation errors."
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

Avec les réponses HTTP, le code de statut ne doit pas figurer dans le corps, mais dans l'en-tête. Cela signifie que "status": 400 et "message": "Bad Request" peuvent être omis ici. 400 doit être le code d'état de la réponse et 400 signifie Requête incorrecte . C'est une norme HTTP et il n'est pas nécessaire de l'expliquer dans la réponse. De plus, "developer_message": "Field validation errors." est une sorte de doublon, car les erreurs spécifiques sont déjà incluses dans chaque erreur, nous pourrions donc les omettre.

Ça laisse

{
  "error": {
    "code": 1 //General bad Request code
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

"code": 1 //General bad Request code
"more_info": "www.api.com/help/errors/1",

Ces 2 lignes n'ont plus de sens maintenant. Elles ne sont pas non plus nécessaires, car chaque erreur a son propre code et son propre lien d’informations. Il est donc possible que nous effacions ces lignes, laissant ainsi ce qui suit.

{
  "error": {
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

Le code d'état 400 indique déjà une erreur, vous n'avez donc plus à indiquer "error": {error details} car nous savons déjà qu'il y a une erreur. La liste des erreurs peut simplement devenir l’objet racine:

[
    {
        "code": 2//Specificfieldvalidationerrorcode
        "message": "Field \"x\" is missing from the array structure",
        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
        "more_info": "www.api.com/help/errors/2"
    },
    {
        "code": 3//Specificfieldvalidationerrorcode
        "message": "Incorrect Format for field \"y\"",
        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
        "more_info": "www.api.com/help/errors/3"
    }
]

Donc, tout ce qui reste dans le corps maintenant est simplement une liste d'erreurs.

Le code d'état est spécifié dans l'en-tête de la réponse.
Les détails sont spécifiés dans le corps de la réponse.

3
Tim Castelijns

J'ai récemment travaillé contre une API Rest qui renverrait plusieurs avertissements ou erreurs dans les résultats. À partir de votre échantillon n ° 2, je le modifierais comme suit:

{
  "status": 400,
  "results" : null,
  "warnings": {
        0: {
                // Build a warning message here, sample text to show concept
                "code": 1 // Specific field validation error code
                "message": "It is no longer neccessary to put .js on the URL"
           }
  }
  "errors": {
        0: {
                "code": 2 // Specific field validation error code
                "message": "Field \"x\" is missing from the array structure"
                "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            },
        1: {
                "code": 3 // Specific field validation error code
                "message": "Incorrect Format for field \"y\"",
                "developer_message": "The field \"y\" must be in the form of \"Y-m-d\""
           }
      }
  }

Cela vous donnerait la possibilité de fournir des résultats, avec plusieurs avertissements ou erreurs selon les besoins de votre réponse.

Et oui, cela a quelque chose de lourd dans la structure, mais cela fournit également une interface facile pour un développeur afin de toujours récupérer ses données dans la même structure.

Je voudrais également supprimer les éléments suivants comme IMHO ils devraient être dans la documentation de l'API (comment trouver de l'aide en utilisant un code d'erreur) plutôt que sur chaque erreur:

"more_info": "www.api.com/help/errors/2"
"more_info": "www.api.com/help/errors/3"

Dans le même ordre d'idées, je ne sais pas si vous avez besoin du message et de developer_message. Ils semblent redondants et ressemblent à ceux que vous essayez de fournir aux utilisateurs les messages d'erreur de l'API lorsque l'appelant n'a pas réussi à fournir les données correctement.

1
Martin Noreke

Tout d'abord, vous auriez fourni de la documentation sur les méthodes de l'API Rest pour les clients. Il est donc attendu du client/développeur qu’il fournisse des données valides pour les paramètres.

Maintenant, cela dit, le n ° 1 est le meilleur moyen de faire une API Reste. La responsabilité du développeur est de réduire au maximum l'utilisation du serveur. Donc, si vous rencontrez une erreur fatale, créez une réponse avec le code d'erreur et le message d'erreur correspondants et renvoyez-la.

De plus, nous ne pouvons pas être sûrs qu'il y ait plus d'erreurs après celle que nous avons rencontrée. Il est donc inutile d'analyser le reste des données. Cela ne fonctionnera pas bien compte tenu du pire des cas.

0
Kamal Kannan