Cuándo usar next () y return next () en Node.js


136

Escenario : considere lo siguiente es la parte del código de una aplicación web de nodo.

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); //or return next();
    }
});

Problema : Estoy comprobando cuál elegir con solo next()o return next(). El código de ejemplo anterior funciona exactamente igual para ambos y no mostró ninguna diferencia en la ejecución.

Pregunta : ¿Alguien puede aclarar esto, cuándo usar next()y cuándo usar return next()y alguna diferencia importante?

Respuestas:


141

Algunas personas siempre escriben return next()para asegurarse de que la ejecución se detiene después de activar la devolución de llamada.

Si no lo hace, corre el riesgo de activar la devolución de llamada una segunda vez más tarde, lo que generalmente tiene resultados devastadores. Su código está bien como está, pero lo reescribiría como:

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;

    if(!id)
        return next();

    // do something
});

Me ahorra un nivel de sangría, y cuando leo el código nuevamente más tarde, estoy seguro de que no nextse llama dos veces.


2
¿Sería cierto algo similar respecto de res.redirect('/')vs. return res.redirect('/')en este tipo de situación? ¿Quizás es mejor escribir siempre el retorno frente a las declaraciones de resolución para evitar errores de configuración de encabezados después de enviarlos?
Adam D

187

Como la respuesta de @Laurent Perrin:

Si no lo hace, corre el riesgo de activar la devolución de llamada una segunda vez más tarde, lo que generalmente tiene resultados devastadores

Doy un ejemplo aquí si escribes middleware como este:

app.use((req, res, next) => {
  console.log('This is a middleware')
  next()
  console.log('This is first-half middleware')
})

app.use((req, res, next) => {
  console.log('This is second middleware')
  next()
})

app.use((req, res, next) => {
  console.log('This is third middleware')
  next()
})

Descubrirá que la salida en la consola es:

This is a middleware
This is second middleware
This is third middleware
This is first-half middleware

Es decir, ejecuta el siguiente código next () después de que termine toda la función de middleware.

Sin embargo, si lo usa return next(), saltará la devolución de llamada inmediatamente y el código siguiente return next()en la devolución de llamada no será accesible.


29
Como principiante, expressesta respuesta me dejó las cosas más claras que las otras respuestas. ¡Pulgares hacia arriba!
mandarín

1
¿Sería cierto algo similar respecto de res.redirect('/')vs. return res.redirect('/')en este tipo de situación? ¿Quizás es mejor escribir siempre returndelante de las resdeclaraciones para evitar errores de configuración de encabezados después de que se envían?
Adam D

1
¿Por qué debería escribir código después de next ()? ¿No es obvio que no hago nada después de terminar mi tarea en un middleware? @PJCHENder
Imran Pollob

1
@ImranPollob a veces ocurren errores. Cuando escribes mucho código, ifs / elses / etc. Puede olvidar `` `return next ()`
Jone Polvora

46

next()es parte de conectar middleware . A las devoluciones de llamada para el flujo del enrutador no le importa si devuelve algo de sus funciones, así que return next()ynext(); return; es básicamente el mismo.

En caso de que desee detener el flujo de funciones, puede usar next(err)como lo siguiente

app.get('/user/:id?', 
    function(req, res, next) { 
        console.log('function one');
        if ( !req.params.id ) 
            next('No ID'); // This will return error
        else   
            next(); // This will continue to function 2
    },
    function(req, res) { 
        console.log('function two'); 
    }
);

Prácticamente next()se usa para extender el middleware de sus solicitudes.


1
¿Podemos enviar parámetros como next('No ID'):?
Amol M Kulkarni

77
next('No ID')en realidad está enviando un error, que interrumpirá el flujo.
drinchev

Use next (nulo, "somevalue"); Para herramientas como async.waterfall, pasará el valor a la siguiente función. Para series complejas de interacciones basadas en datos, generalmente paso un objeto de contexto entre funciones. De esa manera, puedo crear funciones genéricas que se pueden compartir en múltiples puntos finales y controlar el flujo a través de datos en el contexto
Chad Wilson,

55
"entonces return next () y next (); return; es básicamente lo mismo". Justo lo que necesitaba leer. gracias @drinchev
Nick Pineda

1
Observo lo contrario (al disparar el error): next (error) activa el siguiente middleware, pero continúa ejecutando código; return next (error) solo relega la ejecución al siguiente middleware. next (e) y return next (e) NO son lo mismo.
Nickolodeon

0

¡Es mejor no usarlo en absoluto! Lo explico, y eso es lo que hago también lo explico.

La función next () que puede tener cualquier nombre y, por convención, se ha establecido en next. Está indirectamente relacionado con las operaciones (PUT, GET, DELETE, ...) que generalmente se realizan en el mismo recurso URI, por ejemplo/ user /: id

