Om-yum-yum et validation des données

Bonjour à tous! Parlons un peu de la validation des données. Qu'est-ce qui est compliqué et pourquoi devrait-il être nécessaire, par exemple, dans un projet écrit en caractères dactylographiés? Le tapuscrit contrôle assez bien tout, il reste à vérifier l'entrée utilisateur maximale. Autrement dit, pour jeter une douzaine d'habitués dans le projet et tout, il semblerait, peut fermer le sujet, mais ... Loin de toujours, et dans le cas du Web, presque jamais, tout le projet est dans une seule base de code et utilise les mêmes types. A la jonction de telles bases de code, des situations surviennent lorsque l'attente ne correspond pas à la réalité et qu'ici le tapuscrit n'est plus un assistant. Quelques exemples:


  • La partie cliente de l'application reçoit des données de l'API et les valide. Premièrement, l'API peut soudainement et parfois sans préavis changer, et deuxièmement, les "gars du serveur" eux-mêmes ne savent parfois pas ce que leur API peut faire, par exemple, dans certains domaines, au lieu d'un tableau garanti, même s'il est vide, la pleine lune peut être donné null . Lors de la description des données sur le client, les programmeurs semblent déterminer avec quoi le client sait comment travailler et si quelque chose ne va pas, il est beaucoup plus agréable de voir immédiatement un message dans la console sur la source du problème, plutôt que de détecter un bogue incompréhensible déjà là où il est sorti dans la couche d'affichage. (et c'est bien si on le remarque immédiatement). De plus, il existe déjà des solutions ( 1 , 2 ) permettant de transférer des types du serveur au client. Je n'ai pas encore essayé de le faire, mais il est fort possible que ce soit l'avenir.
  • La situation inverse est lorsque le serveur vérifie les paramètres envoyés pour arrêter immédiatement le traitement de la demande s'ils ne répondent pas à ceux attendus. Je pense qu'il n'est pas nécessaire de préciser pourquoi il est important de le faire.
  • La validation des données avant leur stockage dans la base de données ne sera pas non plus superflue. Par exemple, vous pouvez voir comment il est organisé dans l'un de mes vélos: MaraquiaORM # Validation .

Je pense que les exemples sont assez convaincants et maintenant, il n'y a aucun sentiment que vous pouvez faire avec de simples habitués, car il ne s'agit pas seulement de saisie par l'utilisateur, mais de validation de complexes, généralement imbriqués sur plusieurs niveaux de données. Une bibliothèque spéciale est déjà nécessaire ici. Et bien sûr il y en a! Il se trouve qu'au cours des 10 dernières années, à chaque démarrage d'un nouveau projet, j'essaie d'utiliser une autre bibliothèque de ce type, en l'adaptant à mes besoins. Et chaque fois que quelque chose se passe mal, ce qui conduit parfois au remplacement du sujet testé au milieu d'un développement actif. Je ne parlerai pas de toutes les options que j'ai étudiées, je ne dirai que celles testées dans le projet actuel.


vérification de type


Github


Petite bibliothèque assez pratique. Le circuit est décrit comme une chaîne. En utilisant des chaînes multi-lignes, vous pouvez décrire des structures assez complexes:


 `{ ID: String, creator: { fname: String | Null, mname: String | Null, lname: String | Null, email: [String] } | Undefined, sender: Maybe { name: String, email: String }, type: Number, subject: String, ... }` 

Il y a des inconvénients assez sérieux:


  • L'IDE n'aide en aucune façon avec un ensemble de schémas, ce qui était particulièrement ennuyeux lors du passage à la dactylographie.
  • Messages d'erreur presque inutiles. Je n'ai pas utilisé cette bibliothèque depuis plus d'un an et peut-être que quelque chose a changé (à en juger par le code, non). Les messages étaient du style "Chaîne attendue, reçue nulle". Imaginez maintenant, vous avez un tableau de pièces pour 200 objets, dont chacun a des champs avec des chaînes, et dans un seul objet l'un des champs s'est cassé. Comment trouver ce champ? Voir les 200 articles? J'ai souffert tant de fois et ça m'a vraiment brisé la vie, ruiné l'impression de la bibliothèque. Habituellement, je ne veux pas savoir ce qui était attendu et reçu là-bas, mais je veux ouvrir le schéma de données et trouver le champ nécessaire dans celui-ci et le même dans les données elles-mêmes. En d'autres termes, dans le message d'erreur, il est essentiel d'avoir le chemin de clé au bon endroit dans les données / schéma, et ce qui était attendu là et arrivé à tout peut être omis.
  • Bien sûr, bien sûr, mais l'indentation dans l'exemple ci-dessus ne disparaîtra pas lorsque le code est compressé.

Joi


Github
Version du navigateur: joi-browser


Probablement la bibliothèque la plus célèbre sur ce sujet avec un tas de fonctionnalités et une API sans fin. Au début, je l'ai utilisé sur le serveur et il s'est montré parfaitement. À un moment donné, j'ai décidé de le remplacer par type-check sur le client. À cette époque, je ne contrôlais presque pas la taille du paquet, il n'y avait tout simplement aucun problème avec cela. Mais au cours de l'année, il a augmenté rapidement et sur Internet mobile, le premier téléchargement d'application n'était pas du tout confortable. Il a été décidé d'organiser un chargement paresseux des composants. Le rapport du webpack-bundle-analyzer a montré un tas de géants dans le bundle et ils sont tous allés facilement aux morceaux créés par le webpack. Tout le monde sauf Joi . De nombreux composants communiquent avec le serveur et toutes les réponses du serveur sont validées, c'est-à-dire que placer Joi dans une sorte de morceau n'a pas de sens, il sera simplement toujours chargé juste après le principal. À un moment donné, le bundle principal ressemblait à ceci: des tyts . Bien sûr, un désir durable a surgi de faire quelque chose. Je voulais la même bibliothèque pratique, mais beaucoup moins.


Ouaip


Github


Dans le fichier lisez-moi, ils promettent le même Joi , mais en taille adaptée à l'interface. En fait, il n'est que deux fois plus petit, c'est-à-dire que Yup était toujours la plus grande bibliothèque du bundle principal. De plus, des inconvénients supplémentaires sont apparus:


  • La bibliothèque par défaut ignore tout undefined . .required() pas très agréable, et j'aime mieux quand tout est initialement impossible et où cela est permis. Joi a une option presence: 'required' pour configurer ce comportement. J'ai créé une demande avec le numéro infernal 666 , mais jusqu'à présent, les auteurs sont silencieux.
  • Il n'y a aucun moyen de vérifier les valeurs de l'objet, en autorisant toutes les clés. Joi utilise object.pattern pour cela, le premier argument acceptant n'importe quelle chaîne. Il serait probablement toujours possible de s'en sortir, et les auteurs peuvent corriger le premier moins, mais étant donné la taille, je ne voulais pas attendre ou modifier moi-même quelque chose.

Ow


Github


Le candidat suivant s'est finalement avéré être vraiment petit, et il ne l'a pas forcé à écrire constamment () où vous pouvez vous en passer. Par exemple, vous pouvez écrire un validateur qui autorise une chaîne ou undefined comme suit:


 let optionalStringValidator = ow.optional.string; ow(optionalStringValidator, '1'); // Ok ow(optionalStringValidator, undefined); // Ok 

Super! Et null ? En retournant toute la documentation, j'ai trouvé la méthode suivante:


 ow.any(ow.optional.string, ow.null); 

Oh horreur! Lorsque j'ai essayé de réécrire une partie de la validation dans le projet, j'ai failli me casser les doigts en tapant ceci. J'ai ow.nullable problème en ajoutant ow.nullable , qui a été envoyé ici . En bref, ils disent que null n'est pas nécessaire du tout. Les arguments qui y sont donnés sont également tout à fait adéquats compte tenu de la première ligne de leur fichier Lisez-moi:


Validation des arguments de fonction pour les humains

Autrement dit, cette bibliothèque sert à valider les valeurs fournies comme arguments de la fonction. Apparemment, ils ne comptaient pas vraiment sur d’énormes structures imbriquées provenant du serveur.
Une étude plus approfondie et des tentatives d'utilisation ont révélé plusieurs autres fonctionnalités qui, encore une fois, étaient bien expliquées par la même ligne dans le fichier Lisezmoi, mais ne me convenaient pas très bien. C'est en fait une assez bonne bibliothèque, juste pour quelques autres fins.




Par ici, j'étais déjà complètement fatigué d'abandonner et j'ai décidé d'écrire ma bibliothèque avec le blackjack et les vierges. Oui, oui, je reviens avec le prochain vélo :). Rencontrez:


Omyumyum


Quelques exemples:


 import om from 'omyumyum'; const isOptionalNumber = om.number.or.undefined; isOptionalNumber('1'); // => false isOptionalNumber(1); // => true isOptionalNumber(undefined); // => true 

.or peut être utilisé autant de fois que vous le souhaitez en augmentant les options possibles:


 om.number.or.string.or.null.or.undefined; 

Dans ce cas, une fonction presque ordinaire est constamment générée qui prend n'importe quel argument et retourne un boolean .
Si vous souhaitez que la fonction génère une erreur en cas d'échec du test:


 om(om.number, '1'); //  TypeError 

Ou au curry:


 const isNumberOrThrow = om(om.number); isNumberOrThrow('1') //  TypeError 

La fonction résultante n'est pas tout à fait ordinaire, car elle dispose de méthodes supplémentaires. .or déjà affiché, une partie des méthodes dépendra du type sélectionné (voir API ), par exemple, une chaîne peut être améliorée avec une expression régulière:


 const isNonEmptyString = om.string.pattern(/\S/); // == `om.string.nonEmpty` isNonEmptyString(' '); // => false isNonEmptyString('1'); // => true 

Et pour l'objet, vous pouvez spécifier sa forme:


 const isUserData = om.object.shape({ name: om.string, age: om.number.or.vacuum // `.or.vacuum` == `.or.null.or.undefined` }); isUserData({}); // => false isUserData({ age: 20 }) // => false isUserData({ name: '' }); // => true isUserData({ name: '', age: null }); // => true isUserData({ name: '', age: 20 }); // => true 

Le chemin de clé promis à l'endroit problématique:


 om(om.array.of(om.object.shape({ name: om.string })), [{ name: '' }, { name: null }]); //  TypeError('Type mismatch at "[1].name"') 

Si les fonctionnalités intégrées ne sont pas suffisantes, vous pouvez toujours utiliser .custom(validator: (value: any) => boolean) :


 const isEmailOrPhone = om.custom(require('is-email')).or.custom(require('is-phone')); isEmailOrPhone('test@test.test'); // => true 

En stock est également attendu .and utilisé pour combiner et améliorer les types:


 const isNonZeroString = om.string.and.custom(str => str.length > 0); // == `om.string.nonZero` isNonZeroString(''); // => false isNonZeroString('1'); // => true 

.and a priorité sur .or , mais comme .custom() accepte le validateur exactement de la même forme que celle créée par la bibliothèque, cela peut être contourné:


 //      `age`,   `birthday` om.object.shape({ name: om.string }).and.custom( om.object.shape({ age: om.number }) .or.object.shape({ birthday: om.date })] ); 

Vous pouvez continuer à améliorer les validateurs créés précédemment. Les anciens ne se gâtent pas du tout. Essayons d'améliorer les isUserData créés précédemment:


 const isImprovedUserData = isUserData.and.object.shape({ friends: om.array.of(isUserData).or.vacuum }); isImprovedUserData({ name: '', age: 20, friends: [{ name: '', age: 18 }] }); // => true 

Eh bien, .not resté.


 const isNotVacuum = om.not.null.and.not.undefined; // == `om.not.vacuum` isNotVacuum(1); // => true isNotVacuum(null); // => false isNotVacuum(undefined); // => false 

D'autres méthodes disponibles peuvent être trouvées dans l'API de la bibliothèque.


Avantages de la bibliothèque:


  • Syntaxe laconique avec .or , .and , .not et un minimum de parenthèses. En combinaison avec la saisie semi-automatique, l'ensemble se transforme en plaisir.
  • Minuscule même par rapport à Ow (près de 10 fois moins (minify + gzip)), et par rapport à Joi bibliothèque est comme une plume à côté d'une montagne.
  • Joli nom :)

Inconvénients de la bibliothèque:


  • Moins de types et leurs modificateurs. Et il est peu probable qu'il y en ait beaucoup plus. Les trois scénarios d'utilisation donnés au début de l'article (quelque chose sur les jonctions et les bases de code) supposent la transmission de données en texte brut, dans la plupart des cas, c'est JSON. C'est, à mon avis, une telle bibliothèque devrait prendre en charge les types possibles dans JSON, ainsi que les types undefined et quelques types couramment utilisés. Le même Ow pour une raison quelconque, est rempli de prise en charge de tous les types de tableaux typés et autres non-sens. Je pense que c'est superflu.
  • Impossible de convertir des données comme Joi . Je pense que Joi est assez mauvais aussi. Au moins, je n'ai pas assez de ses capacités et, si nécessaire, je fais des transformations avec des outils complètement différents. Il s'agit peut-être d'une nouvelle direction de développement pour l' omyumyum .

C’est tout! Si vous avez aimé l'article, aimez-le, abonnez-vous à la chaîne et bonne chance)).

Source: https://habr.com/ru/post/fr473026/


All Articles