egg.js 基于 Koa 实现,其中间件机制和 Koa 一样基于洋葱模型

编写中间件

写一个打印请求耗时的中间件
app/middleware/cost.js

  1. async function cost(ctx, next) {
  2. const now = Date.now();
  3. await next();
  4. ctx.set('X-Response-Time', `${Date.now() - now}ms`);
  5. }

一般中间件都会有自己的配置,为了避免每次使用配置,egg.js 对中间件配置做了约定

  1. 中间件存放在 app/middleware/ 目录下
  2. 每个中间件需要使用一个方法包裹,并 export 出来,方法接收由框架传来的两个参数
    1. options:中间件的配置项,框架会把 app.config[middlwwareName] 传递进来
    2. app:当前的 application 实例

因此需要包装一下上面写的中间件

  1. module.exports = (options, app) => {
  2. return async function cost(ctx, next) {
  3. const now = Date.now();
  4. await next();
  5. ctx.set('X-Response-Time', `${Date.now() - now}ms`);
  6. };
  7. };

中间件可以添加配置,允许开发者自定义响应头

  1. module.exports = options => {
  2. const header = options.header || 'X-Response-Time';
  3. return async function cost(ctx, next) {
  4. const now = Date.now();
  5. await next();
  6. ctx.set(header, `${Date.now() - now}ms`);
  7. };
  8. };

使用中间件

中间件编写完成后还需要手动挂载使用,支持以下方式:

应用全局使用

如果希望中间件应用级生效,可以把中间件在 config 文件中开启

  1. // config/config.${env}.js
  2. config.middleware = ['cost'];
  3. config.cost = {
  4. header: 'egg-cost',
  5. };

这样所有请求都会应用 cost 中间件,在响应头中使用 egg-const 输出响应时间

部分 router 使用

如果希望中间件只对单条路由生效,可以在路由中配置

  1. const cost = app.middleware.cost({ header: 'egg-cost' });
  2. app.get('/', cost, 'home.index');

通过规则配置

如果只希望某些特定规则的路由生效,一个个配置又十分麻烦,可以使用规则匹配,无论是应用层加载的中间件还是框架自带中间件,都支持几个通用的配置项:

  • enable:控制中间件是否开启
  • match:设置只有符合某些规则的请求才会经过这个中间件
  • ignore:设置符合某些规则的请求不经过这个中间件
    1. // config/config.${env}.js
    2. config.middleware = ['cost'];
    3. config.cost = {
    4. enable: true,
    5. ignore: /^\/api/,
    6. header: 'egg-cost',
    7. }
    match 和 ignore 支持的参数都一样,只是作用完全相反,不允许同时配置。match 和 ignore 支持多种类型的配置方式
  1. 字符串:当参数为字符串类型时,配置的是一个 url 的路径前缀,所有以配置的字符串作为前缀的 url 都会匹配上,也可以直接使用字符串数组
  2. 正则:当参数为正则时,直接匹配满足正则验证的 url 的路径
  3. 函数:当参数为一个函数时,会将请求上下文传递给这个函数,最终取函数返回的结果(true/false)来判断是否匹配
    1. match(ctx) {
    2. // 只有 ios 设备才开启
    3. const reg = /iphone|ipad|ipod/i;
    4. return reg.test(ctx.get('user-agent'));
    5. },
    有关更多的 match 和 ignore 配置情况,详见 egg-path-matching

    使用 koa 中间件

    因为同样使用洋葱模型,koa 中间件可以通过简单包装轻松引入 egg.js。以 koa-compress 为例,在 Koa 中使用时: ```javascript const koa = require(‘koa’); const compress = require(‘koa-compress’);

const app = koa();

const options = { threshold: 2048 }; app.use(compress(options));

  1. egg.js 使用<br />_app/middleware/compress.js_
  2. ```javascript
  3. // koa-compress 暴露的接口(`(options) => middleware`)和框架对中间件要求一致
  4. module.exports = require('koa-compress');

config/config.default.js

  1. module.exports = {
  2. middleware: [ 'compress' ],
  3. compress: {
  4. threshold: 2048,
  5. },
  6. };