Menu

Validation aux frontières d'API : client, serveur et l'écart entre les deux

La validation aux frontières d'API a sa place des deux côtés : le client pour un retour immédiat, le serveur pour l'autorité. Confondre les deux produit des messages qui induisent les utilisateurs en erreur.

Publié le

  • validation
  • API
  • données de test

Un numéro entre dans un produit par plus d’un endroit. Il est saisi dans un formulaire, répercuté par un client, transmis à un service, stocké, puis relu plus tard par un opérateur. Chacun de ces points est une frontière, et chaque frontière a une raison différente de vérifier la valeur.

Les équipes qui les traitent tous comme un même contrôle finissent avec une logique dupliquée qui diverge, et avec le pire des deux mondes : un retour lent parce que le contrôle faisant autorité est distant, et une autorité faible parce que le contrôle rapide est le seul qui s’exécute réellement. Séparer les couches règle les deux problèmes.

La validation doit-elle vivre côté client ou côté serveur ?

Des deux côtés, et pour des raisons différentes. Le client est l’endroit où la latence compte. Un utilisateur qui saisit un long identifiant veut être averti immédiatement d’un caractère mal tapé, et un aller-retour à chaque frappe est la mauvaise façon de le lui dire. Les contrôles locaux, hors ligne, qui n’ont besoin d’aucune consultation, ont leur place ici.

Le serveur est l’endroit où l’autorité compte. Tout ce qu’affirme le client peut être falsifié, contourné en appelant directement le point de terminaison, ou produit par une version plus ancienne dotée d’un ensemble de règles périmé. Le service doit réexécuter le contrôle sur tout ce qu’il accepte, non parce qu’il se méfie de son propre client, mais parce que le client ne fait pas partie de sa frontière de confiance.

Les deux couches devraient partager une même spécification et une même implémentation, publiées sous forme de paquet ou d’artefact généré, afin que le contrôle rapide et le contrôle faisant autorité ne puissent pas diverger sur ce à quoi ressemble une valeur valide. Quand ils divergent, le symptôme est une entrée que le client accepte et que le serveur rejette, ce que les utilisateurs vivent comme un échec inexplicable à la fin d’un formulaire.

Les exemples discutés ici sont structurels. Aucun numéro de compte, d’identité ou de carte réel ne devrait apparaître dans un journal de requêtes, un jeu de test ou une charge utile d’erreur, et les valeurs décrites dans cet article ne sont que des formes illustratives.

Quoi vérifier à la frontière et quoi laisser passer

La frontière doit confirmer ce qu’elle peut confirmer à moindre coût et transmettre le reste comme données.

Vérifiez en bordure : le jeu de caractères, la longueur, la forme normalisée et tout caractère de contrôle publié pour un schéma que le service connaît. Rejetez tôt et avec une raison précise, car l’alternative est une valeur mal formée voyageant plus profondément dans un système qui n’a pas le vocabulaire pour la décrire.

Laissez passer : tout ce qui exige une consultation de registre, sauf si le service a une relation contractuelle qui rend cette consultation peu coûteuse. Un contrôle d’existence de compte sur un point de terminaison public est à la fois un vecteur de déni de service et un risque pour la vie privée, puisqu’il transforme un formulaire en oracle permettant de deviner si une valeur est active.

Normalisez une fois, à la frontière, et stockez la forme normalisée comme valeur canonique tout en conservant l’originale pour l’affichage. Tout ce qui est en aval compare alors une seule représentation, et la classe de bogues où le même numéro apparaît sous trois formats disparaît.

Classer les réponses d’erreur et leur sémantique de nouvelle tentative

Tout rejet ne signifie pas la même chose, et un code d’erreur unique pour tous force chaque appelant à deviner comment réagir.

Situation Signification Réessayable
Entrée mal formée La valeur enfreint la règle de format Non — l’appelant doit envoyer des données différentes
Échec du caractère de contrôle La forme est autorisée mais l’arithmétique ne correspond pas Non — même raison
Schéma non pris en charge Rien de ce que le service implémente ne correspond à cette forme Non — sauf si le service ajoute de la couverture
Temporairement indisponible Une dépendance dont le contrôle a besoin est hors service ou limitée Oui, avec un retrait progressif
Débit limité L’appelant a dépassé une allocation Oui, après l’intervalle indiqué par la réponse

Fondre les quatre premières dans une requête incorrecte générique est l’erreur de conception la plus courante, car cela rend une erreur d’entrée permanente indiscernable d’une panne temporaire. Les clients réessaient alors les mauvais échecs ou abandonnent ceux qui auraient réussi.

Renvoyez un code lisible par machine stable accompagné d’un message lisible par l’humain, et documentez quels codes sont réessayables. Traitez cette classification comme faisant partie du contrat d’interface : la modifier plus tard est un changement cassant pour quiconque y a relié une logique de nouvelle tentative.

