一、koa2 实现自定义中间件
- 在
koa2
项目中,创建middleware
文件夹,用于存放中间件,再在里面创建m1.js
文件,定义中间件m1
,ctx
是维护request
和response
进出app
的承载,Koa Context
将node
的request
和response
对象封装到单个对象中,为方便起见许多上下文的访问器和方法直接委托给它们的ctx.request
或ctx.response
,不然的话它们是相同的,代码如下所示:
function m1(ctx) {
global.console.log('m1')
}
module.exports = function () {
return async function (ctx, next) {
global.console.log('m1 start')
// 执行当前的中间件
m1(ctx)
// 当前中间件执行完毕以后,执行下一个中间件
await next()
global.console.log('m1 end')
}
}
- 在
koa2
项目中的app.js
文件中,引入m1
中间件,并且使用它,代码如下所示:
const m1 = require('./middleware/m1')
app.use(m1())
koa2
中间件的过程分析:
- 首先是
next
的调用,我们知道Koa
的next
执行,其实就是在执行下一个中间件的函数,即下一个use
中的async
函数,为了保证后面的异步代码执行完毕后再继续执行当前的代码,所以我们需要使用await
进行等待 - 其次就是数据从接收到挂在
ctx.request.body
都在Promise
中执行,是因为在接收数据的操作是异步的,整个处理数据的过程需要等待异步完成后,再把数据挂在ctx.request.body
上,可以保证我们在下一个use
的async
函数中可以在ctx.request.body
上拿到数据,所以我们使用await
等待一个Promise
成功后再执行next
。
二、koa2 串联中间件
Koa
中间件是以级联代码(Cascading
) 的方式来执行的,类似于回形针的方式,也是洋葱模型。- 在
middleware
中,再创建m2.js
和m3.js
,定义中间件m2
和m3
,代码如下所示:
- m2.js
function m2(ctx) {
global.console.log('m2')
}
module.exports = function () {
return async function (ctx, next) {
global.console.log('m2 start')
m2(ctx)
await next()
global.console.log('m2 end')
}
}
- m3.js
function m3(ctx) {
global.console.log('m3')
}
module.exports = function () {
return async function (ctx, next) {
global.console.log('m3 start')
m3(ctx)
await next()
global.console.log('m3 end')
}
}
- 在
app.js
中,去使用m2
和m3
中间件,代码如下所示:
const m2 = require('./middleware/m2')
const m3 = require('./middleware/m3')
app.use(m2())
app.use(m3())
-
在最后的结果执行中,先执行
m1 start
,然后m1
,next
先不会执行,然后执行m2 start
,然后m2
,再后面是执行m3 start
,然后m3
,从先往后执行。但是最后执行next
的实现,是先执行m3
中间件,输出m3
,然后执行m2
中间件,输出m2
,最后执行m1
中间件,输出m1
,这个也说明了先执行的后出,后执行的先出,也是洋葱模型。 -
compose
这个方法的实现思想非常的重要,在Koa
源码中用于串联中间件,主要思想是递归函数。