查看: 87|回复: 0

[Express.js]next()函数的作用

[复制链接]

2

主题

0

回帖

0

积分

积极分子

金币
0
阅读权限
220
精华
0
威望
0
贡献
0
在线时间
0 小时
注册时间
2009-12-20
发表于 2026-3-12 18:00:00 | 显示全部楼层 |阅读模式

一、概述

Express.js所谓中间件,就是从接收到用户http请求开始到调用响应对象(res)之间的那段处理过程,它的本质是一个类似这样的回调函数:

(req, res, next) => {
 
}

其中,req是请求对象,res是响应对象,next则是一个函数,调用它可以马上跳到下一个中间件回调函数。

一个匹配的路由可以存在很多个中间件,来作为该用户请求的处理过程,直至最终调用响应对象输出响应内容给用户,这个过程就要用到next函数:

app.use((req, res, next) => {
  console.log("pos_01");
  next();
});

app.use((req, res, next) => {
  console.log("pos_02");
  next();
});
  
app.get('/', (req, res, next) => {
    console.log("pos_03");
    res.send('Hello World');
});

当用户访问站点根目录,以上代码首先会执行第一个app.use传入的“中间件”,再跳到第二个app.use传入的“中间件”,最后才执行app.get绑定的"中间件",在此中间件当中不再调用next(),所以它是处理请求的终点,在此调用响应对象输出响应内容给用户:

//服务器输出
pos_01
pos_02
pos_03

//浏览器输出
Hello World

总之,一个指定了多个中间件的路由,这些中间件当中,如果不指定next(),它是不会主动跳到下一个中间件的,执行完毕的中间件如果没有调用res.send()之类的方法,请求就会挂起:

route.use((req, res, next) => {
  console.log("pos_01");
});

route.get('/', (req, res, next) => {
  console.log("pos_02");
  res.send('bird home page.');
});

由于第一个中间件不调用next(),所有符合此路由之下的剩余中间件都不会执行;

无论是任何方法调用的中间件都沿用该逻辑,以下两个例子都是执行了第一个中间件后,后续的路由即使符合匹配规则,也不会执行:

route.get('/', (req, res, next) => {
  console.log("pos_01");
});

route.get('/', (req, res, next) => {
  //不会执行
  console.log("pos_02");
});
const midd_1 = (req, res, next) => {
  console.log("pos_01");
};
const midd_2 = (req, res, next) => {
  //不会执行
  console.log("pos_02");
}

route.get('/', [midd_1, midd_2]);
route.get('/', (req, res, next) => {
  //不会执行
  console.log("pos_03");
});

 

二、next不会终止当前函数栈,只是纯粹调用了下一个函数(return的重要性)

在回调函数中调用next()并不会影响该函数流程,它只是纯粹进入了另外一个函数栈,当它返回,仍会继续执行剩下的内容:

app.use((req, res, next) => {
  console.log("pos_01");
  next();
  console.log("pos_011");
});

app.use((req, res, next) => {
  console.log("pos_02");
  next();
});
  
app.get('/', (req, res, next) => {
    console.log("pos_03");
    res.send('Hello World');
});

留意第一个app.use传入的“中间件”,它在调用next()后依然有后续逻辑,这些逻辑会等待next执行玩其他"中间件"后继续执行:

//服务器输出:
pos_01
pos_02
pos_03
pos_011

//浏览器输出:
Hello World

所以,我们应该在需要调用next()跳过当前中间件函数的后面加上return语句,防止它继续往后执行:

app.use((req, res, next) => {
  console.log("pos_01");
  next();
  return;
  console.log("pos_011");
});

在第一个app.use绑定的中间件处加上return,就能确保"pos_011"不会再输出;

三、next(param)

next()函数接受一个参数:

1.这个参数可以是一个Error对象,当参数为Error对象,传入这个对象的next()的行为将不是调用下一个"中间件"而是直接抛出错误并返回:

app.use((req, res, next) => {
  console.log("pos_01");
  next();
  console.log("pos_011");
});

app.use((req, res, next) => {
  console.log("pos_02");
  next(new Error('failed to load user'));
});
  
app.get('/', (req, res, next) => {
    console.log("pos_03");
    res.send('Hello World');
});

以上例子的app.get绑定的回调函数将不会执行,浏览器会输出来自next()的错误信息,但是"pos_011"依然会输出,因为next()接受的错误对象不中断流程的执行(这也许是因为回调函数在不同的tick的原因):

//服务器输出:
pos_01
pos_02
pos_011
Error: failed to load user
    at file:///mnt/hgfs/lroot/wwwroot/10003/test001/index04.js:19:8
    ...

//浏览器输出:
Error: failed to load user
    at file:///mnt/hgfs/lroot/wwwroot/10003/test001/index04.js:19:8
    ...

2. next('route'),当参数为'route',next()的行为是跳过当前路由set的所有中间件,从下一个符合条件的路由继续(如果有的话):

//第一个路由定义的一set中间件
route.get('/', (req, res, next) => {
  console.log("pos_01");
  next('route');
  return;//记得加上return,防止后续路由运行完毕后返回运行此路由剩下的上下文
  console.log("pos_02");
}, (req, res, next) => {
  console.log("pos_03");
});
//第二个路由定义的中间件
route.get('/', (req, res, next) => {
  res.send('bird home page.');
  console.log("pos_04");
});

Express将每个编写的app.METHOD(),route.METHOD()定义为一个独立的中间件set,这些方法的第二个以上参数,可以接受一个或多个函数作为只指定一个中间件,或指定一个数组,数组每一项都是函数作为指定多个中间件,总之,只要是指定了多个中间件函数的方法,都认为是中间件set,next('route')就是用来跳过这些中间件set的;

以上示例中,第一个route.METHOD()指定了中间件set,在第一个中间件执行到next('route')后,会直接跳过此数组剩余的中间件函数,直接去执行下一个route.METHOD()指定的中间件,所以"pos_03"是不会输出的,同时又因为加上了return,所以"pos_02"也不会输出,原因参考上文;

注意事項
  • 僅限路由回呼next('route') 只在透過 app.METHOD()router.METHOD() 載入的中間件函式中有效。
  • 不要在 app.use() 使用:在 app.use() 中呼叫 next('route') 的效果等同於普通的 next(),不會跳過後續的中間件。

 

三、指定多个"中间件"但没调用next()

当某个路由指定了多个中间件,但是中途其中一个中间件没有调用next(),那么它将不会自动切换到下一个中间件,结果就是执行完当前中间件后挂起请求(如果这个中间件最终也没有调用响应对象输出内容的话):

const cb0 = function (req, res, next) {
  console.log('CB0')
  //正常情况会调用next(),但是此处故意不调用
  //next()
}

const cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

const cb2 = function (req, res) {
  res.send('Hello from C!')
}
  
app.get('/', [cb0, cb1, cb2]);

结果就是,服务器输出"CB0",客户端请求挂起。

 



来源:https://www.cnblogs.com/yiyide266/p/19061166
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部