
Dans mon article
précédent, j'ai décrit les principaux points lors du développement d'une autre
bibliothèque opensource . J'ai oublié de mentionner encore une chose: si vous ne parlez à personne de la bibliothèque, quelle qu'elle soit, personne ne le saura probablement.
Alors, rencontrez
trava.js - une validation juteuse au profit du projet. Soit dit en passant, nous utilisons de l'herbe depuis plus de six mois, et j'ai pensé qu'il était temps de vous parler des avantages de l'utiliser. Déjà même séché, alors retenez votre souffle. Et allez-y.
Concept
À première vue, il semble que la validation soit un sujet banal qui ne nécessite pas d'attention particulière. La valeur est vraie ou non, ce qui pourrait être plus simple:
function validate (value) {
Mais généralement, ce serait bien de savoir ce qui s'est exactement passé:
function validate (value) { if (!check1(value)) return 'ERROR_1'; if (!check2(value)) return 'ERROR_2'; }
En fait, c'est tout, le problème est résolu.
Sinon pour un «mais».
De l'expérience du développement d'applications réelles, il a été remarqué que l'affaire ne s'arrête pas à la validation. Habituellement, ces données doivent également être converties dans un format spécifique, pour une raison non prise en charge par le sérialiseur dès la sortie de l'emballage, par exemple, des dates, des ensembles ou d'autres types de données personnalisés. Étant donné qu'il s'agit principalement de JSON, en pratique, il s'avère que vous devez effectuer un double passage dans la structure de données d'entrée lors de la validation et de la transformation. L'idée est venue, pourquoi ne pas combiner ces deux étapes en une seule. Un plus possible serait également la présence d'un schéma de données déclaratif explicite.
Pour prendre en charge la conversion d'une valeur dans un format spécifique, le validateur doit pouvoir renvoyer non seulement une erreur, mais également une valeur réduite. Dans le monde js, plusieurs options d'interface sont assez courantes avec des retours d'erreur possibles.
- Le plus courant est probablement le retour du tuple [error, data]:
function validate (value) { if (!check1(value)) return ['ERROR_1']; if (!check2(value)) return ['ERROR_2']; return [null, value]; }
Il existe également une option similaire dans laquelle aucun tableau n'est renvoyé, mais l'objet {error, data} , mais il n'y a aucune différence fondamentale. L'avantage de cette approche est l'évidence, l'inconvénient est que maintenant partout où vous devez maintenir ce contrat. Pour la validation, cela ne cause aucun inconvénient, mais pour les transformations, cela est clairement superflu.
- Utilisez des exceptions. Bien qu'à mon avis une erreur de validation soit une situation standard dans l'application, rien n'est exceptionnel. Honnêtement, je pense que les exceptions ne sont mieux utilisées que lorsque quelque chose a vraiment mal tourné. En outre, des exceptions peuvent être appelées accidentellement dans les valideurs eux-mêmes, et vous ne savez peut-être pas du tout que c'était une erreur dans le code, et non dans la valeur. L'avantage de l'approche est la simplification de l'interface - maintenant, toujours la valeur est retournée de la manière habituelle, et l'erreur est levée comme exception.
- Il existe une option pour mettre une erreur dans une variable globale. Mais je ne tirerais pas l'État inutilement.
- Utilisez un type distinct pour les erreurs. Cela ressemble à l'option avec des exceptions, si vous leur enlevez le type d'erreur, mais ne le jetez pas.
function validate (value) { if (!check1(value)) return new Trava.ValidationError({ code: 401 }); if (!check2(value)) return new Trava.ValidationError({ code: 405 }); return parseOrTransform(value);
J'ai opté pour cette dernière option, bien que ce soit aussi un compromis, mais globalement pas mal.
Trava.ValidationError est proposé comme type d'erreur, qui hérite de l'
erreur standard et ajoute la possibilité d'utiliser un type de données arbitraire pour signaler une erreur. Il n'est pas nécessaire d'utiliser
Trava.ValidationError , vous pouvez utiliser l'
erreur standard, mais n'oubliez pas que le message d'erreur n'est que des chaînes.
Pour résumer, nous pouvons dire que le validateur est une fonction propre et synchrone qui, en plus de la valeur, peut renvoyer une erreur. Extrêmement simple. Et cette théorie fonctionne bien sans bibliothèques. Dans la pratique, les validateurs sont combinés en chaînes et hiérarchies, et ici l'herbe sera certainement utile.
La composition
La composition est peut-être le cas le plus courant de travailler avec des valideurs. La mise en œuvre de la composition peut être différente. Par exemple, dans les célèbres
bibliothèques joi et
v8n, cela se fait à travers un objet et une chaîne de méthodes:
Joi.string().alphanum().min(0).max(255)
Bien qu'elle soit belle à première vue, cette approche présente plusieurs inconvénients, dont l'un est fatal. Et voici la chose. D'après mon expérience, un validateur est toujours une chose pour une application spécifique, donc l'accent principal dans la bibliothèque devrait être sur la commodité d'étendre les validateurs et l'intégration avec l'approche existante, et non sur le nombre de primitives de base, qui, à mon avis, ne font qu'ajouter du poids à la bibliothèque, mais la plupart ne seront pas utilisés. Prenons par exemple le même validateur pour la chaîne. Ensuite, il s'avère que vous devez couper les espaces à partir des extrémités, puis soudain, vous devez autoriser l'utilisation de caractères spéciaux dans un seul cas, et quelque part, vous devez conduire à des minuscules, etc. En fait, il peut y avoir une infinité de telles primitives, et je ne vois tout simplement pas l'intérêt de commencer à les ajouter à la bibliothèque. À mon avis, l'utilisation d'objets est également redondante et entraîne une augmentation de la complexité lors de l'expansion, même si à première vue elle semble faciliter la vie. Par exemple, c
joi n'est pas si facile
d'écrire votre validateur .
Une approche fonctionnelle et de l'herbe ici peuvent aider. Le même exemple de validation d'un nombre spécifié dans la plage de 0 à 255:
L'instruction
Check fait un validateur de la vérification de la vérité (valeur => vrai / faux). Et
Compose enchaîne les validateurs. Une fois exécutée, la chaîne est interrompue après la première erreur. L'important est que les fonctions ordinaires soient utilisées partout, ce qui est très simple à développer et à utiliser. C'est cette facilité d'expansion, à mon avis, qui est une caractéristique clé d'une bibliothèque de validation valide.
Traditionnellement, une place distincte dans la validation est occupée par la vérification de la valeur
nulle et
non définie . Il y a des opérateurs auxiliaires dans l'herbe pour cela:
Il y a plusieurs autres opérateurs auxiliaires dans l'herbe, et ils composent tous magnifiquement et étonnamment simplement se développent. Comme les fonctions ordinaires :)
Hiérarchie
Les types de données simples sont organisés dans une hiérarchie. Les cas les plus courants sont les objets et les tableaux. Il y a des opérateurs dans l'herbe qui facilitent le travail avec eux:
Lors de la validation des objets, il a été décidé de souligner la sévérité de la définition: toutes les clés sont requises par défaut (encapsulées dans
Obligatoire ). Les clés non spécifiées dans le validateur sont supprimées.
Certains
jsonschema , solutions de
quatuor préfèrent décrire les validateurs sous forme de données, par exemple {x: 'nombre', y: 'nombre'}, mais cela conduit aux mêmes difficultés lors de l'expansion. Un avantage significatif de cette approche est la possibilité de sérialisation et d'échange de circuits, ce qui est impossible avec des fonctions. Cependant, cela peut être facilement implémenté au-dessus de l'interface fonctionnelle. Pas besoin de cacher les fonctions derrière les lignes! Les fonctions ont déjà des noms et c'est tout ce qui est nécessaire.
Pour faciliter l'utilisation à l'intérieur des valideurs, les opérateurs
Composer et
Clés peuvent être omis; il est également pratique d'envelopper le validateur racine dans
Trava :
const pointValidator = Trava({
Si vous appelez
Trava avec le deuxième argument, la valeur de retour sera le résultat de l'application du validateur:
const point = Trava({ x: [numberValidator, Trava.Check(v => v > 180)], y: [numberValidator, Trava.Check(v => v < 180)], },
Jusqu'à présent, la prise en charge a été implémentée uniquement pour les tableaux et les objets, comme empoisonner JSON et ça suffit. Tirez les demandes de Wellcome!
Contexte
Lorsque vous utilisez le validateur comme dernier paramètre, vous pouvez passer le contexte qui sera accessible à partir de tous les validateurs appelés comme dernier paramètre. Personnellement, cette opportunité ne m'a pas encore été utile, mais elle est possible.
Pour certains validateurs qui peuvent renvoyer une erreur, il est possible de définir un message d'erreur à différents niveaux. Un exemple:
const pos = Trava.Check(v => v > 0); pos(-1);
Remplacer pour un seul cas:
const pos = Trava.Check(v => v > 0, " "); pos(-1);
Remplacer pour tous les cas:
Trava.Check.ErrorMessage = " "; pos(-1);
De plus, pour une configuration plus détaillée, vous pouvez transférer une fonction à l'endroit de l'erreur, qui devrait retourner une erreur et sera appelée avec les paramètres du validateur.
Cas d'utilisation
La plupart du temps, nous empoisonnons JSON sur le backend avec koa. Le frontend s'assoit également lentement. Il est pratique d'avoir des valideurs communs aux deux extrémités. Et maintenant, je vais montrer un cas d'utilisation presque réel. Supposons que vous souhaitiez implémenter une API pour créer et mettre à jour les données des patients.
common / errors.jsconst trava = require ('trava');
fonction ValidationError (ctx, params) {
if (params instanceof Error) {
params = trava.ValidationError.extractData (params);
}
ctx.body = {
code: 'VALIDATION_ERROR',
params,
};
ctx.status = HttpStatus.BAD_REQUEST;
}
Bien que l'exemple soit très simple, il ne peut pas être qualifié de simplifié. Dans une application réelle, seuls les valideurs seront compliqués. Vous pouvez également effectuer la validation dans un middleware - le validateur est appliqué entièrement au contexte ou au corps de la requête.
Dans le processus de travail et d'utilisation de la validation, nous sommes arrivés à la conclusion que de simples valideurs synchrones et de simples messages d'erreur suffisent. En fait, nous sommes arrivés à la conclusion que nous n'utilisons que deux messages: «REQUIS» et «INVALIDE», qui sont localisés sur le frontend avec des invites pour les champs. Les autres vérifications qui nécessitent des actions supplémentaires (par exemple, lors de l'inscription pour vérifier qu'un tel courrier existe déjà) n'entrent pas dans le cadre de la validation. En tout cas, l'herbe ne concerne pas ce cas.
En conclusion
Dans ce court article, j'ai décrit presque toutes les fonctionnalités de la bibliothèque, en dehors de la portée de l'article, il existe plusieurs assistants simplifiant la vie. Je demande des détails sur github
github.com/uNmAnNeR/travajs .
Nous avions besoin d'un outil aussi personnalisable que possible, dans lequel il n'y a rien de superflu, mais en même temps il y a tout le nécessaire pour le travail quotidien. Et je pense qu'en général cela a été réalisé, j'espère que quelqu'un facilitera également la vie. Je serai heureux de souhaits et suggestions.
Pour la santé.