Validación de entradas de usuario en su aplicación Express.js con express-validator

· 6 min de lectura
Validación de entradas de usuario en su aplicación Express.js con express-validator

Los formularios están en todas partes. Ya sea en un simple blog o en la aplicación más compleja, recibir la entrada del usuario es una de las características más comunes de toda aplicación web moderna hoy en día.

Por lo tanto, se vuelve muy importante validar lo que viene de su usuario tanto en el frontend como en el backend.

Construir tus APIs sin una validación en el backend es lo mismo que poner puertas en una casa nueva y dejar los marcos de las ventanas abiertos. Las puertas impiden el paso a algunas personas, pero cualquiera que sepa mirar puede atravesar el marco de la ventana.

La validación del lado del cliente es estupenda, pero sigue sin evitar los datos maliciosos de un usuario técnico que sabe cómo saltarse el comportamiento por defecto del navegador para las peticiones HTTP.

Un usuario técnico también sabe que las peticiones HTTP pueden ser enviadas de muchas maneras. La mayoría de las veces ni siquiera se necesita un navegador.

Por suerte para nosotros como desarrolladores, las validaciones de backend no son tan complejas de construir.

Es más, hay un par de librerías que han sido construidas por otros desarrolladores de forma gratuita para que podamos utilizarlas. Algunos ORMs como Mongoose para MongoDB también vienen incorporados con validadores. Para una aplicación sencilla, es muy fácil utilizar una simple sentencia if else y expresiones regulares.

Sin embargo, a medida que tu aplicación se hace más grande, la necesidad de validación intrínseca es necesaria.

via GIPHY

En este tutorial, aprenderás a validar aplicaciones básicas y complejas utilizando un módulo gratuito y fácil de configurar llamado Express-Validator

// mongoose schema 

const userSchema = new mongoose.schema ({
    firstName: {
        type: String, 
        maxLength: [30, 'please enter less than 30 characters'], 
        required: true 
    }, 
    lastName: {
        type: String, 
        minLength: [1, 'please enter more than 1 characters'],
        required: true
    },
    phoneNumber: {
        type: Number, 
        min: [8, 'please enter more than 8 characters']
    }
})
// JavaScript if else statement 

if(req.body.password && req.body.password.length > 6) {
    .....
} else {
    throw new Error ('your password must be less than 6 characters')
}

Configuración del proyecto


Es importante señalar que este tutorial requiere un conocimiento previo de Nodejs y Express.js . Asumiré que ya sabes cómo configurar tu aplicación Nodejs y que puedes entrar directamente.

Podemos instalar express-validator con  npm install express-validator --save y puedes incluirlo en el punto de entrada de tu aplicación así (estoy usando index.js).

const express = require ('express'); 
const { body, validationResult } =  require ('express-validator');

const app = express (); 
const apiRouter = express.Router (); 

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use('/api/v1/testValidator', apiRouter); 

apiRouter.post('/login', (req, res) => {
    console.log(req.body);
    res.send('request received on api route')
})

const port = process.env.PORT || 8080; 
app.listen (port, console.log ('server running on port', port)); 

Ahora hemos configurado nuestra aplicación express.js y le hemos pedido a nuestra aplicación que se ejecute en el puerto 8080. Además, hemos configurado una ruta de acceso que toma POST y por ahora sólo envía un mensaje de vuelta al frontend.

Validación rápida


El problema con nuestra nueva ruta es tal y como se ha explicado anteriormente. Cualquiera puede enviar cualquier tipo de datos a ella y manipular nuestra aplicación.

Nuestra ruta actualmente no se relaciona con ninguna base de datos, pero si estuviéramos construyendo una aplicación de la vida real, lo más probable es que verifiquemos nuestros datos con la base de datos.

Un hacker que envíe cualquier dato malicioso tiene vía libre para hacer lo que quiera con este tipo de ruta.

Podemos configurar rápidamente la validación de nuestra ruta de acceso sin sudar:


apiRouter.post ('/login',
body ('email')).trim().notEmpty().whitMessage('please enter your email')
.isEmail().whitMessage('please enter a valid email').isLength({ min:6 })
.isString().whitMessage('please enter a valid password'), 
(req, res) => {
        let errors = validationResult(req); 
        if(!errors.isEmpty()){
        console.log(errors.array());
        return res.json({ errors: errors.array ()});
    }
}

¡Yeahh! Ahora estamos comprobando nuestra ruta de acceso para asegurarnos de que no hay datos erróneos y de que el usuario no está enviando una casilla vacía. Hay un par de comandos que intentaré explicar.

El primero es el método body que se importó por primera vez de express-validator en el código anterior a este. El siguiente es el trim()Método Javascript que recorta los espacios vacíos que rodean el valor de nuestro formulario.

Luego está el isEmail() que comprueba si el correo electrónico introducido por el usuario tiene el formato correcto. El último en la comprobación del correo electrónico es notEmpty() que comprueba que el usuario está pasando un valor.

También existe la withMessage que toma el mensaje de error a enviar al frontend en caso de que la comprobación falle. No obstante, es opcional y se enviará un mensaje por defecto al frontend cuando no se añada.


Me he dado cuenta de que me he saltado una prueba que me hubiera gustado tener allí y que se llama normalizeEmail() . The normalizeEmail() ayuda a comprobar y convertir los correos electrónicos introducidos por el usuario en el formato estándar. Esto significa que un correo electrónico como testdata@googlemail.com se convertirá en testdata@gmail.com .

