Πώς να επιλέξετε ποιος επικυρωτής θα χρησιμοποιήσετε: σύγκριση μεταξύ Joi & express-validator

Φανταστείτε ότι έχετε έναν ιστότοπο ηλεκτρονικού εμπορίου και επιτρέπετε στους χρήστες να δημιουργούν λογαριασμούς χρησιμοποιώντας το όνομα και το email τους. Θέλετε να βεβαιωθείτε ότι έχουν εγγραφεί με πραγματικά ονόματα, όχι σαν το cool_dud3.

Εκεί χρησιμοποιούμε την επικύρωση για να επικυρώσουμε τις εισόδους και να βεβαιωθούμε ότι τα δεδομένα εισόδου ακολουθούν ορισμένους κανόνες.

Στην αγορά, έχουμε ήδη μια δέσμη βιβλιοθηκών επικύρωσης, αλλά θα συγκρίνω δύο σημαντικές βιβλιοθήκες επικύρωσης: Joi και express-validator για εφαρμογές που βασίζονται στο express.js .

Αυτή η σύγκριση είναι χρήσιμη όταν έχετε αποφασίσει να χρησιμοποιήσετε μια εξωτερική βιβλιοθήκη επικύρωσης εισόδου για την εφαρμογή σας που βασίζεται σε expressjs και δεν είστε κάπως σίγουροι ποια θα χρησιμοποιήσετε.

Ποιος είναι τι;

Τζόι

Το Joi σάς επιτρέπει να δημιουργήσετε σχεδιαγράμματα ή σχήματα για αντικείμενα JavaScript (ένα αντικείμενο που αποθηκεύει πληροφορίες) για να διασφαλίσετε την επικύρωση των βασικών πληροφοριών.

Express-επικυρωτής

express-validator είναι ένα σύνολο μεσαίων λογισμικών express.js που τυλίγει τις λειτουργίες validator.js validator και sanitizer.

Έτσι, εξ ορισμού, μπορούμε να πούμε ότι:

  • Το Joi μπορεί να χρησιμοποιηθεί για τη δημιουργία σχημάτων (όπως και το mongoose για τη δημιουργία σχημάτων NoSQL) και μπορείτε να το χρησιμοποιήσετε με απλά αντικείμενα Javascript. Είναι σαν μια βιβλιοθήκη plug n play και είναι εύκολο στη χρήση.
  • Από την άλλη πλευρά, το express-validator χρησιμοποιεί validator.js για την επικύρωση των διαδρομών expressjs και είναι κυρίως κατασκευασμένο για εφαρμογές express.js. Αυτό καθιστά αυτή τη βιβλιοθήκη πιο εξειδικευμένη και παρέχει προσαρμοσμένη επικύρωση και απολύμανση. Επίσης, το βρίσκω εύκολο να το καταλάβω προσωπικά :)

Πάρα πολλές μέθοδοι και API για να κάνετε κάποια επικύρωση στο Joi μπορεί να σας κάνουν να νιώσετε συγκλονισμένοι, ώστε να καταλήξετε να κλείσετε την καρτέλα.

Αλλά μπορεί να κάνω λάθος - οπότε ας κρατήσουμε τις απόψεις μας και συγκρίνουμε και τις δύο βιβλιοθήκες.

Instantiation

Τζόι

ΣεJoi , πρέπει να το χρησιμοποιήσετεJoi.object()για να δημιουργήσετε ένα αντικείμενο σχήματος Joi για να εργαστείτε.

Όλα τα σχήματα πρέπει Joi.object()να επεξεργαστούν επικύρωση και άλλα χαρακτηριστικά Joi.

Θα πρέπει να διαβάσετε ξεχωριστά req.body, req.params, req.queryτο σώμα αίτημα, params, και το ερώτημα.

