前端开发之中间件模式

前言

中间件模式是现如今各个框架——尤其是 Web 框架使用得较多的一种开发模式。譬如大名鼎鼎的 Express, Koa 等均使用中间件来处理请求。那么,何为中间件模式呢?

什么是中间件

处理 Web 请求时,我们常常需要进行验证请求来源、检查登录状态、确定是否有足够权限、打印日志等操作,而这些重复的操作如果写在具体的路由处理函数中,明显会导致代码冗余,这个时候,我们就可以将这些通用的流程抽象为中间件函数,减少重复代码。

我们可以将 Web 请求想象为一条串联的管道,在管道中有多个关卡,请求数据由源头起,依次流过各关卡,每个关卡独立运作,既可以直接响应数据,又可以对请求稍作调整,并使之流向下一关卡,这个关卡,就是中间件。

Koa 的中间件

我们以 Koa 的中间件为例。

router
    .get('/users', loginChecker, async ctx => {
    	const users = await db.getUsers()
        ctx.body = users
	})
    .delete('/users/:id', adminChecker, async ctx => {
    	const { id } = ctx.params
        await db.deleteUserById(id)
    	ctx.body = { error: false }
	})
复制代码

我们使用 loginChecker 来检查用户是否登录,而对于高风险的删除操作,我们使用 adminChecker 来判断用户是否有管理员权限。

诚如你如看到的,我们在编写具体的路由处理函数时,只需考虑事务逻辑,大大降低了心智负担。我们甚至可以在最外层套一个错误处理中间件,catch 住所有异常,统一处理——这也是我为什么不在使用 await 时包装一层 try/catch

我们不讨论怎么开发 Koa 中间件,我们来谈一下如何实现中间件模式。

洋葱模型

Koa 实现了基于洋葱模型的中间件,在处理请求时,总是从外层中间件开始,到最内层,最后回到最外层。基于这个结论,我们可以使用一个数组来顺序存储中间件,并依次调用。

async function exec(middlewares, ctx) {
    let index = 0
    
    const next = async () => {
        const current = middlewares[index++]
        if (current) {
            await current(ctx, next)
        }
    }
    
    await next()
}

const ware1 = async (ctx, next) => {
    ctx.body = 'This is ware 1'
    await next()
    console.log("ware1 is back")
}

// 不调用 next,则不会进入下个中间件
const ware2 = async (next) => {
    ctx.body = 'Hello, world'
}
复制代码

ctx 意为上下文(Context),是中间件通信的桥梁,各中间件通过修改 ctx 来返回数据。如 Koa 就是通过修改 ctx.body 来改变响应体的。

通过向当前中间件传递调用 next 方法,来控制中间件执行的过程,实现了洋葱模型。值得注意的是,ware2 中未调用 next 方法,因此不管它后面是否还有其它中间件,它都会成为洋葱模型中的最里层,随后会回到外层的中间件去。

后记

本文介绍了什么是中间件,并附上简单的实现。如果你是微信小程序开发者,可以参考我使用中间件模式开发的小程序请求库 minapp-request,了解更完整的代码实现。

喜欢请点个赞,能关注一下就更好啦。

猜你喜欢

转载自juejin.im/post/5c7e85c06fb9a049dd810ac9