Om-yum-yum und Datenvalidierung

Hallo allerseits! Lassen Sie uns ein wenig über die Datenvalidierung sprechen. Was ist kompliziert und warum sollte es beispielsweise in einem in Typoskript geschriebenen Projekt überhaupt benötigt werden? Typoskript steuert ziemlich gut alles, es bleibt die maximale Benutzereingabe zu überprüfen. Das heißt, ein Dutzend Stammgäste in das Projekt zu werfen und alles, so scheint es, kann das Thema schließen, aber ... Weit davon entfernt, immer und im Falle des Webs fast nie, befindet sich das gesamte Projekt in einer einzigen Codebasis und verwendet dieselben Typen. An der Kreuzung solcher Codebasen entstehen Situationen, in denen das Warten nicht der Realität entspricht und das Typoskript hier kein Assistent mehr ist. Einige Beispiele:


  • Der Client-Teil der Anwendung empfängt Daten von der API und validiert sie. Erstens kann sich die API plötzlich und manchmal ohne vorherige Ankündigung ändern, und zweitens wissen die "Server-Leute" selbst manchmal nicht, was ihre API tun kann, zum Beispiel in einem bestimmten Bereich, anstatt eines garantierten Arrays, selbst wenn es leer ist, kann der Vollmond null . Bei der Beschreibung der Daten auf dem Client scheinen Programmierer zu bestimmen, mit was der Client zu arbeiten weiß. Wenn etwas schief geht, ist es viel angenehmer, sofort eine Meldung in der Konsole über die Ursache des Problems zu sehen, als einen unverständlichen Fehler zu erkennen, der bereits in der Ansichtsebene aufgetreten ist (und es ist gut, wenn es sofort bemerkt wird). Auch jetzt gibt es bereits Lösungen ( 1 , 2 ), mit denen Typen vom Server zum Client übertragen werden können. Ich habe es noch nicht versucht, aber es ist durchaus möglich, dass dies die Zukunft ist.
  • Die umgekehrte Situation ist, wenn der Server die gesendeten Parameter überprüft, um die Verarbeitung der Anforderung sofort zu beenden, wenn sie nicht den erwarteten entsprechen. Ich denke, es sind keine Details erforderlich, warum dies wichtig ist.
  • Die Validierung von Daten vor dem Speichern in der Datenbank ist ebenfalls nicht überflüssig. Zum Beispiel können Sie sehen, wie es in einem meiner Motorräder organisiert ist: MaraquiaORM # Validation .

Ich denke, die Beispiele sind ziemlich überzeugend und jetzt gibt es kein Gefühl, dass Sie mit einfachen Stammgästen umgehen können, da es nicht nur um Benutzereingaben geht, sondern um die Validierung komplexer, normalerweise auf mehreren Datenebenen verschachtelter Daten. Hier wird bereits eine spezielle Bibliothek benötigt. Und natürlich gibt es! Es kommt einfach so vor, dass ich in den letzten 10 Jahren jedes Mal, wenn ich ein neues Projekt starte, versuche, eine andere solche Bibliothek darin zu verwenden und sie an meine Bedürfnisse anzupassen. Und jedes Mal, wenn etwas schief geht, führt dies manchmal dazu, dass die Testperson mitten in der aktiven Entwicklung ausgetauscht wird. Ich werde nicht über alle Optionen sprechen, die ich studiert habe, sondern nur über die im aktuellen Projekt getesteten.


Typprüfung


Github


Kleine und recht praktische Bibliothek. Die Schaltung wird als String beschrieben. Mit mehrzeiligen Zeichenfolgen können Sie ziemlich komplexe Strukturen beschreiben:


 `{ 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, ... }` 