const Joi = require('joi'); const schema = Joi.object().keys({ // validate fields here })

Express-επικυρωτής

Μπορείτε απλώς να απαιτήσετε το express-validator και τοαρχίστε να χρησιμοποιείτε τις μεθόδους του. Δεν χρειάζεται να διαβάσει τιμές από req.body, req.paramsκαι req.queryχωριστά.

Απλώς πρέπει να χρησιμοποιήσετε τις param, query, bodyπαρακάτω μεθόδους για να επικυρώσετε τις εισόδους αντίστοιχα, όπως μπορείτε να δείτε εδώ:

const { param, query, cookies, header body, validationResult } = require('express-validator/check') app.post('/user', [ // validate fields here ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Απαιτείται πεδίο

Ας πάρουμε ένα πολύ βασικό παράδειγμα όπου θέλουμε να βεβαιωθούμε ότι usernameπρέπει να απαιτείται stringκαι είναι alphaNumericμε minκαι maxχαρακτήρες.

  • Τζόι:
const Joi = require('joi'); const schema = Joi.object().keys({ username: Joi.string().alphanum().min(3).max(30).required() }) app.post('/user', (req, res, next) => { const result = Joi.validate(req.body, schema) if (result.error) { return res.status(400).json({ error: result.error }); } });
  • Express-επικυρωτής
const { body, validationResult } = require('express-validator/check') app.post('/user', [ body('username') .isString() .isAlphanumeric() .isLength({min: 3, max: 30}) .exists(), ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Απολύμανση

Η εξυγίανση ελέγχει βασικά την είσοδο για να βεβαιωθεί ότι είναι απαλλαγμένη από θόρυβο, για παράδειγμα, όλοι έχουμε χρησιμοποιήσει .trim()τη συμβολοσειρά για να αφαιρέσουμε κενά.

Ή εάν έχετε αντιμετωπίσει μια κατάσταση όπου ένας αριθμός μπαίνει "1"έτσι σε αυτές τις περιπτώσεις, θέλουμε να απολυμάνετε και να μετατρέψετε τον τύπο κατά τη διάρκεια του χρόνου εκτέλεσης.

Δυστυχώς, ο Joi δεν παρέχει απολύμανση έξω από το κουτί, αλλά το κάνει ρητός επικυρωτής .

Παράδειγμα: μετατροπή στο ObjectID του MongoDB

const { sanitizeParam } = require('express-validator/filter'); app.post('/object/:id', sanitizeParam('id') .customSanitizer(value => { return ObjectId(value); }), (req, res) => { // Handle the request });

Προσαρμοσμένη επικύρωση

Joi: .extend ( extension)

Αυτό δημιουργεί μια νέα παρουσία Joi προσαρμοσμένη με τις επεκτάσεις που παρέχετε.

Η επέκταση χρησιμοποιεί ορισμένες κοινές δομές που πρέπει να περιγραφούν πρώτα:

  • value - την τιμή που επεξεργάζεται ο Joi.
  • state - ένα αντικείμενο που περιέχει το τρέχον πλαίσιο επικύρωσης.
  • key - το κλειδί της τρέχουσας τιμής.
  • path - την πλήρη διαδρομή της τρέχουσας τιμής.
  • parent - ο δυνητικός γονέας της τρέχουσας τιμής.
  • options- αντικείμενο επιλογών που παρέχονται μέσω any().options()ή Joi.validate().

Επέκταση

extension μπορεί να είναι:

  • ένα αντικείμενο επέκτασης
  • μια εργοστασιακή λειτουργία που δημιουργεί ένα αντικείμενο επέκτασης
  • ή μια σειρά από αυτά

Τα αντικείμενα επέκτασης χρησιμοποιούν τις ακόλουθες παραμέτρους:

  • name- το όνομα του νέου τύπου που ορίζετε, μπορεί να είναι ένας υπάρχων τύπος. Απαιτείται.
  • base- ένα υπάρχον σχήμα Joi για να βασιστεί ο τύπος σας. Προεπιλογές σε Joi.any().
  • coerce- μια προαιρετική λειτουργία που εκτελείται πριν από τη βάση, συνήθως εξυπηρετεί όταν θέλετε να εξαναγκάσετε τιμές διαφορετικού τύπου από τη βάση σας. Παίρνει 3 επιχειρήματα value, stateκαι options.
  • pre- μια προαιρετική συνάρτηση που εκτελείται πρώτη στην αλυσίδα επικύρωσης, συνήθως εξυπηρετεί όταν πρέπει να μεταδώσετε τιμές. Παίρνει 3 επιχειρήματα value, stateκαι options.
  • language- ένα προαιρετικό αντικείμενο για την προσθήκη ορισμών σφαλμάτων. Κάθε κλειδί θα προτάσσεται από το όνομα τύπου.
  • describe - μια προαιρετική συνάρτηση που λαμβάνει την πλήρως διαμορφωμένη περιγραφή για την επεξεργασία της.
  • rules - μια προαιρετική σειρά κανόνων για προσθήκη.
  • name- όνομα του νέου κανόνα. Απαιτείται.
  • params- ένα προαιρετικό αντικείμενο που περιέχει σχήματα Joi κάθε παραμέτρου που έχει παραγγελθεί. Μπορείτε επίσης να περάσετε ένα μεμονωμένο σχήμα Joi αρκεί να είναι Joi.object(). Φυσικά ορισμένες μέθοδοι όπως patternή renameδεν θα είναι χρήσιμες ή δεν θα λειτουργήσουν καθόλου σε αυτό το δεδομένο πλαίσιο.
  • setup- μια προαιρετική συνάρτηση που λαμβάνει ένα αντικείμενο με τις παρεχόμενες παραμέτρους για να επιτρέπει τον εσωτερικό χειρισμό του σχήματος όταν έχει οριστεί ένας κανόνας. Μπορείτε προαιρετικά να επιστρέψετε ένα νέο σχήμα Joi που θα ληφθεί ως το νέο σχήμα σχήματος. Τουλάχιστον ένα από τα δύο setupή validateπρέπει να παρέχεται.
  • validate - an optional function to validate values that takes 4 parameters params, value, state and options. At least one of setup or validate must be provided.
  • description - an optional string or function taking the parameters as an argument to describe what the rule is doing.

Example:

joi.extend((joi) => ({ base: joi.object().keys({ name: joi.string(), age: joi.number(), adult: joi.bool().optional(), }), name: 'person', language: { adult: 'needs to be an adult', }, rules: [ { name: 'adult', validate(params, value, state, options) { if (!value.adult) { // Generate an error, state and options need to be passed return this.createError('person.adult', {}, state, options); } return value; // Everything is OK } } ] })

Express-validator

A custom validator may be implemented by using the chain method .custom(). It takes a validator function.

Custom validators may return Promises to indicate an async validation (which will be awaited upon), or throw any value/reject a promise to use a custom error message.

const { param, query, cookies, header body, validationResult } = require('express-validator/check') app.get('/user/:userId', [ param('userId') .exists() .isMongoId() .custom(val => UserSchema.isValidUser(val)), ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } }

Conditional Validation

express-validator does not support conditional validation as of now, but there is a PR for that already you can check //github.com/express-validator/express-validator/pull/658

Let’s see how it works in Joi:

any.when(condition, options)

any:Generates a schema object that matches any data type.

const schema = Joi.object({ a: Joi.any().valid('x'), b: Joi.any() }).when( Joi.object({ b: Joi.exist() }) .unknown(), { then: Joi.object({ a: Joi.valid('y') }), otherwise: Joi.object({ a: Joi.valid('z') }) });

alternatives.when(condition, options)

Adds a conditional alternative schema type, either based on another key (not the same as any.when()) value, or a schema peeking into the current value, where:

  • condition - the key name or reference, or a schema.
  • options - an object with:
  • is - the required condition joi type. Forbidden when condition is a schema.
  • then - the alternative schema type to try if the condition is true. Required if otherwise is missing.
  • otherwise - the alternative schema type to try if the condition is false. Required if then is missing.
const schema = Joi .alternatives() .when(Joi.object({ b: 5 }).unknown(), { then: Joi.object({ a: Joi.string(), b: Joi.any() }), otherwise: Joi.object({ a: Joi.number(), b: Joi.any() }) });

Nested Validation

When you want to validate an array of objects/items or just object keys

Both libraries support nested validation

Now what about express-validator?

Wildcards

Wildcards allow you to iterate over an array of items or object keys and validate each item or its properties.

The * character is also known as a wildcard.

const express = require('express'); const { check } = require('express-validator/check'); const { sanitize } = require('express-validator/filter'); const app = express(); app.use(express.json()); app.post('/addresses', [ check('addresses.*.postalCode').isPostalCode(), sanitize('addresses.*.number').toInt() ], (req, res) => { // Handle the request });

Joi

const schema = Joi.object().keys({ addresses: Joi.array().items( Joi.object().keys({ postalCode: Joi.string().required(), }), ) });

Custom Error Messages

Joi

any.error(err, [options])

Overrides the default joi error with a custom error

let schema = Joi.string().error(new Error('Was REALLY expecting a string'));

Express-validator

const { check } = require('express-validator/check'); app.post('/user', [ // ...some other validations... check('password') .isLength({ min: 5 }).withMessage('must be at 5 chars long') .matches(/\d/).withMessage('must contain a number') ], (req, res) => { // Handle the request somehow });

Conclusion

I covered the most important parts of both libraries and you can decide yourself which one you want to use. Please let me know in the comments below if I left out anything important in the comparison.

I hope you find it helpful when deciding the next input validation module for your express.js application.

I wrote an in-depth article on it here: how to validate inputs. Do check it out.

Μην διστάσετε να χειροκροτήσετε αν το θεωρήσατε αξιόλογο!

Αρχικά δημοσιεύθηκε στο 101node.io στις 31 Μαρτίου 2019.