侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

Egg07 - 中间件

barwe
2022-04-17 / 0 评论 / 0 点赞 / 580 阅读 / 2,851 字
温馨提示:
本文最后更新于 2022-04-20,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

洋葱圈模型

在洋葱圈模型中,应用本身只相当于洋葱的核,每添加一个中间件,就相当于在在外面添加一层壳。

我们用一根针贯穿这个洋葱,那么针首先会接触并穿过最外层的壳,越过核心之后,最后从另一面的最外层壳穿出。

一个请求就是这样一根针穿过洋葱的行为:按顺序执行中间件,然后执行内核,最后按照相反的顺序再次执行中间件。

编写中间件

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 设置符合指定规则的请求不会经过这个中间件

matchignore支持多种验证方式:

  • 单个字符串:匹配以这个字符串开头的 URL
  • 字符串数组:匹配以这些字符串开头的 URL
  • 正则表达式
  • 函数:该函数接受ctx作为参数,返回boolean

参考

https://eggjs.github.io/zh/guide/middleware.html
http://www.wangchonghaha.cn/bookstact/JsServer/Eggjs/middleware.html

0

评论区