Format de réponse API JSON standard?


697

Existe-t-il des normes ou des meilleures pratiques pour structurer les réponses JSON à partir d'une API? De toute évidence, les données de chaque application sont différentes, de sorte que je ne m'inquiète pas, mais plutôt de la "réponse standard", si vous voulez. Un exemple de ce que je veux dire:

Demande réussie:

{
  "success": true,
  "payload": {
    /* Application-specific data would go here. */
  }
}

Échec de la demande:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}

16
Les gens ont probablement appris de SOAP et ne le reconstruiront pas ...
Denys Séguret

18
@dystroy: Voulez-vous expliquer votre commentaire?
FtDRbwLXw6

5
J'ai été vraiment intéressé par cette question car j'ai dû concevoir une API JSON récemment et je me suis demandé si c'était des normes définissant un format de réponse. Le vôtre est en fait assez joli et vaut la peine d'être utilisé si vous ne trouvez pas de norme. Il est dommage que les réponses fournies ne répondent pas réellement à la question.
Alex

13
@Alex malheureusement, c'est parce que peu importe où vous allez, il n'y a pas de norme. Non seulement au sein de JSON lui-même, mais en termes de la façon de l'utiliser pour les applications RESTful, ou toute autre chose du genre. Tout le monde le fait différemment. Vous pouvez vous sentir libre de suivre les meilleures pratiques (réponses HTTP, structure de package significative, un œil vers la structuration de vos données pour la consommation par votre système), mais tout le monde qui est un grand distributeur fait au moins une chose différente des autres. .. Il n'y a pas de norme, et il n'y en aura probablement pas, alors construisez quelque chose de solide et construisez-le pour vous.
Norguard

Réponses:


641

Oui, il y a quelques normes (bien que certaines libertés sur la définition de la norme) qui ont émergé:

  1. API JSON - L' API JSON couvre également la création et la mise à jour des ressources, pas seulement les réponses.
  2. JSend - Simple et probablement ce que vous faites déjà.
  3. Protocole ODATA JSON - Très compliqué.
  4. HAL - Comme OData mais visant à être comme HATEOAS .

Il existe également des formats de description de l'API JSON:

  • Swagger
    • Schéma JSON (utilisé par swagger mais vous pouvez l'utiliser seul)
  • WADL dans JSON
  • RAML
  • HAL parce que HATEOAS en théorie est auto-décrivant.

19
Je vous remercie. JSend en particulier est exactement ce que je cherchais. C'est similaire à ce que je faisais, mais présente certains avantages que ma méthode n'a pas. En toute justice pour @trungly, JSend est également très proche de sa propre réponse.
FtDRbwLXw6

8
Pour les réponses aux erreurs en particulier, j'aime également le brouillon Détails du problème pour les API HTTP RFC.
Pieter Ennes

1
Vous souhaitez peut-être ajouter code.google.com/p/json-service à la liste des formats de description?
emilesilvis

1
Je pense que l'étiquette "Un standard recommandé pour les rails" est un peu exagérée - ce n'est qu'une solution d'un programmeur. Vous ne savez pas ce qui en fait un «standard recommandé» (surtout si vous regardez la popularité du bijou - ne semble pas que beaucoup de gens l'utilisent du tout)? Personnellement, je ne pense pas que la plupart des programmeurs Rails recommanderaient cette solution en raison de l'utilisation du corps de réponse au lieu des en-têtes HTTP pour le statut
Iwo Dziechciarow

2
Google JSON Style Guide est également une bonne référence
MRodrigues

195

Guide Google JSON

Retour de réponse de succès data

{
  "data": {
    "id": 1001,
    "name": "Wing"
  }
}

Retour de réponse d'erreur error

{
  "error": {
    "code": 404,
    "message": "ID not found"
  }
}

et si votre client est JS, vous pouvez utiliser if ("error" in response) {} pour vérifier s'il y a une erreur.


13
Tout d'abord, le guide Google JSON recommande d'utiliser des guillemets doubles au lieu de guillemets simples.
rpozarickij

1
Je ne sais pas si vous pouvez gérer cela à partir d'une API JSON côté serveur comme PlayJson, de toute façon cela n'a pas d'importance. @Steely, vos liens sont rompus
Rhys Bradbury

3
Qu'en est-il des erreurs qui doivent fournir une liste d'échecs (comme des problèmes de validation)?
Xeoncross

1
@Xeoncross cliquez sur le lien sur le mot error, la page de Google en donne un exemple
MI Wright

@Xeoncross Vous pouvez renvoyer une liste d'échecs à l'aide de error.errors [], défini comme: "Conteneur pour toute information supplémentaire concernant l'erreur. Si le service renvoie plusieurs erreurs, chaque élément du tableau d'erreurs représente une erreur différente." Peut-être que l'erreur de niveau supérieur mentionnerait «La demande de validation d'entrée a échoué» et le tableau d'erreurs [] aurait une entrée pour chaque échec de validation spécifique qui s'est produit.
James Daily

130

Je suppose qu'une norme de facto n'a pas vraiment émergé (et peut-être jamais). Mais peu importe, voici mon point de vue:

Demande réussie:

{
  "status": "success",
  "data": {
    /* Application-specific data would go here. */
  },
  "message": null /* Or optional success message */
}

Échec de la demande:

{
  "status": "error",
  "data": null, /* or optional error payload */
  "message": "Error xyz has occurred"
}

Avantage: mêmes éléments de niveau supérieur dans les cas de réussite et d'erreur

Inconvénient: pas de code d'erreur, mais si vous le souhaitez, vous pouvez soit changer le statut en code (de réussite ou d'échec), soit ajouter un autre élément de niveau supérieur nommé "code".


3
oui c'est la bonne façon si vous utilisez POJO pour l'analyse json! lorsque nous utilisons des POJO, nous avons besoin d'un format json statique et non dynamique!
LOG_TAG

Simple et précis. Mieux que jsend à mon avis car jsend distingue l'erreur de l'échec.
Josue Alexander Ibarra

1
J'utilise aussi ce modèle mais avec un champ appelé messagesqui est un tableau de messages au lieu d'une seule chaîne.
StockBreak

4
La réponse est presque une copie de JSend bien documentée qui est simple et très utile. Ils ont fourni le troisième statut failpour les problèmes de validation typiques, alors qu'il errorn'est utilisé qu'avec des erreurs fatales comme les erreurs de base de données.
s3m3n

pour le succès: si elle a 200dans les en-têtes pourquoi avez-vous même besoin d'un statuschamp? renvoyez simplement l'objet de données directement. Vous savez que cela peut causer des problèmes supplémentaires avec les langages FE typés comme TypeScript.
Deniss M.

84

En supposant que votre question concerne la conception des services Web REST et plus précisément le succès / l'erreur.

Je pense qu'il existe 3 types différents de design.

  1. Utilisez uniquement le code d'état HTTP pour indiquer s'il y a eu une erreur et essayez de vous limiter aux erreurs standard (cela devrait généralement suffire).

    • Avantages: C'est une norme indépendante de votre API.
    • Inconvénients: moins d'informations sur ce qui s'est réellement passé.
  2. Utilisez HTTP Status + json body (même s'il s'agit d'une erreur). Définissez une structure uniforme pour les erreurs (ex: code, message, raison, type, etc.) et utilisez-la pour les erreurs.Si c'est un succès, renvoyez simplement la réponse json attendue.

    • Avantages: Toujours standard car vous utilisez les codes d'état HTTP existants et vous renvoyez un json décrivant l'erreur (vous fournissez plus d'informations sur ce qui s'est passé).
    • Inconvénients: La sortie json variera selon qu'il s'agit d'une erreur ou d'un succès.
  3. Oubliez le statut http (ex: toujours le statut 200), utilisez toujours json et ajoutez à la racine de la réponse un boolean responseValid et un objet d'erreur (code, message, etc.) qui sera renseigné s'il s'agit d'une erreur sinon les autres champs (succès) sont peuplés.

    • Avantages: Le client ne traite que le corps de la réponse qui est une chaîne json et ignore le statut (?).

    • Inconvénients: le moins standard.

A vous de choisir :)

Selon l'API, je choisirais 2 ou 3 (je préfère 2 pour les apis json rest). Une autre chose que j'ai connue dans la conception de REST Api est l'importance de la documentation pour chaque ressource (URL): les paramètres, le corps, la réponse, les en-têtes, etc. + exemples.

Je vous recommanderais également d'utiliser jersey (implémentation jax-rs) + genson (bibliothèque de java / json). Vous n'avez qu'à déposer genson + jersey dans votre classpath et json est automatiquement pris en charge.

ÉDITER:

  • La solution 2 est la plus difficile à mettre en œuvre, mais l'avantage est que vous pouvez bien gérer les exceptions et pas seulement les erreurs commerciales, l'effort initial est plus important mais vous gagnez sur le long terme.

  • La solution 3 est facile à implémenter à la fois, côté serveur et client, mais ce n'est pas aussi agréable que vous devrez encapsuler les objets que vous souhaitez renvoyer dans un objet de réponse contenant également l'erreur responseValid +.


2
Vous dites que je devrais "définir une structure uniforme pour les erreurs" et d'autres suggestions similaires, mais c'est précisément ce que je demande. Je suppose que la réponse se révèle être "non, il n'y a pas de standard ou de meilleures pratiques en ce qui concerne cette structure."
FtDRbwLXw6

7
Pour mémoire: le code d'état HTTP n'est pas un en-tête.
pepkin88

3
"la réponse ne sera pas json mais html." faux! html n'a rien à voir avec la gestion des erreurs. la réponse peut être le type de contenu que vous prenez en charge.
oligofren

2
@ ア レ ッ ク ス Le code d'état HTTP est un code à 3 chiffres dans la ligne d'état de l'en-tête d'une réponse HTTP. À la suite de cette ligne se trouvent des champs d'en-tête, également appelés en-têtes.
pepkin88

1
@ ア レ ッ ク ス La page Wikipédia sur HTTP répond bien à vos questions, vous pouvez le vérifier ici: en.wikipedia.org/wiki/… (lien vers la section Message de réponse)
pepkin88


19

Voici l'instagram au format json utilisé

{
    "meta": {
         "error_type": "OAuthException",
         "code": 400,
         "error_message": "..."
    }
    "data": {
         ...
    },
    "pagination": {
         "next_url": "...",
         "next_max_id": "13872296"
    }
}

19

Je ne serai pas aussi arrogant pour prétendre qu'il s'agit d'une norme, je vais donc utiliser le formulaire "Je préfère".

Je préfère une réponse concise (lorsque je demande une liste de / articles, je veux un tableau JSON d'articles).

Dans mes conceptions, j'utilise HTTP pour le rapport d'état, un 200 renvoie juste la charge utile.

400 renvoie un message de ce qui n'allait pas avec la demande:

{"message" : "Missing parameter: 'param'"}

Renvoyer 404 si le modèle / contrôleur / URI n'existe pas

S'il y a eu une erreur de traitement de mon côté, je retourne 501 avec un message:

{"message" : "Could not connect to data store."}

D'après ce que j'ai vu, un certain nombre de cadres REST-ish ont tendance à aller dans ce sens.

Raisonnement :

JSON est censé être un format de charge utile , ce n'est pas un protocole de session. L'idée globale des charges utiles de session-ish verbeuse vient du monde XML / SOAP et de divers choix malavisés qui ont créé ces conceptions gonflées. Après avoir réalisé que tout cela était un énorme mal de tête, le but de REST / JSON était de le BAISER et d'adhérer à HTTP. Je ne pense pas qu'il y ait quoi que ce soit à distance standard dans les deux JSend et surtout pas avec les plus verbeux d'entre eux. XHR réagira à la réponse HTTP, si vous utilisez jQuery pour votre AJAX (comme la plupart le font), vous pouvez utiliser try/ catchet done()/ fail()callbacks pour capturer les erreurs. Je ne vois pas comment l'encapsulation des rapports d'état dans JSON est plus utile que cela.


2
"JSON est un format de charge utile ...". Non, JSON est un format de sérialisation des données. Vous pouvez l'utiliser pour transmettre tout ce que vous voulez, y compris des protocoles de session ou simplement de simples charges utiles. Vos commentaires sur KISS sont conformes à la cible et indépendants de JSON. Mieux vaut garder le JSON concentré sur ce qu'il est (données de succès ou données de raison d'échec comme vous le décrivez) que de le polluer avec un méli-mélo des deux qui doit constamment être composé et ensuite supprimé. Ensuite, vous pouvez aller jusqu'au bout et stocker vos données JSON telles quelles dans Couchbase et les renvoyer telles quelles à l'application.
Dirk Bester

1
J'aurais peut-être dû le formuler comme «censé être un format de charge utile», mais à part cela, je maintiens mon commentaire. Vous pouvez mettre des données de session / erreur en tant qu'attributs de la balise body dans un document HTML, mais cela ne fait pas en sorte que ce soit le bon ou le bon sens.
Bojan Markovic

16

Pour ce que ça vaut, je fais ça différemment. Un appel réussi n'a que les objets JSON. Je n'ai pas besoin d'un objet JSON de niveau supérieur qui contient un champ de réussite indiquant vrai et un champ de charge utile qui a l'objet JSON. Je viens de renvoyer l'objet JSON approprié avec un 200 ou tout ce qui est approprié dans la plage 200 pour le statut HTTP dans l'en-tête.

Cependant, s'il y a une erreur (quelque chose dans la famille 400), je retourne un objet d'erreur JSON bien formé. Par exemple, si le client POSTE un utilisateur avec une adresse e-mail et un numéro de téléphone et que l'un d'eux est mal formé (c'est-à-dire que je ne peux pas l'insérer dans ma base de données sous-jacente), je retournerai quelque chose comme ceci:

{
  "description" : "Validation Failed"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Invalid phone number."
  } ],
}

Les bits importants ici sont que la propriété "field" doit correspondre exactement au champ JSON qui n'a pas pu être validé. Cela permet aux clients de savoir exactement ce qui n'a pas fonctionné avec leur demande. En outre, "message" se trouve dans les paramètres régionaux de la demande. Si "emailAddress" et "phoneNumber" n'étaient pas valides, le tableau "erreurs" contiendrait des entrées pour les deux. Un corps de réponse JSON 409 (conflit) pourrait ressembler à ceci:

{
  "description" : "Already Exists"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Phone number already exists for another user."
  } ],
}

Avec le code d'état HTTP et ce JSON, le client a tout ce dont il a besoin pour répondre aux erreurs de manière déterministe et il ne crée pas une nouvelle norme d'erreur qui tente de remplacer les codes d'état HTTP. Notez que cela ne se produit que pour la plage de 400 erreurs. Pour tout ce qui est dans la gamme 200, je peux simplement retourner tout ce qui est approprié. Pour moi, c'est souvent un objet JSON de type HAL, mais cela n'a pas vraiment d'importance ici.

La seule chose que j'ai pensé à ajouter était un code d'erreur numérique soit dans les entrées du tableau "erreurs", soit à la racine de l'objet JSON lui-même. Mais jusqu'à présent, nous n'en avons pas eu besoin.


9

Il n'y a pas d'accord sur les autres formats de réponse api des grands géants du logiciel - Google, Facebook, Twitter, Amazon et autres, bien que de nombreux liens aient été fournis dans les réponses ci-dessus, où certaines personnes ont essayé de normaliser le format de réponse.

Comme les besoins des API peuvent différer, il est très difficile de faire participer tout le monde et de convenir d'un certain format. Si vous avez des millions d'utilisateurs utilisant votre API, pourquoi changeriez-vous votre format de réponse?

Voici mon point de vue sur le format de réponse inspiré par Google, Twitter, Amazon et certains messages sur Internet:

https://github.com/adnan-kamili/rest-api-response-format

Fichier Swagger:

https://github.com/adnan-kamili/swagger-sample-template


1
vote positif pour le format sans enveloppe de réponse-api-réponse
Kerem Baydoğan

@adnan kamilli - >>> StatusCode: 304, ReasonPhrase: 'Non modifié', Version: 1.1, Contenu: <null>, En-têtes: {} <<<< est-ce une réponse appropriée de restApi?
Arnold Brown

@ArnoldBrown Pour quel point de terminaison API - action renvoyez-vous ce code?
adnan kamili

c'est un retour de réponse d'une api utilisée pour télécharger une image (données de formulaire) - API écrites par le client.
Arnold Brown

7

Le point de JSON est qu'il est complètement dynamique et flexible. Pliez-le à votre guise, car il s'agit simplement d'un ensemble d'objets et de tableaux JavaScript sérialisés, enracinés dans un seul nœud.

C'est vous qui décidez du type du nœud racine, ce qu'il contient, si vous envoyez des métadonnées avec la réponse, c'est vous qui décidez, si vous définissez le type mime application/jsonou si vous le laissez tel text/plainqu'il vous appartient ( tant que vous savez comment gérer les cas de bord).

Créez un schéma léger que vous aimez.
Personnellement, j'ai trouvé que le suivi analytique et le service mp3 / ogg et le service de galerie d'images et la messagerie texte et les paquets réseau pour les jeux en ligne, et les articles de blog et les commentaires de blog ont tous des exigences très différentes en termes de ce qui est envoyés et ce qui est reçu et comment ils doivent être consommés.