Es gibt ziemlich schwerwiegende Nachteile:


  • Die IDE hilft in keiner Weise bei einer Reihe von Schemata, was besonders beim Wechsel zu Typoskript ärgerlich war.
  • Fast nutzlose Fehlermeldungen. Ich habe diese Bibliothek seit mehr als einem Jahr nicht mehr benutzt und vielleicht hat sich etwas geändert (nach dem Code zu urteilen, nein). Nachrichten hatten den Stil "Erwartete Zeichenfolge, null erhalten". Stellen Sie sich nun vor, Sie haben eine Reihe von Teilen für 200 Objekte, von denen jedes Felder mit Zeichenfolgen enthält, und in nur einem Objekt ist eines der Felder gebrochen. Wie finde ich dieses Feld? Alle 200 Artikel anzeigen? Ich habe so oft gelitten und es hat mein Leben wirklich gebrochen und den Eindruck der Bibliothek ruiniert. Normalerweise möchte ich nicht wissen, was dort erwartet und empfangen wurde, aber ich möchte das Datenschema öffnen und das darin benötigte Feld und dasselbe in den Daten selbst finden. Mit anderen Worten, in der Fehlermeldung ist es wichtig, den Schlüsselpfad an der richtigen Stelle in den Daten / im Schema zu haben, und was dort erwartet wurde und überhaupt angekommen ist, kann weggelassen werden.
  • Natürlich eine Kleinigkeit, aber die Einrückung im obigen Beispiel verschwindet nicht, wenn der Code komprimiert wird.

Joi


Github
Browserversion : joi-browser


Wahrscheinlich die bekannteste Bibliothek zu diesem Thema mit einer Reihe von Funktionen und einer endlosen API. Zuerst habe ich es auf dem Server benutzt und es hat sich perfekt gezeigt. Irgendwann habe ich beschlossen, es durch eine type-check auf dem Client zu ersetzen. Zu diesem Zeitpunkt hatte ich fast keine Kontrolle über die Größe des Bundles, es gab einfach keine Probleme damit. Aber im Laufe des Jahres ist es schnell gewachsen und im mobilen Internet war der erste Anwendungsdownload überhaupt nicht bequem. Es wurde beschlossen, ein verzögertes Laden von Komponenten zu organisieren. Der Webpack-Bundle-Analyzer- Bericht zeigte eine Reihe von Riesen im Bundle, und alle gingen problemlos zu den vom Webpack erstellten Blöcken. Alle außer Joi . Viele Komponenten kommunizieren mit dem Server und alle Serverantworten werden validiert. Das heißt, es ist nicht sinnvoll, Joi in eine Art Block zu stecken. Es wird einfach immer direkt nach dem Hauptblock geladen. Irgendwann sah das Hauptbündel so aus: tyts . Natürlich entstand ein anhaltender Wunsch, etwas dagegen zu unternehmen. Ich wollte die gleiche praktische Bibliothek, aber viel weniger.


Ja


Github


In der Readme-Version versprechen sie ungefähr das gleiche Joi , aber in der für das Frontend geeigneten Größe. Tatsächlich ist es nur etwa zweimal kleiner, Yup war immer noch die größte Bibliothek im Hauptpaket. Darüber hinaus traten zusätzliche Nachteile auf:


  • Die Standardbibliothek überspringt alle undefined . Ständig .required() schreiben .required() nicht sehr angenehm, und ich mag es besser, wenn anfangs alles unmöglich ist und wo es erlaubt ist. Joi hat eine Option presence: 'required' , um dieses Verhalten zu konfigurieren. Ich habe eine Anfrage mit der höllischen Nummer 666 erstellt , aber bisher schweigen die Autoren.
  • Es gibt keine Möglichkeit, die Werte eines Objekts zu überprüfen, da alle Schlüssel zulässig sind. Joi verwendet dazu object.pattern , wobei das erste Argument eine beliebige Zeichenfolge akzeptiert . Wahrscheinlich wäre es immer noch möglich, irgendwie herauszukommen, und die Autoren können das erste Minus korrigieren, aber angesichts der Größe wollte ich nicht warten oder etwas selbst bearbeiten.

Ow


Github


Der nächste Bewerber erwies sich schließlich als sehr klein, und er ließ ihn nicht ständig schreiben () wo Sie darauf verzichten können. Sie können beispielsweise einen Validator schreiben, der eine Zeichenfolge zulässt oder wie folgt undefined :


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

Großartig! Was ist mit null ? Beim Umblättern der gesamten Dokumentation fand ich die folgende Methode:


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

Oh Horror! Als ich versuchte, einen Teil der Validierung im Projekt neu zu schreiben, habe ich mir beim Tippen fast die Finger gebrochen. Ich habe ow.nullable Problem mit dem Hinzufügen von ow.nullable , das hier gesendet wurde . Kurz gesagt, sie sagen, dass null nicht benötigt wird. Die dort angegebenen Argumente sind auch in Anbetracht der ersten Zeile in ihrer Readme-Datei völlig ausreichend:


