配置与中间件
🎯 引言
学完这篇文章,你能看懂 config.default.js 的配置方式,会编写一个打印请求耗时的自定义中间件并挂载到应用中,还能说清楚 Egg 内置的 csrf 安全校验是什么、调试接口时如何处理。
🧱 config.default.js 配置方式
Egg 的配置统一放在 config 目录下,入口是 config.default.js。打开脚手架生成的文件:
module.exports = appInfo => {
const config = {};
// Cookie 签名密钥,用于加密 Cookie,每个项目要改成自己的
config.keys = appInfo.name + '_my_secret_key';
// 要挂载的中间件列表,后面会用到
config.middleware = [];
// 自定义配置:挂在 config 上,项目中随处可取
config.myAppName = 'egg-demo';
return config;
};
它导出一个函数,接收应用信息 appInfo,返回配置对象。自定义的配置项可以在代码里通过 this.config 或 ctx.app.config 读取:
const { Controller } = require('egg');
class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = this.config.myAppName;
}
}
module.exports = HomeController;
🧱 按环境区分配置
开发环境和线上环境的配置往往不同,比如端口、日志级别。Egg 的做法是:在 config.default.js 的基础上,再提供环境专属文件做覆盖:
config
├── config.default.js # 所有环境共享的默认配置
└── config.prod.js # 生产环境的覆盖配置
module.exports = () => {
const config = {};
// 只在生产环境生效的配置
config.logger = {
level: 'WARN',
};
return config;
};
Egg 启动时会根据运行环境自动合并:同名配置项,环境文件里的值覆盖默认值。学习阶段了解这个机制即可,日常开发只改 config.default.js 就够了。
🧱 中间件是什么
中间件是请求处理链条上的一环,可以在 Controller 执行前后插入通用逻辑,比如记录日志、统计耗时、统一处理错误。
Koa 课程学过的洋葱模型在这里完全适用:请求像剥洋葱一样穿过一层层中间件到达 Controller,响应再沿原路穿回来。Egg 的中间件写法和 Koa 一脉相承,只是多了约定的存放位置。
🛠 编写自定义中间件
约定:自定义中间件放在 app/middleware 目录下。我们来写一个统计请求耗时的 response_time.js:
// options 是配置项,app 是应用实例
module.exports = (options, app) => {
return async function responseTime(ctx, next) {
const start = Date.now();
// await next() 之前的代码在请求进入时执行
await next();
// await next() 之后的代码在响应返回时执行
const cost = Date.now() - start;
if (cost > options.limit) {
console.log(`慢请求:${ctx.method} ${ctx.url} 耗时 ${cost}ms`);
}
};
};
结构是固定的两层函数:外层接收配置和应用实例,内层就是标准的中间件函数。以 await next() 为分界线,可以分别在请求阶段和响应阶段插入逻辑。
🛠 挂载中间件并配置 options
光写好文件还不生效,要在 config.default.js 中挂载:
module.exports = appInfo => {
const config = {};
config.keys = appInfo.name + '_my_secret_key';
// 挂载中间件:文件名 response_time.js 对应驼峰名 responseTime
config.middleware = ['responseTime'];
// 中间件的配置项:以中间件名为 key,会作为 options 传入
config.responseTime = {
limit: 10,
};
return config;
};
保存后随便访问几个接口,终端就会打印出耗时超过 10ms 的请求:
慢请求:GET /users 耗时 25ms
response_time.js)会自动转换成驼峰名(responseTime),挂载和配置时都用驼峰名。这是 Egg 的统一约定。⚠️ 内置安全与 csrf 校验
Egg 内置了 egg-security 安全插件,默认开启多种防护,其中和我们调试接口直接相关的是 csrf 校验。
它默认要求 POST、PUT、DELETE 等请求携带 csrf token,防止跨站请求伪造攻击。所以你在学习阶段直接用 curl 或接口工具发 POST 请求,很可能会收到 403 missing csrf token 的报错。
学习阶段可以在配置中临时关闭它,方便调试:
config.security = {
csrf: {
enable: false,
},
};
🧾 小节总结
config.default.js导出函数返回配置对象,自定义配置通过this.config读取。config.prod.js等环境文件会在对应环境覆盖默认配置。- 自定义中间件放在
app/middleware下,两层函数结构,用await next()划分请求和响应阶段。 - 挂载用
config.middleware = ['responseTime'],配置项用同名 key(驼峰)传入,文件名用下划线。 - Egg 默认开启 csrf 校验,学习调试可临时关闭,生产环境必须开启。
❓ 知识问答
Q1:config.default.js 为什么要导出一个函数?
导出函数能拿到 appInfo(应用名、环境等信息),方便在配置里动态取值,比如用应用名拼接 config.keys。直接导出对象也可以,但函数形式更灵活。
Q2:中间件的执行顺序是怎样的?
按 config.middleware 数组里的顺序执行,越靠前越先进入、越后返回,符合洋葱模型。框架内置的中间件(如 bodyParser)排在自定义中间件之前。
Q3:只想让中间件对部分路径生效怎么办?
给配置项加 match 或 ignore,例如 config.responseTime = { match: '/api', limit: 10 },就只对 /api 开头的路径生效。
Q4:关了 csrf 后还有哪些常见的安全默认行为?
还有 XSS 过滤、响应头防护等。这些都是框架替我们把好的安全关,初学阶段保持默认即可,不要随便关闭。
🧪 小练习
编写一个 request_log 中间件,在每次请求结束时打印一行日志,格式为 GET /users -> 200(方法、路径、状态码),并挂载到应用中。
module.exports = (options, app) => {
return async function requestLog(ctx, next) {
// 请在这里编写代码
};
};
// 请在这里挂载 requestLog 中间件
🎉 恭喜你已经掌握配置与中间件的用法啦!下一篇我们学习插件与扩展,用插件为应用增加参数校验能力,再封装一个统一的响应格式。