Para la contraseña, ponemos trim() para los espacios vacíos, establece notEmpty() para mostrar que es un campo obligatorio, isString() para imponer un tipo de datos al usuario y isLength({min: 6}) para comprobar que la contraseña introducida por el usuario no es inferior a 6 caracteres.

También podemos establecer max como parte del mismo objeto isLength. El isLength en el campo Contraseña seguirá funcionando incluso sin el metodo  withMessage  ya que es un campo opcional como ya se ha explicado.

Después de esto, creamos una nueva variable y la ponemos igual a nuestro validationResult procedente de express-validator. A continuación, comprobamos su longitud para ver si contiene errores y enviamos la respuesta al frontend para que el usuario la vea.

Si nuestro código funciona bien, obtendremos este tipo de mensaje de error en nuestra consola si el usuario no introduce una dirección de correo electrónico en el formato correcto.

{
    "errors": [
        {
            "value": "eadeleke", 
            "msg": "please enter a valid email", 
            "param": "email",
            "location": "body"
        }
    ]
}

Ahora, hay un problema con este tipo de prueba. Probablemente se ve bien ahora que estamos comprobando sólo el correo electrónico y la contraseña. Pero imagina lo que ocurre cuando un usuario se está registrando y tenemos que comprobar más información.

Nuestro código se infla rápidamente y se vuelve ilegible. Incluso se vuelve difícil de rastrear. Esta es una situación ideal para traer Middlewares.

Separar el código del validador y el código principal


Según el sitio web de Express.js, las funciones Middleware son funciones que tienen acceso a la petición(req),  la respuesta (res), y el next en el ciclo de solicitud-respuesta de la aplicación.

Esto significa que pueden acceder al cuerpo de la solicitud enviada por un usuario, trabajar en él y pasar la acción a nuestro código principal. Esto funciona muy bien para nosotros.

Crearemos un nuevo archivo llamado validator.js en el directorio de nuestra aplicación y añadir el siguiente código de validación en él.

const { body } = require('express-validator')

const SingUpCheck = () => { 
    return [
        body('email').trim().not().isEmpty().whitMessage 
        ('this field is required').isEmail().WhitMessage
        ('please enter a valid email address'),
        body('firstName').trim().not().isEmpty().isString().whitMessage
        ('please enter only letters').isLength({min: 3, max: 5}), 
        body('lastName').trim().not().isEmpty().isLength({min: 3, max: 5}).isString().whitMessage
        ('please enter only characters'),
        body('phoneNumber').trim().isInt().whitMessage
        ('please enter numbers').isLength({ min: 7, max: 15}).whitMessage
        ('phoneNumber can not be less than 7 and must be more than 15'),
        body( 'subscribed' ).isBoolean().whitMessage
        ('please enter a true or false value'), 
        body( 'occupation' ).trim().isIn(['employed','self-employed','enterpreneur']).WhitMessage
        ('you must have something doing')
    ]
}
module.export = {
    SingUpCheck
}

Aquí hay nuevos métodos que no he explicado antes. Está el método isInt() que funciona como isString() pero para los números. El isArray() para confirmar las matrices. Existe el isBoolean que comprueba que el usuario está enviando un valor verdadero/falso. También tenemos not() que funciona de la misma manera ! en Javascript.

Niega el método que viene, así que cuando tenemos not().isEmpty() En realidad, estamos comprobando que ese campo no está vacío. Esto es sólo una forma diferente de escribir el método notEmpty

Supongamos que estás construyendo una aplicación móvil que recibe una entrada de un usuario y tiene que comprobar si esa entrada es un día de la semana. El método isIn ayuda en este caso. Podemos tener isIn['monday','tuesday','wednesday','thursday','friday','saturday',sunday'] .

Se devuelve un mensaje de error cuando un usuario, por ejemplo, introduce mondayful .

Podemos utilizar esta comprobación en nuestro punto de entrada index.js de la siguiente manera:

apiRouter.post('/register', signUpCheck (), (req, res) => {
    let errors = validationResult (req) ; 
    if ( !errors.isEmpty()) {
        console.log(errors.array());
        return res.json({errors: errors.array() });
    }
}
)

Esto se ejecuta de la misma manera que antes y valida la entrada del usuario.

Validadores personalizados


Aunque el módulo express-validator tiene un montón de validadores que pueden ayudar a la hora de construir incluso las aplicaciones más grandes. Tu propia aplicación puede requerir algo que no está incluido en el módulo.

El módulo Express-Validator da cabida a funciones personalizadas para mejorar la calidad de tu aplicación. Vamos a construir una función personalizada para comparar dos entradas de contraseña confirm password and password fields. Podemos utilizar la función custom() ya incorporada en el módulo para conseguirlo.

const { body } = require('express-validator')

const signUpCheck = () => {
    return [
        body('password').trim().notEmpty().isLength({ min: 6}), 
        body ('cfpassword').trim().custom((value, {req}) => {
            if (value !== req.body.password) {
                throw new Error('password not eqaul to confirm password')
            }
            return true; 
        }),
        body ('ocupation').trim().isIn([ 'employed', 'self-employed','enterpreneur']).whitMessage 
        ('you must have something doing')
    ]
}
module.exports = { 
    signUpCheck
}

El módulo express-validator nos ayuda a ocuparnos de la mayoría de las validaciones y también nos da margen para explorar. He hecho todo lo posible para explicar en detalle y cubrir los terrenos para usted para que pueda empezar lo más rápido posible.

Fuente

Plataforma de cursos gratis sobre programación

Artículos Relacionados

¿Cómo implementar Redis PubSub en NodeJS?
· 3 min de lectura
OpenAPI
· 6 min de lectura
¡NodeJs versión 18 ya está aquí!
· 3 min de lectura