Funktionsargumentvalidierung für Menschen

Das heißt, diese Bibliothek dient zum Überprüfen der Werte, die als Argumente für die Funktion dienen. Anscheinend haben sie nicht wirklich mit riesigen verschachtelten Strukturen vom Server gerechnet.
Weitere Studien und Verwendungsversuche ergaben mehrere weitere Funktionen, die wiederum durch dieselbe Zeile in der Readme-Datei gut erklärt wurden, aber nicht sehr gut zu mir passten. Dies ist eigentlich eine ziemlich gute Bibliothek, nur für ein paar andere Zwecke.




Hier hatte ich es schon satt aufzugeben und beschloss, meine Bibliothek mit Blackjack und Jungfrauen zu schreiben. Ja, ja, ich bin mit dem nächsten Fahrrad wieder bei dir :). Treffen:


Omyumyum


Einige Beispiele:


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

.or kann beliebig .or verwendet werden, indem Sie die Optionen erweitern:


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

In diesem Fall wird ständig eine fast gewöhnliche Funktion generiert, die jedes Argument boolean und einen boolean zurückgibt.
Wenn die Funktion einen Fehler auslösen soll, wenn der Test fehlschlägt:


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

Oder mit Curry:


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

Die resultierende Funktion ist nicht ganz normal, da sie zusätzliche Methoden enthält. .or bereits angezeigt, ein Teil der Methoden hängt vom ausgewählten Typ ab (siehe API ). Beispielsweise kann eine Zeichenfolge mit einem regulären Ausdruck erweitert werden:


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

Und für das Objekt können Sie seine Form angeben:


 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 

Der versprochene Schlüsselweg zum Problempunkt:


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

Wenn die integrierten Funktionen nicht ausreichen, können Sie immer .custom(validator: (value: any) => boolean) :


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

Auf Lager ist auch die erwartete .and verwendet, um Typen zu kombinieren und zu verbessern:


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

.and hat Vorrang vor .or , aber da .custom() den Validator mit genau der Form akzeptiert, die von der Bibliothek erstellt wurde, kann dies umgangen werden:


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

Sie können zuvor erstellte Validatoren weiter verbessern. Die alten verderben überhaupt nicht. Versuchen isUserData die zuvor erstellten isUserData zu verbessern:


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

Nun, blieb .not :


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

Weitere verfügbare Methoden finden Sie in der Bibliotheks- API .


Vorteile der Bibliothek:


  • Lakonische Syntax mit .or , .or , .or und einem Minimum an Klammern. In Kombination mit dem Autocomplete-Typoskript wird das Set zum Vergnügen.
  • Winziges Gewicht sogar im Vergleich zu Ow (fast 10-mal weniger (minify + gzip)) und im Vergleich zu Joi Bibliothek wie eine Feder neben einem Berg.
  • Schöner Name :)

Nachteile der Bibliothek:


  • Weniger Typen und ihre Modifikatoren. Und es ist unwahrscheinlich, dass es noch viel mehr geben wird. Alle drei am Anfang des Artikels angegebenen Verwendungsszenarien (etwas über Junctions und Codebasen) setzen die Übertragung von Klartextdaten voraus, in den meisten Fällen handelt es sich um JSON. Das heißt, meiner Meinung nach sollte eine solche Bibliothek die in JSON möglichen Typen sowie undefined und einige häufig verwendete Typen unterstützen. Aus irgendeinem Grund ist derselbe Ow mit Unterstützung für alle Arten von typisierten Arrays und anderem Unsinn vollgestopft. Ich finde das überflüssig.
  • Kann keine Daten wie Joi konvertieren. Ich denke, Joi ist auch ziemlich schlecht darin. Zumindest habe ich nicht genug von seinen Fähigkeiten und mache bei Bedarf Transformationen mit völlig anderen Werkzeugen. Vielleicht ist dies eine weitere Entwicklungsrichtung für omyumyum .

Das ist alles! Wenn Ihnen der Artikel gefallen hat, abonnieren Sie den Kanal und viel Glück)).

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


All Articles