what
在 Node.js 中,中间件主要是指封装所有 Http 请求细节处理的方法。一次 Http 请求通常包含很多工作,如记录日志、ip 过滤、查询字符串、请求体解析、Cookie 处理、权限验证、参数验证、异常处理等,但对于 Web 应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让开发者能够关注在业务的开发上,以达到提升开发效率的目的,它的工作模型下图所示。
中间件的实现方式一(express)
- 如下定义三个简单的中间件:
const middleware1 = (req, res, next) => {
console.log('middleware1 start')
next()
}
const middleware2 = (req, res, next) => {
console.log('middleware2 start')
next()
}
const middleware3 = (req, res, next) => {
console.log('middleware3 start')
next()
}
- 通过递归的形式,将后续中间件的执行方法传递给当前中间件,在当前中间件执行结束,通过调用
next()方法执行后续中间件的调用。
// 中间件数组
const middlewares = [middleware1, middleware2, middleware3]
function run (req, res) {
const next = () => {
// 获取中间件数组中第一个中间件
//shift:get&delete
const middleware = middlewares.shift()
if (middleware) {
middleware(req, res, next)
}
}
next()
}
run() // 模拟一次请求发起
执行以上代码,可以看到如下结果:
middleware1 start
middleware2 start
middleware3 start
- 如果中间件中有异步操作,需要在异步操作的流程结束后再调用
next()方法,否则中间件不能按顺序执行。改写middleware2中间件:
const middleware2 = (req, res, next) => {
console.log('middleware2 start')
new Promise(resolve => {
setTimeout(() => resolve(), 1000)
}).then(() => {
next()
})
}
//执行结果与之前一致,不过middleware3会在middleware2异步完成后执行
中间件的实现方式二(koa)
- 有些中间件不止需要在业务处理前执行,还需要在业务处理后执行,比如统计时间的日志中间件。在方式一情况下,无法在
next()为异步操作时再将当前中间件的其他代码作为回调执行。因此可以将next()方法的后续操作(即将要调用的下一个中间件)封装成一个Promise对象(next()后,就执行,并且返回一个promise),中间件内部就可以使用next.then()形式完成业务处理结束后的回调。改写run()方法如下:
function run (req, res) {
const next = () => {
const middleware = middlewares.shift()
if (middleware) {
// 将middleware(req, res, next)包装为Promise对象,返回promise对象
return Promise.resolve(middleware(req, res, next))
}
}
next()
}
- 中间件的调用方式需改写为:
const middleware1 = (req, res, next) => {
console.log('middleware1 start')
// 所有的中间件都应返回一个Promise对象,即next()获得一个promise(使得可以调用then)
// Promise.resolve()方法:直接封装中间件返回的结果成为Promise对象,供下层中间件异步控制
return next().then(() => {
console.log('middleware1 end')
})
}
得益于 async 函数的自动异步流程控制,中间件也可以用如下方式来实现:
// async函数自动返回Promise对象
const middleware2 = async (req, res, next) => {
console.log('middleware2 start')
await new Promise(resolve => {//业务逻辑,和koa实现没有关系
setTimeout(() => resolve(), 1000)
})
await next()
console.log('middleware2 end')
//也可以写成:next.then().catch()之类的
}
const middleware3 = async (req, res, next) => {
console.log('middleware3 start')
await next()
console.log('middleware3 end')
}
express koa的中间件机制对比
- 比较于express优势:express无法在
next()为异步操作时再将当前中间件的接下来的代码作为回调执行