Donc, la dernière chose que je voudrais, en faisant tout cela, est d'essayer de rendre chacun conforme au même standard standard, qui est basé sur XML2.0 ou quelque chose du genre.

Cela dit, il y a beaucoup à dire pour utiliser des schémas qui ont du sens pour vous et sont bien pensés.
Lisez simplement certaines réponses de l'API, notez ce que vous aimez, critiquez ce que vous n'aimez pas, notez ces critiques et comprenez pourquoi elles vous frottent dans le mauvais sens, puis réfléchissez à la façon d'appliquer ce que vous avez appris à ce dont vous avez besoin.


1
Merci pour la réponse, mais encore une fois, je ne suis pas inquiet pour les charges utiles elles-mêmes. Alors que vos exemples ont tous des exigences très différentes en termes de ce qui est envoyé / reçu dans les charges utiles et comment ces charges utiles sont consommées, ils doivent tous résoudre les mêmes problèmes en ce qui concerne la réponse elle-même . À savoir, ils doivent tous déterminer si la demande a abouti. Si tel est le cas, poursuivez le traitement. Sinon, qu'est-ce qui a mal tourné. C'est ce passe-partout qui est commun à toutes les réponses API auxquelles je fais référence dans ma question.
FtDRbwLXw6

Soit renvoyez un état de 200 pour tout, et définissez vous-même une charge utile d'erreur spécifique, ou renvoyez un état correspondant à l'erreur, avec et / ou sans plus de détails dans le corps de la charge utile (si pris en charge). Comme je l'ai dit, le schéma dépend de vous - y compris des informations de méta / état. C'est une ardoise vierge à 100% avec ce que vous voulez en fonction de votre style d'architecture préféré.
Norguard

2
Je me rends compte que c'est une ardoise vierge à faire à ma guise. Le but de ma question est de demander s'il y avait des normes émergentes en ce qui concerne la structure. Je ne demandais pas "qu'est-ce que JSON et comment l'utiliser", mais plutôt: "Je sais comment utiliser JSON pour retourner / structurer tout ce que je veux, mais j'aimerais savoir si des structures standard sont utilisées ou devenir populaire. " Je suis désolé si j'ai mal écrit par question. Merci pour votre réponse, de toute façon.
FtDRbwLXw6

7

JSON-RPC 2.0 définit un format standard de demande et de réponse, et est une bouffée d'air frais après avoir travaillé avec les API REST.


La seule chose que JSON-RPC_2.0 offre pour les exceptions est un code d'erreur? Un code d'erreur numérique ne peut représenter avec quelque fidélité que ce soit le problème survenu.
AgilePro

