Sélectionnez votre langue

Valider un message

Écrire et valider

Valider un message : la chaîne et les outils

Valider un message Telebib2 ne demande rien d'exotique : une bibliothèque JSON Schema standard, les schémas du registre et trois lignes de code. Le tout est de choisir la bonne bibliothèque et de la configurer correctement -deux points sur lesquels il est facile de se tromper sans que rien ne le signale.

Un seul point d'entrée

Vous ne validez jamais un schéma de MIG directement. Vous validez votre message contre messageEnvelope.202701.json et la cascade se déroule seule : l'enveloppe appelle le datablock, qui appelle le routeur, qui choisit le schéma métier selon le MIG et la version annoncés.

La conséquence pratique compte : votre validateur doit pouvoir résoudre les références. Chaque $ref du registre est une URL absolue qui pointe vers un autre fichier. Si ces références ne se résolvent pas, la validation n'échoue pas franchement et dès lors, elle valide beaucoup moins que vous ne le croyez.

Les fichiers sont servis en statique, avec CORS ouvert en lecture. Un $id collé dans un navigateur affiche le schéma : identité et adresse sont la même chose.

Le point d'entrée

curl -s https://json.telebib2.org/schemas/\

messageEnvelope.202701.json
On ne valide jamais un schéma de MIG directement : on valide le message contre messageEnvelope.202701.json, et la cascade se déroule seule, de l'enveloppe au datablock, du datablock au routeur, du routeur au schéma du MIG, puis jusqu'aux composants, listes et types primitifs. Si les références ne se résolvent pas, la validation n'échoue pas franchement : elle valide beaucoup moins qu'on ne le croit.
Fig. 01 — Un seul point d'entrée

Le piège : le draft 2020-12 n'est pas universel

Les schémas Telebib2 sont écrits en JSON Schema draft 2020-12. Beaucoup de bibliothèques, elles, ciblent encore draft-07 par défaut et parfois sans le dire.

C'est un problème sérieux, parce que la fermeture des schémas repose sur unevaluatedProperties, un mot-clé introduit par le 2020-12. Un validateur qui ne le connaît pas ne proteste pas : la spécification lui demande d'ignorer les mots-clés inconnus. Il accepte donc sans broncher un message truffé de propriétés qui n'existent pas dans la norme.

Le symptôme est le pire qui soit : tout passe au vert, et rien n'est vérifié. Avant toute chose, forcez explicitement la version de la spécification et vérifiez qu'un message volontairement fautif est bien rejeté.

Les deux bibliothèques marquées ci-contre sont celles qui valident le registre lui-même, à chaque publication. Les autres sont connues pour couvrir le 2020-12 ; nous ne les avons pas éprouvées sur l'ensemble des schémas.

Bibliothèques éprouvées en 2020-12

Python       jsonschema >= 4.18

             Draft202012Validator          ← utilisée



.NET         JsonSchema.Net 7.x

             (json-everything)             ← utilisée



Java         networknt/json-schema-validator

             VersionFlag.V202012



JavaScript   JavaScript   Ajv 8.x (branche courante)

             import ajv/dist/2020

             le point d'entrée par défaut valide

             en draft-07, pas en 2020-12



Go           santhosh-tekuri/jsonschema
Le même message fautif, avec une propriété inventée insuredNme, soumis à deux validateurs. Le validateur en draft-07 ne connaît pas le mot-clé unevaluatedProperties ; la spécification lui demande d'ignorer les mots-clés inconnus, il conclut valide : faux positif, tout passe au vert et rien n'est vérifié. Le validateur en draft 2020-12 applique unevaluatedProperties false, la propriété n'est prévue nulle part dans le schéma, le message est rejeté et l'erreur signalée tout de suite. Test de fumée en trois minutes : prendre un payload de référence, y ajouter une propriété inventée, valider ; si c'est valide, ce n'est pas du 2020-12.
Fig. 02 — Le piège du draft : le même message fautif, deux validateurs

Charger les schémas : enregistrer plutôt que déréférencer

Laisser le validateur aller chercher chaque $ref en HTTP au fil de la validation fonctionne. Une fois. En production, cela vous rend dépendant du réseau pour valider un message et multiplie les allers-retours : le registre compte plus de quatre cents fichiers, dont près de trois cents listes de valeurs.

La bonne pratique est de miroiter le registre en local et d'enregistrer tous les schémas par leur $id avant la première validation. Le validateur résout alors tout hors ligne, instantanément. C'est exactement ce que font les deux validateurs de référence : les fichiers sont montés en lecture seule et enregistrés au démarrage.

Un mot sur Ajv : en mode strict, il refuse les mots-clés qu'il ne connaît pas. Or le registre en porte deux, description_i18n et x-enum-labels, qui sont des annotations de documentation. Déclarez les plutôt que de désactiver le mode strict. Une ligne suffit et vous gardez tout le filet.

Python

from jsonschema import Draft202012Validator

from referencing import Registry, Resource



registry = Registry().with_resources([

    (s["$id"], Resource.from_contents(s))

    for s in charger("schemas/**/*.json")

])



validator = Draft202012Validator(enveloppe,

                                 registry=registry)



for err in validator.iter_errors(payload):

    print(err.json_path, "→", err.message)

C#

