
In meinem
vorherigen Beitrag habe ich die wichtigsten Punkte bei der Entwicklung einer anderen
OpenSource-Bibliothek beschrieben . Ich habe vergessen, noch etwas zu erwähnen: Wenn Sie niemandem von der Bibliothek erzählen, was auch immer es sein mag, wird höchstwahrscheinlich niemand davon erfahren.
Treffen Sie also
trava.js - eine saftige Validierung zum Nutzen des Projekts. Übrigens verwenden wir seit mehr als sechs Monaten Gras, und ich dachte, es wäre an der Zeit, Ihnen die Vorteile der Verwendung zu erläutern. Bereits getrocknet, also halten Sie den Atem an. Und mach weiter.
Konzept
Auf den ersten Blick scheint die Validierung ein triviales Thema zu sein, das keine besondere Aufmerksamkeit erfordert. Der Wert ist entweder wahr oder nicht, was einfacher sein könnte:
function validate (value) {
Aber normalerweise wäre es schön zu wissen, was genau schief gelaufen ist:
function validate (value) { if (!check1(value)) return 'ERROR_1'; if (!check2(value)) return 'ERROR_2'; }
Eigentlich ist das alles, das Problem ist gelöst.
Wenn nicht für ein "aber".
Aus der Erfahrung mit der Entwicklung realer Anwendungen wurde festgestellt, dass die Angelegenheit nicht mit der Validierung endet. In der Regel müssen diese Daten auch in ein bestimmtes Format konvertiert werden, aus irgendeinem Grund, der vom Serializer nicht sofort unterstützt wird, z. B. Datumsangaben, Sätze oder andere benutzerdefinierte Datentypen. Da dies hauptsächlich JSON ist, stellt sich in der Praxis heraus, dass Sie während der Validierung und Transformation einen doppelten Durchlauf durch die Eingabedatenstruktur durchführen müssen. Die Idee kam auf, warum nicht diese beiden Stufen zu einer kombinieren. Ein mögliches Plus wäre auch das Vorhandensein eines expliziten deklarativen Datenschemas.
Um die Konvertierung eines Werts in ein bestimmtes Format zu unterstützen, muss der Validator nicht nur einen Fehler, sondern auch einen reduzierten Wert zurückgeben können. In der js-Welt sind mehrere Schnittstellenoptionen mit möglichen Fehlerrückgaben ziemlich häufig.
- Am häufigsten ist wahrscheinlich die Rückgabe des Tupels [error, data]:
function validate (value) { if (!check1(value)) return ['ERROR_1']; if (!check2(value)) return ['ERROR_2']; return [null, value]; }
Es gibt auch eine ähnliche Option, bei der kein Array zurückgegeben wird, sondern das Objekt {error, data} , aber es gibt keine grundlegenden Unterschiede. Der Vorteil dieses Ansatzes ist die Offensichtlichkeit, das Minus ist, dass Sie diesen Vertrag jetzt überall pflegen müssen. Für die Validierung verursacht dies keine Unannehmlichkeiten, für Transformationen ist dies jedoch eindeutig überflüssig.
- Verwenden Sie Ausnahmen. Obwohl meiner Meinung nach ein Validierungsfehler eine Standardsituation in der Anwendung ist, ist nichts außergewöhnlich. Ehrlich gesagt denke ich, dass Ausnahmen am besten nur verwendet werden, wenn etwas wirklich schief gelaufen ist. Außerdem können Ausnahmen versehentlich in den Validatoren selbst aufgerufen werden, und dann wissen Sie möglicherweise überhaupt nicht, dass es sich um einen Fehler im Code und nicht im Wert handelt. Der Vorteil des Ansatzes ist die Vereinfachung der Schnittstelle - jetzt wird der Wert immer auf die übliche Weise zurückgegeben und der Fehler wird als Ausnahme ausgelöst.
- Es besteht die Möglichkeit , einen Fehler in eine globale Variable einzufügen. Aber ich würde den Staat nicht unnötig ziehen.
- Verwenden Sie für Fehler einen separaten Typ. Es sieht aus wie die Option mit Ausnahmen, wenn Sie die Art des Fehlers von ihnen nehmen, aber nicht wegwerfen.
function validate (value) { if (!check1(value)) return new Trava.ValidationError({ code: 401 }); if (!check2(value)) return new Trava.ValidationError({ code: 405 }); return parseOrTransform(value);
Ich habe mich für die letztere Option entschieden, obwohl dies auch ein Kompromiss ist, aber insgesamt nicht schlecht.
Trava.ValidationError wird als Typ für den Fehler vorgeschlagen, der vom Standardfehler erbt und die Möglichkeit bietet, einen beliebigen Datentyp zum Melden eines Fehlers zu verwenden. Es ist nicht erforderlich,
Trava.ValidationError zu verwenden. Sie können den Standardfehler verwenden.
Vergessen Sie jedoch nicht, dass die Fehlermeldung nur aus Zeichenfolgen besteht.
Zusammenfassend können wir sagen, dass der Validator eine saubere, synchrone Funktion ist, die zusätzlich zum Wert einen Fehler zurückgeben kann. Sehr einfach. Und diese Theorie funktioniert gut ohne Bibliotheken. In der Praxis werden Validatoren zu Ketten und Hierarchien zusammengefasst, und hier wird das Gras definitiv nützlich sein.
Zusammensetzung
Vielleicht ist die Zusammensetzung der häufigste Fall bei der Arbeit mit Validatoren. Die Umsetzung der Zusammensetzung kann unterschiedlich sein. In den berühmten
Bibliotheken joi und
v8n erfolgt dies beispielsweise über ein Objekt und eine Methodenkette:
Joi.string().alphanum().min(0).max(255)
Obwohl es auf den ersten Blick schön aussieht, hat dieser Ansatz mehrere Nachteile, und einer ist fatal. Und hier ist das Ding. Nach meiner Erfahrung ist ein Validator immer eine Sache für eine bestimmte Anwendung, daher sollte das Hauptaugenmerk in der Bibliothek auf der Bequemlichkeit der Erweiterung der Validatoren und der Integration in den bestehenden Ansatz liegen und nicht auf der Anzahl der Grundelemente, die meiner Meinung nach nur der Bibliothek Gewicht verleihen, sondern Die meisten werden nicht verwendet. Nehmen Sie zum Beispiel den gleichen Validator für die Zeichenfolge. Dann stellt sich heraus, dass Sie die Leerzeichen von den Enden abschneiden müssen, dann müssen Sie plötzlich die Verwendung von Sonderzeichen in einem einzigen Fall zulassen und irgendwo müssen Sie zu Kleinbuchstaben usw. führen. Tatsächlich kann es unendlich viele solcher Grundelemente geben, und ich sehe keinen Sinn darin, sie überhaupt zur Bibliothek hinzuzufügen. Meiner Meinung nach ist die Verwendung von Objekten ebenfalls redundant und führt zu einer Zunahme der Komplexität während der Erweiterung, obwohl dies auf den ersten Blick das Leben einfacher zu machen scheint. Zum Beispiel
ist es nicht so einfach
, Ihren Validator zu schreiben .
Ein funktionaler Ansatz und Gras hier können helfen. Das gleiche Beispiel für die Validierung einer im Bereich von 0 bis 255 angegebenen Zahl:
Die
Check- Anweisung macht aus der Wahrheitsprüfung einen Validator (Wert => wahr / falsch). Und
Compose verkettet die Validatoren. Bei der Ausführung wird die Kette nach dem ersten Fehler unterbrochen. Wichtig ist, dass überall gewöhnliche Funktionen verwendet werden, die sehr einfach zu erweitern und zu verwenden sind. Diese einfache Erweiterung ist meiner Meinung nach ein wesentliches Merkmal einer gültigen Validierungsbibliothek.
Traditionell wird ein separater Platz in der Validierung belegt, indem nach
null und
undefiniert gesucht wird . Hierfür gibt es Hilfskräfte im Gras:
Es gibt mehrere weitere Helfer im Gras, und alle komponieren wunderschön und erweitern sich überraschend einfach. Wie gewöhnliche Funktionen :)
Hierarchie
Einfache Datentypen sind in einer Hierarchie organisiert. Die häufigsten Fälle sind Objekte und Arrays. Es gibt Bediener im Gras, die die Arbeit mit ihnen erleichtern:
Bei der Validierung von Objekten wurde beschlossen, den Schweregrad der Definition hervorzuheben: Alle Schlüssel sind standardmäßig erforderlich (eingeschlossen in
Erforderlich ). Schlüssel, die nicht im Validator angegeben sind, werden verworfen.
Einige
jsonschema ,
Quartettlösungen bevorzugen es, Validatoren in Form von Daten zu beschreiben, zum Beispiel {x: 'number', y: 'number'}, aber dies führt zu den gleichen Schwierigkeiten beim Erweitern. Ein wesentlicher Vorteil dieses Ansatzes ist die Möglichkeit der Serialisierung und des Austauschs von Schaltungen, die mit Funktionen unmöglich ist. Dies kann jedoch problemlos über die Funktionsschnittstelle implementiert werden. Keine Notwendigkeit, Funktionen hinter den Linien zu verstecken! Funktionen haben bereits Namen und das ist alles, was benötigt wird.
Zur Vereinfachung der Verwendung in Validatoren können die Operatoren
Compose und
Keys weggelassen werden. Es ist auch praktisch, den Root-Validator in
Trava zu verpacken:
const pointValidator = Trava({
Wenn Sie
Trava mit dem zweiten Argument aufrufen, ist der Rückgabewert das Ergebnis der Anwendung des Validators:
const point = Trava({ x: [numberValidator, Trava.Check(v => v > 180)], y: [numberValidator, Trava.Check(v => v < 180)], },
Bisher wurde die Unterstützung nur für Arrays und Objekte implementiert im Grunde JSON vergiften und das ist genug. Pull Anfragen für Wellcome!
Kontext
Wenn Sie den Validator als letzten Parameter verwenden, können Sie den Kontext, auf den alle aufgerufenen Validatoren zugreifen können, als letzten Parameter übergeben. Persönlich hat sich diese Gelegenheit für mich noch nicht als nützlich erwiesen, aber es ist möglich.
Für einige Validatoren, die möglicherweise einen Fehler zurückgeben, ist es möglich, eine Fehlermeldung auf verschiedenen Ebenen zu definieren. Ein Beispiel:
const pos = Trava.Check(v => v > 0); pos(-1);
Überschreiben für einen Einzelfall:
const pos = Trava.Check(v => v > 0, " "); pos(-1);
Überschreiben für alle Fälle:
Trava.Check.ErrorMessage = " "; pos(-1);
Für eine detailliertere Konfiguration können Sie auch eine Funktion an der Stelle des Fehlers übertragen, die einen Fehler zurückgeben sollte und mit den Validatorparametern aufgerufen wird.
Anwendungsfall
Meistens vergiften wir JSON im Backend zusammen mit Koa. Das Frontend setzt sich ebenfalls langsam auf. Es ist zweckmäßig, an beiden Enden gemeinsame Prüfer zu haben. Und jetzt werde ich einen fast realen Anwendungsfall zeigen. Angenommen, Sie möchten eine API zum Erstellen und Aktualisieren von Patientendaten implementieren.
common / error.jsconst trava = erfordern ('trava');
Funktion ValidationError (ctx, params) {
if (params instanceof Error) {
params = trava.ValidationError.extractData (params);
}}
ctx.body = {
Code: 'VALIDATION_ERROR',
params,
};
ctx.status = HttpStatus.BAD_REQUEST;
}}
Obwohl das Beispiel sehr einfach ist, kann es nicht als vereinfacht bezeichnet werden. In einer realen Anwendung sind nur Validatoren kompliziert. Sie können die Validierung auch in Middleware durchführen. Der Validator wird vollständig auf den Kontext oder den Anforderungshauptteil angewendet.
Während des Arbeitens und der Verwendung der Validierung kamen wir zu dem Schluss, dass einfache synchrone Validatoren und einfache Fehlermeldungen völlig ausreichend sind. Tatsächlich kamen wir zu dem Schluss, dass wir nur zwei Nachrichten verwenden: "ERFORDERLICH" und "UNGÜLTIG", die zusammen mit den Eingabeaufforderungen für die Felder im Frontend lokalisiert sind. Andere Überprüfungen, die zusätzliche Aktionen erfordern (z. B. bei der Registrierung, um zu überprüfen, ob solche E-Mails bereits vorhanden sind), liegen außerhalb des Validierungsbereichs. In jedem Fall geht es im Gras nicht um diesen Fall.
Abschließend
In diesem kurzen Artikel habe ich fast die gesamte Funktionalität der Bibliothek beschrieben. Außerhalb des Geltungsbereichs des Artikels gibt es mehrere Helfer, die das Leben vereinfachen. Ich bitte um Details auf github
github.com/uNmAnNeR/travajs .
Wir brauchten ein Werkzeug, das so individuell wie möglich angepasst werden kann, in dem nichts überflüssig ist, aber gleichzeitig alles für die tägliche Arbeit notwendig ist. Und ich denke im Allgemeinen wurde dies erreicht, ich hoffe, jemand wird auch das Leben leichter machen. Ich freue mich über Wünsche und Anregungen.
Gesundheit.