洋葱圈模型
在洋葱圈模型中,应用本身只相当于洋葱的核,每添加一个中间件,就相当于在在外面添加一层壳。
我们用一根针贯穿这个洋葱,那么针首先会接触并穿过最外层的壳,越过核心之后,最后从另一面的最外层壳穿出。
一个请求就是这样一根针穿过洋葱的行为:按顺序执行中间件,然后执行内核,最后按照相反的顺序再次执行中间件。
编写中间件
export default function(options?: Record<string, any>, app?: Application) {
return async function(ctx: Context, next: () => Promise<void>) {
console.log('Firstly run demo middleware')
await next()
console.log('Secondly run demo middleware')
}
}
Egg 默认约定一个中间件就是放置在 app/middleware 目录下的一个文件,文件以中间件名称命名。
它需要默认导出一个普通函数,该函数有两个参数:
options
: 中间件的配置,等价于app.config.中间件名称
app?
: 当前应用的 Application 实例
这个普通函数需要返回一个异步函数,该函数的参数固定为两个:
ctx
: 处理的请求的上下文next
: 异步执行洋葱圈模型中当前中间件层内部的所有步骤
Egg 的中间件与 Koa 的中间件写法一致,Koa 中间件可以应用于 Egg 应用。
使用中间件
编写的中间件需要注册到应用才能使用。
使用中间件处理请求
按照洋葱圈模型,在处理请求时中间件会被调用两次。
export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;
// 注册自定义中间件
config.middleware = ['demo'];
// 配置中间件,传递给 options 参数
config.demo = {
name: 'demoMiddleware',
}
}
在中间件中可通过options
或者app.config.demo
获取到配置的参数。
在框架和插件中使用中间件
在 config/config.default.ts 中注册的中间件只能在处理请求时使用,它们会被保存到app.config.appMiddleware
里面。
除此之外,还有一个app.config.coreMiddleware
注册了框架默认的核心中间件。
要想在 coreMiddleware 上做一些事情,需要在 app.ts 中注册中间件。
例如注册一个统计请求响应时间的中间件到框架中,该中间件需要在所有中间件之前执行,并在最后再被执行一次。
import { Context } from 'egg'
export default function () {
return async function (_ctx: Context, next: () => Promise<void>) {
const start = Date.now()
await next()
const end = Date.now()
console.log('Time consuming: ', end - start)
}
}
import { Application, Context } from 'egg'
export default function (app: Application) {
app.config.coreMiddleware.unshift('report')
}
应用层注册的app.config.appMiddleware
和框架默认的app.config.coreMiddleware
都会被加载器加载,并挂载到app.middleware
上。
只对指定路由使用中间件
在应用层面和框架层面注册的中间件都会应用到所有请求。
可以在 app/router.ts 中配置对单个路由的中间件。
import { Application } from 'egg'
export default (app: Application) => {
const { controller, router } = app
// 获取中间件对象
const demo = app.middleware.demo({ name: 'demo' })
// 为路由指定中间件
router.get('/', demo, controller.home.index)
}
这种方式传递的参数可以通过options
访问到,但是app.config
上没有对应的中间件参数对象。
框架默认中间件
应用层的中间件指的是我们作为框架使用者自定义的中间件,它们在配置文件中被注册,在每次请求发生时自动调用。
框架层的中间件指的是框架自带的默认中间件,这些中间件提供了较为基础的功能,一般不需要我们修改。
框架层中间件的配置方式与应用层的中间件配置方式相同,例如修改默认中间件 bodyParser 的配置:
export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;
config.bodyParser = {
jsonLimit: '10mb',
}
}
默认中间件在应用中间件之前被加载,它们更靠近洋葱中心的位置。
默认中间件不能被应用中间件覆盖,如果存在同名中间件,则会报错。
使用 Koa 的中间件
Egg 的中间件编写规范与 Koa 一致,这表示可以直接在 Egg 中引入 Koa 的中间件。
例如注册 Koa 的中间件 koa-compress:
import compress from 'koa-compress'
export default compress
export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;
config.middleware = ['compress'];
config.compress = {
threshold: 2048,
}
}
有些 Koa 中间件可能与 Egg 的中间件规范((options) => middleware
)存在差异,需要进行转换。
通用配置
所有层面的中间件都有一些通用配置:
enable
是否启用该中间件,例如关闭框架默认中间件match
设置只有符合某些规则的请求才会经过这个中间件ignore
设置符合指定规则的请求不会经过这个中间件
match
和ignore
支持多种验证方式:
- 单个字符串:匹配以这个字符串开头的 URL
- 字符串数组:匹配以这些字符串开头的 URL
- 正则表达式
- 函数:该函数接受
ctx
作为参数,返回boolean
值
参考
https://eggjs.github.io/zh/guide/middleware.html
http://www.wangchonghaha.cn/bookstact/JsServer/Eggjs/middleware.html
评论区