koa2中间件开发
koa2 中间件机制
Koa 是一个简单、轻量的 Web 框架。Koa 的最大特色,也是最重要的一个设计,就是中间件(middleware) 。Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。Koa中使用 app.use()
用来加载中间件,基本上Koa 所有的功能都是通过中间件实现的。每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next
函数。只要调用next
函数,就可以把执行权转交给下一个中间件。
下图为经典的Koa洋葱模型
看看官网的经典示例:
const Koa = require('koa')
const app = new Koa()
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
ctx.set('X-Response-Time', `${ms}ms`)
})
// logger
app.use(async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}`)
})
// response
app.use(async ctx => {
ctx.body = 'Hello World'
})
app.listen(3000)
上面的执行顺序就是:请求 ==> response-time中间件 ==> logger中间件 ==> 响应中间件 ==> logger中间件 ==> response-time中间件 ==> 响应。
请求进来,先进到x-response-time
中间件,执行const start = new Date()
然后遇到await next()
,则暂停x-response-time
中间件的执行,跳转进logger
中间件,同理,最后进入响应中间件,响应中间件中没有await next()
代码,则开始逆序执行,也就是再先是回到logger
中间件,执行await next()
之后的代码,执行完后再回到x-response-time
中间件执行await next()
之后的代码。
koa2 中间件编写
我们来看看如何编写中间件,其实上面的logger、x-response-time都是中间件,通过app.use
注册,同时为该函数传入 ctx
和 next
两个参数。
ctx
作为上下文使用,包含了基本的 ctx.request
和 ctx.response
,还对 Koa
内部对一些常用的属性或者方法做了代理操作,使得我们可以直接通过 ctx
获取。比如,ctx.request.url
可以写成 ctx.url
。
next
参数的作用是将处理的控制权转交给下一个中间件,而 next()
后面的代码,将会在下一个中间件及后面的中间件运行结束后再执行。
// middleware/logger.js
module.exports = function () {
return async function ( ctx, next ) {
console.log( ctx.method, ctx.header.host + ctx.url )
await next()
}
}
消息闪现中间件
前面我们实现了用户登录注册,但是没有一个友好的提示如:注册成功、登陆成功等。一般一个操作完成后,我们都希望在页面上闪出一个消息,告诉用户操作的结果。其原先是出自 rails 的,用于在页面上显示一些提示信息。
我们就来实现一个基于session的消息闪现。新建middlewares
目录,并建一个flash.js
module.exports = function flash (opts) {
let key = 'flash'
return async (ctx, next) => {
if (ctx.session === undefined) throw new Error('ctx.flash requires sessions')
let data = ctx.session[key]
ctx.session[key] = null
Object.defineProperty(ctx, 'flash', {
enumerable: true,
get: () => data,
set: (val) => {
ctx.session[key] = val
}
})
await next()
}
}
这个flash消息就是将消息挂到session上再清空,只显示一次,刷新后就没有了。这个中间件可优化的地方还很多,这儿重点不是优化功能就先跳过。
我们还需添加一个显示提示的视图模板,就叫他notification.html
吧
// components/notification.html
{% if ctx.flash %}
<div class="notifications">
{% if ctx.flash.success %}
<div class="notification is-success">
{{ctx.flash.success}}
</div>
{% elif ctx.flash.warning %}
<div class="notification is-warning">
{{ctx.flash.warning}}
</div>
{% endif %}
</div>
{% endif %}
这个模板中,添加了success和warning两种提示。把它引入base.html
。
使用flash中间件
// index.js
...
const flash = require('./middlewares/flash')
...
app.use(flash())
...
// user.js
...
signout (ctx, next) {
ctx.session.user = null
ctx.flash = { warning: '退出登录' }
ctx.redirect('/')
}
...