Pourquoi un contrôle échoué ne doit-il pas être signalé comme un numéro manquant ?

Parce que les deux affirmations ont des conditions de vérité différentes. Un caractère de contrôle échoué dit que la chaîne ne s’accorde pas avec elle-même, ce que le service sait avec certitude. Un numéro manquant dit qu’aucun compte ou enregistrement de ce type n’existe, ce que le service ne peut généralement pas savoir du tout.

La confusion cause un tort réel dans les deux sens. En apprenant qu’un numéro n’existe pas, un utilisateur disposant d’une valeur authentique peut abandonner une transaction légitime ou saisir autre chose. En apprenant qu’un numéro est valide parce qu’un contrôle a réussi, un utilisateur peut croire qu’un compte a été confirmé alors que seule l’arithmétique l’a été.

Le bon message décrit la chaîne : le format correspondait, ou le chiffre de contrôle ne tenait pas, ou aucune règle implémentée ne reconnaît l’entrée. Rien dans cette liste n’affirme quoi que ce soit sur le monde, et chaque élément dit à l’appelant quelque chose sur lequel il peut agir. La même discipline s’applique à l’intérieur des tâches par lots, où une catégorie mal étiquetée peut envoyer un relecteur traquer un défaut qui n’existe pas ; la catégorisation utilisée pour les grands fichiers est construite exactement sur cette distinction.

Limites de débit, délais d’attente et replis pour les consultations externes

Dès qu’un contrôle a besoin de données extérieures au processus, il acquiert les modes de défaillance d’un appel réseau. Concevoir pour eux n’est pas du pessimisme ; c’est la différence entre une dégradation et une panne.

Chaque appel externe a besoin d’un délai d’attente plus court que la requête qui le contient, afin qu’une dépendance lente ne consomme pas tout le budget. Il a besoin d’une politique de nouvelle tentative avec retrait progressif et variation aléatoire pour les défaillances temporaires, et d’un disjoncteur afin qu’une dépendance qui échoue de façon persistante cesse d’être appelée à pleine cadence.

Il a aussi besoin d’un comportement défini pour le cas où la consultation ne peut pas avoir lieu. Deux réponses sont légitimes, et le choix est une décision produit : faire échouer la requête, ou accepter la valeur provisoirement et la marquer comme non vérifiée. Ce qui n’est pas légitime, c’est de traiter silencieusement une consultation inaccessible comme un succès, car cela convertit une panne en problème de qualité des données.

La mise en cache aide et a besoin de ses propres règles. Ne mettez en cache que ce que le contrat permet, indexez sur la valeur normalisée, et donnez aux entrées une durée de vie adaptée à la vitesse à laquelle le fait sous-jacent peut changer. Ne mettez jamais en cache un rejet comme s’il s’agissait d’un fait vérifié sur le numéro.

Pour les développeurs : contrats, versions et journaux

Traitez l’ensemble de règles comme une dépendance versionnée de l’API, et non comme un détail d’implémentation.

Exposez quelle version de l’ensemble de règles a produit un verdict, afin qu’un appelant puisse dire si un changement de comportement vient de sa propre version ou du service. Versionnez l’ensemble de règles indépendamment du point de terminaison lorsque la couverture change, et gardez les anciennes versions disponibles assez longtemps pour que les clients migrent. Quand un schéma publie de nouveaux paramètres, le changement devrait être une mise à jour de données avec un nouveau numéro de version, et non une modification de code dont les notes de version omettent la différence de comportement.

Journalisez les verdicts, jamais les valeurs complètes. Consignez le schéma, le verdict, la version de l’ensemble de règles et un identifiant de corrélation, et gardez la valeur entièrement hors de la ligne de journal — y compris dans les chemins d’erreur, où les valeurs divulguées apparaissent le plus souvent.

Enfin, rappelez-vous ce que la frontière ne peut pas établir. Un format validé est une affirmation sur une chaîne, comme le cas uniquement formel le montre clairement pour les schémas sans aucune arithmétique, et le modèle en trois couches de la validation est la référence pour garder les couches séparées.

Étapes suivantes

Prenez le point de terminaison qui reçoit votre identifiant le plus sensible et listez chaque contrôle qu’il effectue, en marquant chacun comme arithmétique locale ou consultation externe. Confirmez ensuite que le client exécute les contrôles locaux tôt et que le serveur les réexécute tous ; l’outil de validation de numéros montre le libellé des verdicts qu’un client peut réutiliser sans danger sans affirmer plus que ce que l’arithmétique soutient.

Continuer la lecture

Articles sur Validateur de numéros de carte et d'identité