koa2中间件开发
koa2 中间件机制
Koa 是一个简单、轻量的 Web 框架。Koa 的最大特色,也是最重要的一个设计,就是中间件(middleware) 。Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。Koa中使用 app.use()
用来加载中间件,基本上Koa 所有的功能都是通过中间件实现的。每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next
函数。只要调用next
函数,就可以把执行权转交给下一个中间件。
下图为经典的Koa洋葱模型
看看官网的经典示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const Koa = require('koa') const app = new Koa()
app.use(async (ctx, next) => { const start = Date.now() await next() const ms = Date.now() - start ctx.set('X-Response-Time', `${ms}ms`) })
app.use(async (ctx, next) => { const start = Date.now() await next() const ms = Date.now() - start console.log(`${ctx.method} ${ctx.url} - ${ms}`) })
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()
后面的代码,将会在下一个中间件及后面的中间件运行结束后再执行。
1 2 3 4 5 6 7
| module.exports = function () { return async function ( ctx, next ) { console.log( ctx.method, ctx.header.host + ctx.url ) await next() } }
|
消息闪现中间件
前面我们实现了用户登录注册,但是没有一个友好的提示如:注册成功、登陆成功等。一般一个操作完成后,我们都希望在页面上闪出一个消息,告诉用户操作的结果。其原先是出自 rails 的,用于在页面上显示一些提示信息。
我们就来实现一个基于session的消息闪现。新建middlewares
目录,并建一个flash.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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
吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // 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中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ... const flash = require('./middlewares/flash') ... app.use(flash()) ...
... signout (ctx, next) { ctx.session.user = null ctx.flash = { warning: '退出登录' } ctx.redirect('/') } ...
|