Hintergrund
Nachdem wir uns an zahlreichen Stellen in der Javascript-Entwicklung mit Situationen getroffen hatten, in denen es notwendig war, Werte zu validieren, wurde klar, dass es notwendig war, dieses Problem irgendwie zu lösen. Zu diesem Zweck wurde folgende Aufgabe gestellt:
Entwickeln Sie eine Bibliothek, die Folgendes ermöglicht:
- Datentypen validieren;
- Standardwerte anstelle ungültiger Felder oder Elemente festlegen ;
- ungültige Teile eines Objekts oder Arrays löschen ;
- Erhalten Sie eine Fehlermeldung
Die Basis davon wird sein:
- Leicht zu lernen
- Lesbarkeit des empfangenen Codes.
- Einfache Codeänderung
Um diese Ziele zu erreichen, wurde eine Validierungsbibliothek für Quartette entwickelt.
Grundlegende Validierungsbausteine
Das Herzstück der meisten Systeme, die für eine Vielzahl von Aufgaben ausgelegt sind, sind die einfachsten Elemente : Aktionen, Daten und Algorithmen. Sowie Methoden ihrer Zusammensetzung - um aus einfachen Elementen etwas Komplizierteres zusammenzusetzen, um komplexere Probleme zu lösen.
Validator
Die Quartettbibliothek basiert auf dem Konzept eines Validators . Die Validatoren in dieser Bibliothek sind Funktionen der folgenden Form
function validator( value: any, { key: string|int, parent: any }, { key: string|int, parent: any }, ... ): boolean
In dieser Definition gibt es mehrere Dinge, die genauer beschrieben werden sollten:
function(...): boolean
Wert - sagt, dass der Validator - das Ergebnis der Validierung berechnet und das Ergebnis der Validierung ein boolescher Wert ist - wahr oder falsch , jeweils gültig oder ungültig
value: any
- gibt an, dass der Validator - das Ergebnis der Validierung eines Werts berechnet , bei dem es sich um einen beliebigen Javascript-Wert handeln kann. Der Validator weist den validierten Wert entweder gültig oder ungültig zu.
{ key: string|int, parent: any }, ...
- gibt an, dass sich der validierte Wert in verschiedenen Kontexten befinden kann, je nachdem, auf welcher Verschachtelungsebene der Wert liegt. Lassen Sie es uns anhand von Beispielen zeigen
Beispielwert ohne Kontext
const value = 4;
Beispielwert in einem Array-Kontext
Beispielwert im Kontext eines Objekts
const obj = { a: 1, b: 2, c: value, d: 8 }
Da Strukturen in einem Objekt eine größere Verschachtelung aufweisen können, ist es sinnvoll, über eine Vielzahl von Kontexten zu sprechen
const arrOfObj = [{ a: 1, b: 2, c: value, d: 8 },
Usw.
Über die Ähnlichkeit mit Array-MethodenDiese Definition eines Validators sollte Sie an die Definition von Funktionen erinnern, die als Argument an Array-Methoden übergeben werden, z. B.: Map, Filter, einige, alle usw.
- Das erste Argument für diese Funktionen ist ein Array-Element.
- Das zweite Argument ist der Index des Elements.
- Das dritte Argument ist das Array selbst.
Der Validator ist in diesem Fall eine allgemeinere Funktion - er verwendet nicht nur den Index des Elements im Array und des Arrays, sondern auch den Index des Arrays - in seinem übergeordneten und seinem übergeordneten Element und so weiter.
Was sollen wir ein Haus bauen?
Die oben beschriebenen Steine heben sich nicht von anderen "Steinlösungen" ab , die am "Strand" der Javascript-Krücke liegen. Lassen Sie uns deshalb etwas Kohärenteres und Interessanteres daraus machen. Dafür haben wir eine Komposition .
Wie baue ich einen Wolkenkratzer für die Objektvalidierung?
Stimmen Sie zu, es wäre zweckmäßig, Objekte so zu validieren, dass die Beschreibung der Validierung mit der Beschreibung des Objekts übereinstimmt. Hierfür verwenden wir die Objektzusammensetzung von Validatoren . Es sieht so aus:
Wie Sie sehen können, können wir aus verschiedenen für bestimmte Felder definierten Validierungsbausteinen einen Objektvalidator zusammenstellen - ein "kleines Gebäude", das immer noch ziemlich überfüllt ist - aber besser als ohne. Dazu verwenden wir den Composer of Validators v
. Jedes Mal, wenn das Objektliteral v
anstelle des Validators erfüllt wird, wird es als Objektzusammensetzung betrachtet und in seinen Feldern in einen Objektvalidator umgewandelt.
Manchmal können wir nicht alle Felder beschreiben . Wenn ein Objekt beispielsweise ein Datenwörterbuch ist:
const quartet = require('quartet') const v = quartet() const isStringValidator = name => typeof name === 'string' const keyValueValidator = (value, { key }) => value.length === 1 && key.length === 1 const dictionarySchema= { dictionaryName: isStringValidator, ...v.rest(keyValueValidator) } const compositeObjValidator = v(dictionarySchema) const obj = { dictionaryName: 'next letter', b: 'c', c: 'd' } const isObjValid = compositeObjValidator(obj) console.log(isObjValid)
Wie kann man Baulösungen wiederverwenden?
Wie wir oben gesehen haben, müssen einfache Validatoren wiederverwendet werden. In diesen Beispielen mussten wir den "String Type Validator" bereits zweimal verwenden.
Um den Datensatz zu verkürzen und seine Lesbarkeit zu verbessern, verwendet die Quartettbibliothek Stringsynonyme von Validatoren. Immer wenn ein Validator-Komponist an der Stelle, an der sich der Validator befinden sollte, auf eine Zeichenfolge stößt, durchsucht er das Wörterbuch nach seinem Validator und verwendet ihn .
Standardmäßig sind die gängigsten Validatoren bereits in der Bibliothek definiert.
Betrachten Sie die folgenden Beispiele:
v('number')(1)
und viele andere in der Dokumentation beschrieben .
Jeder Bogen hat seine eigene Art von Ziegeln?
Der Validator Composer (Funktion v
) ist ebenfalls eine Validator Factory. In dem Sinne, dass es viele nützliche Methoden enthält, die zurückkehren
- Funktionsprüfer
- Werte, die der Komponist als Schemata zum Erstellen von Validatoren wahrnimmt
Schauen wir uns zum Beispiel die Array-Validierung an: Meistens besteht sie darin, den Typ des Arrays und alle seine Elemente zu überprüfen. Wir werden hierfür die Methode v.arrayOf(elementValidator)
verwenden. Nehmen Sie zum Beispiel ein Array von Punkten mit Namen.
const a = [ {x: 1, y: 1, name: 'A'}, {x: 2, y: 1, name: 'B'}, {x: -1, y: 2, name: 'C'}, {x: 1, y: 3, name: 'D'}, ]
Da ein Array von Punkten ein Array von Objekten ist, ist es sinnvoll, die Objektzusammensetzung zu verwenden, um die Elemente des Arrays zu validieren.
const namedPointSchema = { x: 'number',
Erstellen Sie nun mit der Factory-Methode v.arrayOf
einen Validator für das gesamte Array.
const isArrayValid = v.arrayOf({ x: 'number', y: 'number', name: 'string' })
Mal sehen, wie dieser Validator funktioniert:
isArrayValid(0)
Dies ist nur eine der Factory-Methoden, von denen jede in der Dokumentation beschrieben ist.
Wie Sie oben gesehen haben, ist v.rest
auch eine Factory-Methode, die eine Objektzusammensetzung zurückgibt, die alle Felder überprüft, die nicht in der Objektzusammensetzung angegeben sind. Das heißt, es kann mit dem spread-operator
in eine andere Objektzusammensetzung eingebettet spread-operator
.
Nennen wir als Beispiel die Verwendung mehrerer von ihnen:
Sein oder Nichtsein?
Es kommt häufig vor, dass gültige Daten verschiedene Formen annehmen, zum Beispiel:
id
kann eine Zahl oder eine Zeichenfolge sein.- Das Punktobjekt kann je nach Dimension einige Koordinaten enthalten oder nicht.
- Und viele andere Fälle.
Um die Validierung von Varianten zu organisieren, wird ein separater Kompositionstyp bereitgestellt - Variantenkomposition. Es wird durch eine Reihe von Validatoren möglicher Optionen dargestellt. Ein Objekt gilt als gültig, wenn mindestens einer der Validatoren seine Gültigkeit meldet.
Betrachten Sie ein Beispiel mit der Bezeichnervalidierung:
const isValidId = v([ v.and('not-empty', 'string'),
Beispiel für die Punktvalidierung:
const isPointValid = v([ {
Wenn ein Komponist ein Array sieht, betrachtet er es als eine Zusammensetzung der Validatorelemente dieses Arrays, sodass die Berechnung der Validierung stoppt und der Wert als gültig erkannt wird, wenn einer von ihnen den Wert für gültig hält.
Wie wir sehen, betrachtet der Komponist nicht nur die Validatorfunktion als Validator, sondern auch alles, was zu einer Validatorfunktion führen kann.
Validatortyp | Beispiel | Wie vom Komponisten wahrgenommen |
---|
Validierungsfunktion | x => typeof x === 'bigint' | habe gerade die notwendigen Werte aufgerufen |
Objektzusammensetzung | { a: 'number' } | Erstellt eine Validierungsfunktion für ein Objekt basierend auf den angegebenen Feldvalidatoren |
Variantenzusammensetzung | ['number', 'string'] | Erstellt eine Validierungsfunktion, um einen Wert mit mindestens einer der Optionen zu validieren |
Factory Method Call-Ergebnisse | v.enum('male', 'female') | Die meisten Factory-Methoden geben Validierungsfunktionen zurück (mit Ausnahme von v.rest , das die Objektzusammensetzung zurückgibt), sodass sie wie reguläre Validierungsfunktionen behandelt werden |
Alle diese Validatoroptionen sind gültig und können überall im Schema verwendet werden, wo sich der Validator befinden sollte.
Infolgedessen sieht das Arbeitsschema immer so aus: v(schema)
gibt eine Validierungsfunktion zurück. Als nächstes wird diese Validierungsfunktion für bestimmte Werte aufgerufen:
v(schema)(value[, ...parents])
Haben Sie auf der Baustelle Unfälle gehabt?
- Noch nicht, keiner
- Sie werden!
Es kommt vor, dass die Daten ungültig sind und wir in der Lage sein müssen, die Ursache für die Ungültigkeit zu ermitteln.
Hierfür bietet die Quartettbibliothek einen Erklärungsmechanismus . Es besteht darin, dass der Validator, ob intern oder extern, die Gültigkeit der verifizierten Daten feststellt , eine Erläuterung senden muss.
Für diese Zwecke wird das zweite Argument des Komponisten der Validatoren v
. Es fügt den Nebeneffekt hinzu, dass bei ungültigen Daten eine Erläuterung an das Array v.explanation
.
Lassen Sie uns beispielsweise ein Array validieren und die Anzahl aller ungültigen Elemente und ihren Wert herausfinden:
Wie Sie sehen können, hängt die Wahl der Erklärung von der Aufgabe ab. Manchmal ist es gar nicht nötig.
Manchmal müssen wir etwas mit ungültigen Feldern tun. In solchen Fällen ist es sinnvoll, den Namen des ungültigen Feldes als Erklärung zu verwenden :
const objSchema = { a: v('number', 'a'), b: v('number', 'b'), c: v('string', 'c') } const isObjValid = v(objSchema) let invalidObj = { a: 1, b: '1', c: 3 } isObjValid(invalidObj)
Mit diesem Erklärungsmechanismus können Sie jedes Verhalten implementieren, das mit den Validierungsergebnissen verbunden ist.
Eine Erklärung kann alles sein:
- ein Objekt, das die erforderlichen Informationen enthält;
- Funktion, die den Fehler korrigiert. (
getExplanation => function(invalid): valid
); - Name des ungültigen Feldes oder Index des ungültigen Elements;
- Fehlercode
- und das alles reicht für deine Fantasie.
Was tun, wenn keine Dinge gebaut werden?
Das Korrigieren von Validierungsfehlern ist keine seltene Aufgabe. Zu diesem Zweck verwendet die Bibliothek Validatoren mit einem Nebeneffekt, der sich an den Ort des Fehlers und dessen Behebung erinnert.
v.default(validator, value)
- v.default(validator, value)
einen Validator zurück, der sich an einen ungültigen Wert erinnert, und legt zum Zeitpunkt des Aufrufs von v.fix
den Standardwert festv.filter(validator)
- v.filter(validator)
einen Validator zurück, der sich an einen ungültigen Wert erinnert, und entfernt diesen Wert zum Zeitpunkt des Aufrufs von v.fix
vom übergeordneten Wertv.addFix(validator, fixFunc)
- gibt einen Validator zurück, der sich an einen ungültigen Wert erinnert, und ruft zum Zeitpunkt des Aufrufs von v.fix
fixFunc mit Parametern (value, {key, parent}, ...) auf. fixFunc
- muss einen der Partner mutieren - um den Wert zu ändern
const toPositive = (negativeValue, { key, parent }) => { parent[key] = -negativeValue } const objSchema = { a: v.default('number', 1), b: v.filter('string', ''), c: v.default('array', []), d: v.default('number', invalidValue => Number(invalidValue)),
Aufgaben sind immer noch nützlich
In dieser Bibliothek gibt es auch Dienstprogrammmethoden für Validierungsaktionen:
Methode | Ergebnis |
---|
v.throwError | Wenn ungültig, wird ein TypeError mit der angegebenen Nachricht ausgelöst. |
v.omitInvalidItems | Gibt ein neues Array (oder Wörterbuchobjekt) ohne ungültige Elemente (Felder) zurück. |
v.omitInvalidProps | Gibt ein neues Objekt ohne ungültige Felder gemäß dem angegebenen Objektvalidator zurück. |
v.validOr | Gibt den Wert zurück, wenn er gültig ist, andernfalls wird er durch den angegebenen Standardwert ersetzt. |
v.example | Überprüft, ob die angegebenen Werte zum Schema passen. Wenn sie nicht passen, wird ein Fehler ausgegeben. Dient als Dokumentation und Schaltungstest |
Ergebnisse
Die Aufgaben wurden folgendermaßen gelöst:
Herausforderung | Lösung |
---|
Datentypüberprüfung | Standardmäßig benannte Validatoren. |
Standardwerte | v.default |
Ungültige Teile entfernen | v.filter , v.omitInvalidItems und v.omitInvalidProps . |
Leicht zu lernen | Einfache Validatoren, einfache Möglichkeiten, sie zu komplexen Validatoren zusammenzusetzen. |
Lesbarkeit des Codes | Eines der Ziele der Bibliothek war es, die Validierungsschemata selbst zu vergleichen |
validierte Objekte. |
Einfache Änderung | Wenn Sie die Elemente von Kompositionen beherrschen und Ihre eigenen Validierungsfunktionen verwenden, ist das Ändern des Codes ganz einfach. |
Fehlermeldung | Erläuterung in Form einer Fehlermeldung. Oder Fehlercodeberechnung basierend auf Erklärungen. |
Nachwort
Diese Lösung wurde entwickelt, um schnell und bequem Validierungsfunktionen mit der Möglichkeit zu erstellen, benutzerdefinierte Validierungsfunktionen einzubetten. Daher sind Korrekturen, Kritik und Verbesserungsmöglichkeiten von Personen, die diesen Artikel lesen, willkommen. Vielen Dank für Ihre Aufmerksamkeit.