express中间件请求顺序,中间件next请求流程走向
发布于 作者:苏南大叔 来源:程序如此灵动~
本文主要说明express的中间件的请求顺序问题,中间件如何加载,如何控制页面的流程走向,着重说明的是顺序。

苏南大叔的“程序如此灵动”博客,记录苏南大叔和计算机代码的故事。测试环境:node@16.14.2,express@4.18.2。
定义一个中间件
中间件的定义很容易,不容易的是内部的逻辑。中间件可以是async,也可以是普通的函数。
(req, res, next) => {
//...
next();
//...
}async (req, res, next) => {
//...
await next();
//...
}
如果是单独文件的话,还需要模块导出:
module.exports = (req, res, next) => {
//...
next();
//...
}module.exports = async (req, res, next) => {
//...
await next();
//...
}注意这里module.exports是个commonjs的模块写法,在主程序中可以require也可以import。
res和req
req是客户端请求,res是服务器端响应。所以,是req先end,res才开始构造,再end。req.body和res.body是不同的。先分析获取req.body,再构造发送res.body。
res.send()只能send一次,其会引发res的end事件。所以,在res.send()之后,再输出的话,就会引发错误。例如:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the clientError: Can't set headers after they are sent.先res.sendFile()再res.send()不会引发错误。但是,只会有后面发送的数据是生效的。

next()
next()是中间件之间相互沟通的桥梁。必须有next(),否则页面就可能会出现没有响应或者中间件没有执行的问题。但是,next()有三个变种:
| 变种 | 说明 |
|---|---|
| next() | 无 |
| await next() | 需要配置async |
| return next() | 当前中间件里面,return之后的的逻辑就无法执行了,不影响其他中间件里面的逻辑。 |
next()的言外之意就是:执行接下来的中间件,包括app.get()或者app.post()的主体逻辑。

async和await
这个中间件到底是个异步的,还是个同步的呢? 答案是两者都可以。但是,如果要await next()的话,就只能配合async。这个道理大家都懂。参考文章:
生效途径
中间件可以是全局的中间件,还可以是局部的中间件。如果注册为全局的中间件的话,就需要慎重考虑了。因为中间件之间可能会有冲突竞争关系,会导致不可抗的失败因素。
用作全局中间件
app.use(中间件)这样的操作,所有的页面都会经过这些中间件的过滤。
用作路由中间件
app.get("/test.html", 中间件, function (req, res) {
//...
});这种情况下,对应的中间件只会处理这个路由所在的文件请求。路由里面的中间件可以是个变量,也可以是个数组,效果上和使用多个app.use()效果类似。
执行顺序
整体来说,req=>res。但是,req和res结束于哪个中间件里面,不一定。或者说req可能结束于多个中间件里面,因为每个中间件都可以监控req的end事件。res的end事件,可以由res.send()来引发。任何一个插件(包括路由里面)res.send()之后,就意味着输出通道的结束,也就是res的end事件。后续逻辑可以执行,但是不可以输出。
先顺序执行app.use()全局定义的中间件,再执行顺序app.get()或者app.post()定义的中间件,然后执行app.get()或者app.post()。通过next()进行串联,所以,这些中间件内的放在next()之后的逻辑,就会在最后执行了。
因为一般来说,app.get()或者app.post()会有输出。之后才会去执行中间件里面,next()之后的逻辑。那么,能做的事情就很有限了,因为已经输出结果了。
执行顺序上,先【顺序】执行next()之前的逻辑,在【逆序】执行next()之后的逻辑。变数有两个:
return next()。当前中间件逻辑马上结束,不影响后续中间件逻辑。await next()。之后的逻辑在所有的逆序里面优先级提到最后,多个await,优先级顺序排到最后。(整体上来说,就是把所有的await都跳出来放到最后,他们之间相对顺序不变。)
测试代码如下:
let express = require("express");
let app = express();
let server = app.listen(3222, function () {
var port = server.address().port;
console.log(`访问地址为 http://localhost:${port}`);
});
app.use((req, res, next) => {
console.log("1");
next();
console.log("8");
})
app.use(async (req, res, next) => {
console.log("2");
await next();
console.log("7");
})
app.use(async (req, res, next) => {
console.log("3");
return next();
console.log("6");
})
let m1 = require("./middlewares/middle1");
let m2 = require("./middlewares/middle2");
let m3 = require("./middlewares/middle3");
let middlewares = [
m1, m2, m3
];
app.get("/test.html", middlewares, function (req, res) {
console.log("4");
res.send("sunan");
// res.send("苏南大叔");
console.log("5");
});
middlewares/middle1.js:
module.exports = (req, res, next) => {
var path = require('path');
var scriptName = path.basename(__filename);
console.log(scriptName+"前");
next();
console.log(scriptName+"后");
}middlewares/middle2.js【async+await】:
module.exports = async (req, res, next) => {
var path = require('path');
var scriptName = path.basename(__filename);
console.log(scriptName + "前");
await next();
console.log(scriptName + "后");
}middlewares/middle3.js【return】:
module.exports = (req, res, next) => {
var path = require('path');
var scriptName = path.basename(__filename);
console.log(scriptName + "前");
return next();
console.log(scriptName + "后");
}
执行顺序是这样的:
1 2 3 middle1.js前 middle2.js前 middle3.js前
4 5 middle1.js后 8 middle2.js后 7相关链接
结束语
更多express的相关经验文章,请点击: