¿Para qué se utiliza el parámetro "siguiente" en Express?
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, req
y res
, que representan los objetos de solicitud y respuesta respectivamente.
Por otro lado, existen 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 cuál next()
es el punto o por qué se usa. En ese ejemplo, si la identificación no existe, ¿qué está next
haciendo realmente?
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 id
se proporcionó un y asignarlo a 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
});
Dado que /users/123 coincidirá primero con la ruta en su ejemplo, primero verificará y encontrará user 123
; Entonces /users
puedo 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 depende de un esquema de URI particular ni de un orden de ruta. Me inclinaría por modelar el ejemplo que se muestra así, asumiendo un Users
modelo con async 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 un flujo como este es bastante útil. Es posible que desees que ciertas páginas solo estén disponibles para 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 servido de inspiración!
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);
Antes de comprenderlo next
, debe tener una pequeña idea del ciclo Solicitud-Respuesta en el nodo, aunque no mucho en detalle. Comienza cuando usted realiza 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('Hello World');
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 del primero. Porque eso se monta primero en la pila de middleware y res.send finalizará el ciclo.
Pero ¿y si no siempre queremos el mensaje “¡¡¡Hola mundo!!!!” respuesta de vuelta. Para algunas condiciones es posible que deseemos el mensaje "¡¡¡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é next
hace aquí? Y sí, es posible que tengas nervios. Se omitirá 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é pasa si la primera función de middleware no envía ninguna respuesta pero ejecuta una parte de la 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, es necesario invocar ambas funciones del 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á suspendida. La segunda función nunca será invocada y no recibirás la respuesta.
Siguiente se utiliza para pasar el control a la siguiente función de middleware. En caso contrario, la solicitud quedará suspendida o abierta.
Un poco de interior aquí. El objetivo principal de express
app
handle
la función es enviar una respuesta al cliente y finalizar el archivo request-response cycle
. Y la terminación de este ciclo se puede realizar mediante uno de response methods
(por ejemplo, res.end(), res.json(), etc.). Es decir, si un middleware route handler
realiza algunas acciones pero luego no llama a uno de ellos response methods
ni pasa el control al siguiente controlador o middleware, request-response cycle
no se terminará. Pero lo que next
hace depende de dónde y cómo se llama.
Para gestionar diferentes tareas (manejadores de ruta, middlewares) express
se crea stacks
. Parecen uno queue
de los tasks
. Cada uno router
crea route
el suyo stack
propio tasks
;
El use
método de los express
app
push task
( middleware
función) al stack
del router
. El app.get
,, app.post
etc. crea un separado route
(con su propio stack
, y empuja hacia él el real handlers
del route
) en el router
, luego empuja hacia los router
envueltos en una función esos route
controladores. Es decir, cuando route
se crea router
stack
algo así como route
task
(función contenedora) con subtasks
push.
// pushes task to the router stack
app.use((req, res, next) => {
console.log('log request');
next();
});
// creates route, route stack,
// pushes tasks to the route stack,
// wraps tasks in a function (another task)
// pushes wrapper function to the
// router stack
app.get('/', (req, res, next) => {
res.send('Hello World');
});
Como a route
tiene su propia stack
llamada next
sin los argumentos, solo nos lleva al siguiente controlador de route
:
app.get('/',
(req, res, next) => {
console.log('first handler');
// passes the control to the second handler
next();
},
(req, res, next) => {
console.log('second handler');
res.send('Hello World');
}
);
Llamar next
dentro de a middleware
( express
se recomienda aplicar use
el método para montar a middleware
) nos lleva al siguiente route
o middleware
del router
, porque middleware
(cuando se montó) se empujó al router
stack
.
next
Acepta diferentes argumentos. Cualquier argumento que no sea 'route'
o 'router'
será tratado como un error y se pasará al error
middleware
que debe montarse después de todas las rutas y tener cuatro argumentos:
// error handling middleware
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.send(error.message || 'Server internal error');
});
La cadena 'route'
como argumento para next
omitirá todo lo restante route
handlers
y nos dará el siguiente route
de router
:
app.get('/',
(req, res, next) => {
console.log('first handler');
// passes control to the next route
next('route');
},
(req, res, next) => {
// this handler will be skipped
next();
}
);
app.get('/',
(req, res, next) => {
// this route will be called at the end
res.send('Hello World');
}
);
La cadena 'router'
como argumento next
nos saca de la corriente router
:
// router 'one'
app.get('/',
(req, res, next) => {
console.log('first handler');
// passes control to the next router
next('router');
},
(req, res, next) => {
// this handler will be skipped
next();
}
);
// router 'two'
app.get('/',
(req, res, next) => {
// this route will be called at the end
res.send('Hello World');
}
);