@AgilePro D'accord, un code d'erreur numérique n'est pas très agréable, et je souhaite que les auteurs de la spécification aient autorisé le codechamp à être une chaîne. Heureusement, la spécification nous permet de remplir toutes les informations que nous voulons dans le datachamp de l'erreur . Dans mes projets JSON-RPC, j'utilise généralement un code numérique unique pour toutes les erreurs de couche application (par opposition à l'une des erreurs de protocole standard). Ensuite, j'ai mis les informations détaillées sur l'erreur (y compris un code de chaîne indiquant le type d'erreur réel) dans le datachamp.
dnault

2

Pour ceux qui viendront plus tard, en plus de la réponse acceptée qui inclut HAL, JSend et JSON API, j'ajouterais quelques autres spécifications qui méritent d'être examinées:

  • JSON-LD , qui est une recommandation du W3C et spécifie comment créer des services Web interopérables en JSON
  • Ion Hypermedia Type for REST, qui se présente comme "un type hypermédia simple et intuitif basé sur JSON pour REST"

1

Le cadre de base suggéré semble bien, mais l'objet d'erreur tel que défini est trop limité. Souvent, on ne peut pas utiliser une seule valeur pour exprimer le problème, et au lieu de cela, une chaîne de problèmes et de causes est nécessaire .

J'ai fait quelques recherches et j'ai découvert que le format le plus courant pour renvoyer une erreur (exceptions) est une structure de ce formulaire:

{
   "success": false,
   "error": {
      "code": "400",
      "message": "main error message here",
      "target": "approx what the error came from",
      "details": [
         {
            "code": "23-098a",
            "message": "Disk drive has frozen up again.  It needs to be replaced",
            "target": "not sure what the target is"
         }
      ],
      "innererror": {
         "trace": [ ... ],
         "context": [ ... ]
      }
   }
}

C'est le format proposé par la norme de données OASIS OASIS OData et semble être l'option la plus standard, mais il ne semble pas y avoir de taux d'adoption élevés d'aucune norme à ce stade. Ce format est conforme à la spécification JSON-RPC.

Vous pouvez trouver la bibliothèque open source complète qui implémente cela à: Mendocino JSON Utilities . Cette bibliothèque prend en charge les objets JSON ainsi que les exceptions.

Les détails sont abordés dans mon article de blog sur la gestion des erreurs dans l'API JSON REST


0

Il n'y a aucune norme contraire à la loi ou illégale autre que le bon sens. Si nous résumons cela comme deux personnes parlant, la norme est la meilleure façon de se comprendre avec précision en un minimum de mots en un minimum de temps. Dans notre cas, «mots minimum» optimise la bande passante pour l'efficacité du transport et «comprendre avec précision» est la structure pour l'efficacité de l'analyseur; qui finit par se retrouver avec moins de données, et la structure commune; de sorte qu'il peut passer par un trou d'épingle et peut être analysé à travers une portée commune (au moins initialement).

Presque dans tous les cas suggérés, je vois des réponses distinctes pour le scénario «Succès» et «Erreur», ce qui est une sorte d'ambiguïté pour moi. Si les réponses sont différentes dans ces deux cas, alors pourquoi devons-nous vraiment y placer un indicateur de «réussite»? N'est-il pas évident que l'absence d '«erreur» est un «succès»? Est-il possible d'avoir une réponse où «Succès» est VRAI avec un «Erreur» défini? Ou la façon dont le «succès» est FAUX sans aucune «erreur» définie? Un seul drapeau ne suffit pas? Je préférerais avoir le drapeau "Erreur" uniquement, car je pense qu'il y aura moins "Erreur" que "Succès".

De plus, devrions-nous vraiment faire de «l'erreur» un indicateur? Et si je veux répondre avec plusieurs erreurs de validation? Donc, je trouve plus efficace d'avoir un nœud «Erreur» avec chaque erreur comme enfant sur ce nœud; où un noeud «Erreur» vide (compte jusqu'à zéro) dénoterait un «Succès».


-2

Meilleure réponse pour les API Web facilement compréhensibles par les développeurs mobiles.

C'est pour la réponse "succès"

{  
   "ReturnCode":"1",
   "ReturnMsg":"Successfull Transaction",
   "ReturnValue":"",
   "Data":{  
      "EmployeeName":"Admin",
      "EmployeeID":1
   }
}

C'est pour la réponse "Erreur"

{
    "ReturnCode": "4",
    "ReturnMsg": "Invalid Username and Password",
    "ReturnValue": "",
    "Data": {}
}

2
Il serait préférable de standardiser vos propriétés. Ce sont toutes des valeurs "Retour ...". Mais les données ne sont pas préfixées. Je dirais, supprimez tous les préfixes "Retour".
z0mbi3

Inclure "Retour" est également assez redondant.
Jack Marchetti
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.