var options = new EvaluationOptions {

    EvaluateAs  = SpecVersion.Draft202012,

    OutputFormat = OutputFormat.List,

};

foreach (var (uri, s) in schemas)

    options.SchemaRegistry.Register(new Uri(uri), s);



var results = enveloppe.Evaluate(payload, options);

JavaScript

import Ajv2020 from "ajv/dist/2020.js";



const ajv = new Ajv2020();



// annotations de documentation portées par le registre —

// déclarées, donc le mode strict reste actif

ajv.addVocabulary(["description_i18n", "x-enum-labels"]);



schemas.forEach((s) => ajv.addSchema(s));



const valider = ajv.getSchema(ENVELOPPE);

const valide = valider(payload);



if (!valide) console.error(valider.errors);
Les bibliothèques et ce qu'il faut y forcer explicitement. Python : jsonschema 4.18 ou plus, avec Draft202012Validator, éprouvée. .NET : JsonSchema.Net 7 de json-everything, avec SpecVersion.Draft202012, éprouvée. Java : networknt json-schema-validator, avec VersionFlag.V202012. JavaScript : Ajv 8, importé depuis ajv/dist/2020. Go : santhosh-tekuri/jsonschema. Deux pièges d'Ajv : le point d'entrée par défaut ne fait pas de 2020-12, et le mode strict refuse les deux annotations du registre, description_i18n et x-enum-labels. Les deux bibliothèques éprouvées valident le registre lui-même à chaque publication ; les autres couvrent le 2020-12 sans avoir été éprouvées sur l'ensemble des schémas.
Fig. 03 — Les bibliothèques et ce qu'il faut y forcer

Lire les erreurs sans s'égarer

Une erreur de validation vous donne deux chemins : celui de la donnée fautive dans votre message et celui de la règle violée dans le schéma. Le premier se lit comme une adresse /dataBlocks/0/content/motorisedVehicle/numberPlate désigne la plaque du véhicule du premier datablock.

Attention au bruit des combinateurs. Le routeur essaie les branches les unes après les autres. Selon le format de sortie choisi, certaines bibliothèques remontent aussi les erreurs des branches explorées puis abandonnées et ce, y compris quand le message est finalement valide. Fiez-vous au verdict global et ne remontez les erreurs que lorsqu'il est négatif.

Commencez toujours par le couple MIG + version. Si le routeur ne reconnaît pas les valeurs annoncées, il refuse le message avant même de regarder son contenu et l'erreur porte sur le routeur plutôt que sur une propriété. Une erreur incompréhensible en tête de cascade est presque toujours un exchangeUnitBusinessContentDescription ou une version qui ne figure pas dans le registre.

Une erreur typique

{

  "path": "/dataBlocks/0/content/domain",

  "keyword": "enum",

  "message": "'auto' is not one of ['motor', …]"

}
Le chemin de la donnée fautive se lit comme une adresse : dataBlocks zéro désigne le premier datablock, content le contenu métier, motorisedVehicle l'objet de risque et numberPlate la plaque. Les trois causes les plus fréquentes sont une version en chaîne au lieu d'un entier, un montant en nombre au lieu d'une chaîne et une propriété inconnue par faute de frappe. Le routeur essayant ses branches l'une après l'autre, il faut se fier au verdict global et commencer toujours par le couple MIG et version.
Fig. 04 — Lire les erreurs sans s'égarer

Se donner un filet

Le registre publie, pour chaque message, deux payloads de référence : une variante min réduite aux propriétés obligatoires et une variante full qui exerce toutes les propriétés du schéma. Ce sont vos meilleurs cas de test, ils sont valides par construction et ils le resteront.

Le réflexe utile est d'en faire une suite de non-régression : si votre implémentation refuse un payload de référence, c'est votre implémentation qui a un problème. Et lorsqu'une nouvelle release arrive, rejouer ces payloads vous dit en quelques secondes ce qui a bougé.

Le registre s'applique la même discipline. Chaque message est validé sur deux moteurs indépendants, un Python et un .NET et les verdicts sont comparés. Un désaccord signale soit un schéma ambigu, soit un bug de bibliothèque. Dans les deux cas, quelque chose sur quoi il ne faut pas construire. Les rapports de validation sont publiés avec les schémas.

Un rapport de validation

{

  "message": "M0151.202701",

  "summary": {

    "valid": true,

    "engines": [

      "dotnet-jsonschema.net-7.0.0.0",

      "python-jsonschema-4.23.0"

    ]

  }

}
Chaque message publié dispose de deux payloads de référence, une variante min réduite aux propriétés obligatoires et une variante full qui exerce tout le schéma, valides par construction : ce sont des cas de non-régression tout prêts, à rejouer à chaque nouvelle release. Le registre s'applique la même discipline en validant chaque message sur deux moteurs indépendants, un Python et un .NET, et en comparant les verdicts ; les rapports sont publiés avec les schémas.
Fig. 05 — Se donner un filet

En résumé : forcez le draft 2020-12, enregistrez les schémas localement plutôt que de les déréférencer à la volée et vérifiez le jour même qu'un message volontairement fautif est bien rejeté. Ces trois précautions écartent l'essentiel de ce qui fait perdre du temps en début d'implémentation.

Contact

À propos de Telebib2