¿Para qué se usa el parámetro "siguiente" en Express?

295

Supongamos que tiene un bloque de código simple como este:

app.get('/', function(req, res){
    res.send('Hello World');
});

Esta función tiene dos parámetros reqy res, que representan los objetos de solicitud y respuesta respectivamente.

Por otro lado, hay otras funciones con un tercer parámetro llamado next. Por ejemplo, echemos un vistazo al siguiente código:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

No puedo entender de qué next()sirve o por qué se está utilizando. En ese ejemplo, si la identificación no existe, ¿qué está nexthaciendo realmente?

Menztrual
fuente
13
Siguiente simplemente permite que el siguiente controlador de ruta en línea maneje la solicitud. En este caso, si existe la identificación de usuario, probablemente se usará res.sendpara completar la solicitud. Si no existe, es probable que haya otro controlador que emita un error y complete la solicitud en ese momento.
Dominic Barnes
1
Entonces, ¿estás diciendo que tengo un app.post('/login',function(req,res))después de app.get('/users',function(req,res)) que llame al inicio de sesión como la siguiente ruta en el archivo app.js llamando a next ()?
Menztrual
2
No, debe consultar esta parte de la documentación de Express.js : expressjs.com/guide.html#passing-route control
Dominic Barnes
3
Básicamente, la siguiente ruta a ejecutar será otra que coincida con la URL de la solicitud. En este caso, si se registró otra ruta a través de app.get("/users"), se ejecutará si el controlador anterior llama a continuación.
Dominic Barnes
3
El siguiente es básicamente solo una devolución de llamada.
Jonathan Ong

Respuestas:

266

Pasa el control a la siguiente ruta coincidente . En el ejemplo que da, por ejemplo, puede buscar el usuario en la base de datos si idse le dio uno y asignarlo req.user.

A continuación, podría tener una ruta como:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

Como / users / 123 coincidirá primero con la ruta en su ejemplo, primero comprobará y encontrará el usuario 123; entonces /userspuede hacer algo con el resultado de eso.

Sin embargo, en mi opinión, el middleware de ruta es una herramienta más flexible y poderosa, ya que no se basa en un esquema de URI particular o en un pedido de ruta. Me inclinaría a modelar el ejemplo que se muestra así, suponiendo un Usersmodelo con una sincronización findOne():

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

Ser capaz de controlar el flujo de esta manera es bastante útil. Es posible que desee que ciertas páginas solo estén disponibles para los usuarios con una marca de administrador:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

¡Espero que esto te haya inspirado!

Asera
fuente
¡Podrías decirlo! Sin embargo, estaría más inclinado a hacer este tipo de cosas con el middleware de ruta , ya que no combina la lógica con un orden particular de rutas o estructuras de URI particulares.
Asherah
55
¿Por qué a veces vuelves al siguiente () pero a veces no?
John
66
@John: el valor de retorno se ignora realmente; Solo quiero regresar para asegurarme de no next()volver a llamar . Sería lo mismo si solo lo usara next(new Error(…)); return;.
Asherah
1
@ level0: el valor de retorno se ignora; puedes considerarlo taquigrafía para next(new Error(…)); return;. Si pasamos un valor a next, se considera unilateralmente un error . No he investigado demasiado el código express, pero busca y encontrarás lo que necesitas :)
Asherah
1
@ level0: (me he cambiado return next(…);para next(…); return;que sea menos confuso).
Asherah,
87

También tuve problemas para entender next (), pero esto ayudó

var app = require("express")();

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);
rajesk
fuente
3
Muy sucinto! ¡Gracias! Pero, ¿cómo se asegura de que .getse llame el primero y no el segundo?
JohnnyQ
18
@JohnnyQ Será de arriba a abajo
Tapash
59

Antes de comprender next, debe tener una pequeña idea del ciclo de Solicitud-Respuesta en el nodo, aunque no mucho en detalle. Comienza con una solicitud HTTP para un recurso en particular y finaliza cuando envía una respuesta al usuario, es decir, cuando encuentra algo como res.send ('Hola Mundo');

Echemos un vistazo a un ejemplo muy simple.

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