app.get('/user/:id', function (req,res,next)...)
app.put('/user/:id', function (req,res,next)...)
app.delete('/user/:id', function (req,res,next)...)
app.post('/user/', function ()...)

Ahora, si observa app.get, app.put y app.delete usan la misma uri (/ user /: id), lo único que las diferencia es su implementación. Cuando se realiza la solicitud (req) express coloca la solicitud primero en app.get, si falla cualquier validación que haya creado porque esa solicitud no es para ese controlador, pasa la solicitud a app.put que es la siguiente ruta en el archivo y así en. Como se ve en el ejemplo a continuación.

    app.get('/user/:id', function (req,res,next){

    if(req.method === 'GET')
    //whatever you are going to do
    else
      return next() //it passes the request to app.put

    //Where would GET response 404 go, here? or in the next one. 
    // Will the GET answer be handled by a PUT? Something is wrong here.

   })
    app.put('/user/:id', function (req,res,next){

    if(req.method === 'PUT')
    //whatever you are going to do
    else
      return next()

   })

El problema radica en que, al final, terminas pasando la solicitud a todos los controladores con la esperanza de que haya una que haga lo que quieras, mediante la validación de la solicitud. Al final, todos los controladores terminan recibiendo algo que no es para ellos :(.

Entonces, ¿cómo evitar el problema de next () ?

La respuesta es realmente simple.

1- solo debe haber una uri para identificar un recurso

http: // IpServidor / colection /: resource / colection /: resource si su URI es más largo que eso, debería considerar crear una nueva uri

Ejemplo http: // IpServidor / users / pepe / contactos / contacto1

2-Todas las operaciones en este recurso deben realizarse respetando la idempotencia de los verbos http (get, post, put, delete, ...) para que la llamada a un URI realmente solo tenga una forma de llamar

POST http://IpServidor/users/  //create a pepe user 
GET http://IpServidor/users/pepe  //user pepe returns   
PUT http://IpServidor/users/pepe  //update the user pepe 
DELETE http://IpServidor/users/pepe  //remove the user pepe

Más información [ https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resourcesfont>[1]

¡Veamos el código! ¡La implementación concreta que nos hace evitar el uso de next ()!

En el archivo index.js

//index.js the entry point to the application also caller app.js
const express = require('express');
const app = express();

const usersRoute = require('./src/route/usersRoute.js');

app.use('/users', usersRoute );

En el archivo usersRoute.js

    //usersRoute.js
    const express = require('express');
    const router = express.Router();

    const getUsersController = require('../Controllers/getUsersController.js');
    const deleteUsersController = require('../Controllers/deleteUsersController.js');

    router.use('/:name', function (req, res) //The path is in /users/:name
    {
    switch (req.method)
    {
    case 'DELETE':
      deleteUsersController(req, res);
      break;
    case 'PUT':
     // call to putUsersController(req, res);
     break;
    case 'GET':
     getUsersController(req, res);
     break;
    default:
     res.status(400).send('Bad request');
    } });

router.post('/',function (req,res) //The path is in /users/
{
    postUsersController(req, res);
});

module.exports = router;

Ahora el archivo usersRoute.js hace lo que se espera que haga un archivo llamado usersRoute, que es administrar las rutas del URI / users /

// archivo getUsersController.js

//getUsersController.js
    const findUser= require('../Aplication/findUser.js');
    const usersRepository = require('../Infraestructure/usersRepository.js');

    const getUsersController = async function (req, res)
    {

       try{
          const userName = req.params.name;
        //...
          res.status(200).send(user.propertys())

        }catch(findUserError){
           res.status(findUserError.code).send(findUserError.message)
        }
    }
   module.exports = getUsersController;

De esta forma, evita el uso de next, desacopla el código, gana rendimiento, desarrolla SOLID, deja la puerta abierta para una posible migración a microservicios y, sobre todo, es fácil de leer por un programador.


2
Esto es incorrecto, app.get no pasará a app.put como sugiere. Solo se llaman las solicitudes coincidentes, por lo que si el método es GET, solo se llamará al middleware app.get. El middleware no necesita verificar el método de solicitud. Su sugerencia ignora una función principal de express e implementa su propio enrutamiento. Además, su sugerencia asume que la ruta es el único middleware que usará, ya que nunca se pasa a ningún lado.
Ravenex

Información incorrecta, consulte las respuestas anteriores.
DDiamond

-3

Próximo() :

Llamar a esta función invoca la siguiente función de middleware en la aplicación. La función next () no es parte de Node.js o Express API, pero es el tercer argumento que se pasa a la función de middleware.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.