Aquí no necesitamos next (), porque resp.send finalizará el ciclo y devolverá el control al middleware de ruta.

Ahora echemos un vistazo a otro ejemplo.

app.get('/hello', function (req, res, next) {
  res.send("Hello World !!!!");
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Aquí tenemos 2 funciones de middleware para la misma ruta. Pero siempre obtendrás la respuesta de la primera. Porque eso se monta primero en la pila de middleware y res.send terminará el ciclo.

Pero, ¿y si siempre no queremos el "Hola Mundo!" respuesta de vuelta. Para algunas condiciones podemos querer el "Hola Planeta !!!!" respuesta. Modifiquemos el código anterior y veamos qué sucede.

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

¿Qué está nexthaciendo aquí? Y sí, podrías tener quejas. Saltará la primera función de middleware si la condición es verdadera e invocará la siguiente función de middleware y obtendrá la "Hello Planet !!!!"respuesta.

Entonces, luego pase el control a la siguiente función en la pila de middleware.

¿Qué sucede si la primera función de middleware no envía ninguna respuesta pero ejecuta una lógica y luego obtiene la respuesta de la segunda función de middleware?

Algo como a continuación: -

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

app.get('/hello', function (req, res, next) {
  res.send("Hello !!!!");
});

En este caso, necesita invocar ambas funciones de middleware. Entonces, la única forma de llegar a la segunda función de middleware es llamando a next ();

¿Qué pasa si no haces una llamada al siguiente? No espere que la segunda función de middleware se invoque automáticamente. Después de invocar la primera función, su solicitud quedará pendiente. La segunda función nunca se invocará y no obtendrá la respuesta.

Mav55
fuente
Entonces, ¿ next()funciona como un gotodisco duro con una etiqueta? Es decir, en su tercer fragmento, una vez que llame next(), res.send("Hello World !!!!"); ¿nunca se ejecutará? Me di cuenta de que @Ashe siempre tenía una llamada return;posterior nextque tenía código en el mismo árbol de ejecución ... Supongo que siempre podía registrarme en express, ¿eh? / corre hacia su editor de texto;)
ruffin
@ruffin sí, puedes pensar en el próximo parecido a un goto. pero luego sabe a dónde ir a diferencia de goto que requiere una etiqueta. Luego pasará el control a la siguiente función de middleware. Además, puede nombrar 'siguiente' cualquier cosa que desee. Es solo una etiqueta aquí. Pero la mejor práctica es usar el nombre 'siguiente'
Mav55
3
De acuerdo, parece que eso no es exacto. Intenté el código ( pastebin aquí ), y se llama al código después de la next()llamada . En este caso, se escribe en la consola, y luego aparece un error, como se llama al segundo , aunque sin éxito. El flujo de código regresa después de la llamada, lo que hace importante a @ Ashe (u otra gestión lógica). past the next() callError: Can't set headers after they are sent.res.sendnext()returns
ruffin
44
@ruffin, sí, tienes razón. Necesitamos una declaración de devolución después de la next()para omitir la ejecución de las declaraciones restantes. Gracias por señalar eso.
Mav55
1
Gracias por explicar realmente qué es "middleware" / qué hace con ejemplos claros y no solo parrotear la documentación. Esta fue la única respuesta que realmente dice algo claro sobre lo que sucede, por qué y cómo.
mc01
11

Siguiente se usa para pasar el control a la siguiente función de middleware. Si no, la solicitud quedará pendiente o abierta.

Ronique Ricketts
fuente
66
No estoy seguro de lo que esta respuesta agrega a una pregunta de casi siete años ...
mherzig
Aunque breve, este comentario me llevó a esto: expressjs.com/en/guide/writing-middleware.html
hmak
@mherzig, es un trazador de líneas y cubre todo
M. Gopal
5

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. La función next () podría llamarse cualquier cosa, pero por convención siempre se llama "next".

Binayak Gouri Shankar
fuente
2

La ejecución de la nextfunción notifica al servidor que ha terminado con este paso de middleware y puede ejecutar el siguiente paso en la cadena.

Agrush
fuente