中间件组合与复用技巧
中间件组合与复用技巧
Koa2中间件机制基于洋葱模型,通过组合不同的中间件函数实现请求处理流程的灵活控制。合理组合与复用中间件能显著提升代码可维护性,避免重复逻辑。下面从基础组合到高级模式逐步展开具体实践方案。
基础中间件组合方式
最简单的组合方式是直接使用app.use()
链式调用:
const Koa = require('koa');
const app = new Koa();
app
.use(async (ctx, next) => {
console.log('Middleware 1 start');
await next();
console.log('Middleware 1 end');
})
.use(async (ctx, next) => {
console.log('Middleware 2 start');
await next();
console.log('Middleware 2 end');
});
这种基础组合会产生典型的洋葱圈执行顺序:
Middleware 1 start → Middleware 2 start → Middleware 2 end → Middleware 1 end
模块化中间件封装
将业务逻辑封装为独立模块是复用的基础模式。典型示例是错误处理中间件:
// error-handler.js
module.exports = function() {
return async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = { error: err.message };
ctx.app.emit('error', err, ctx);
}
};
};
// app.js
const errorHandler = require('./error-handler');
app.use(errorHandler());
中间件工厂模式
通过工厂函数创建可配置的中间件实例:
function logger(format = ':method :url') {
return async (ctx, next) => {
const str = format
.replace(':method', ctx.method)
.replace(':url', ctx.url);
console.log(str);
await next();
};
}
app.use(logger());
app.use(logger(':method :url :status'));
中间件合并技术
koa-compose
包可将多个中间件合并为单一中间件:
const compose = require('koa-compose');
const middleware1 = async (ctx, next) => { /*...*/ };
const middleware2 = async (ctx, next) => { /*...*/ };
const combined = compose([middleware1, middleware2]);
app.use(combined);
条件中间件加载
根据运行环境动态加载中间件:
const conditionalMiddleware = (env) => {
const devMiddleware = require('koa-dev-middleware');
const prodMiddleware = require('koa-compress');
return env === 'development'
? devMiddleware()
: prodMiddleware();
};
app.use(conditionalMiddleware(process.env.NODE_ENV));
中间件参数传递
通过上下文对象传递中间件间数据:
app.use(async (ctx, next) => {
ctx.state.user = await getUser(ctx.headers.authorization);
await next();
});
app.use(async ctx => {
// 可以获取上游中间件设置的数据
console.log(ctx.state.user);
});
中间件短路控制
通过next()
控制流程中断:
// 权限检查中间件
app.use(async (ctx, next) => {
if (!ctx.state.user) {
ctx.status = 401;
return; // 中断后续中间件执行
}
await next();
});
中间件组合模式实践
认证+授权组合示例
const auth = compose([
// JWT解析
async (ctx, next) => {
const token = ctx.get('Authorization');
ctx.state.user = jwt.verify(token, SECRET);
await next();
},
// 权限检查
async (ctx, next) => {
if (!ctx.state.user.roles.includes('admin')) {
throw new Error('Forbidden');
}
await next();
}
]);
app.use(auth);
请求预处理链
const preprocess = compose([
bodyParser(),
cors(),
helmet(),
requestId()
]);
app.use(preprocess);
中间件性能优化
避免在中间件中执行重复计算:
// 反模式
app.use(async (ctx, next) => {
const data = await expensiveOperation(); // 每次请求都执行
ctx.state.data = data;
await next();
});
// 优化方案
let cachedData;
app.use(async (ctx, next) => {
if (!cachedData) {
cachedData = await expensiveOperation(); // 仅首次执行
}
ctx.state.data = cachedData;
await next();
});
中间件错误处理策略
分层错误处理方案:
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
// 业务错误
if (err.isBusinessError) {
ctx.status = 400;
ctx.body = { code: err.code };
return;
}
// 系统错误
ctx.status = 500;
ctx.app.emit('error', err, ctx);
}
});
中间件测试方案
使用supertest
测试中间件组合:
const request = require('supertest');
const app = require('../app');
describe('middleware stack', () => {
it('should process auth middleware', async () => {
const res = await request(app)
.get('/')
.set('Authorization', 'Bearer token');
expect(res.status).toEqual(200);
});
});
动态中间件编排
运行时动态调整中间件顺序:
function createApp(middlewares) {
const app = new Koa();
middlewares.forEach(mw => app.use(mw));
return app;
}
const standardStack = [helmet(), bodyParser()];
const devStack = [...standardStack, morgan('dev')];
const prodStack = [...standardStack, compress()];
中间件元编程
通过装饰器增强中间件功能:
function measureTime(target) {
return async function timedMiddleware(ctx, next) {
const start = Date.now();
await target(ctx, next);
const duration = Date.now() - start;
ctx.set('X-Response-Time', `${duration}ms`);
};
}
app.use(measureTime(async (ctx) => {
ctx.body = await fetchData();
}));
中间件与TypeScript
强类型中间件定义示例:
interface KoaContextWithUser extends Koa.Context {
state: {
user?: {
id: string;
name: string;
};
};
}
const authMiddleware: Koa.Middleware<KoaContextWithUser> = async (ctx, next) => {
const user = await getUser(ctx.headers.authorization);
ctx.state.user = user;
await next();
};
中间件版本控制
实现API版本兼容的中间件:
function versionedMiddleware(versions) {
return async (ctx, next) => {
const version = ctx.get('X-API-Version') || 'v1';
const middleware = versions[version] || versions.default;
return middleware(ctx, next);
};
}
app.use(versionedMiddleware({
v1: legacyMiddleware,
v2: modernMiddleware,
default: modernMiddleware
}));
中间件调试技巧
使用调试命名空间定位问题:
const debug = require('debug')('app:middleware');
app.use(async (ctx, next) => {
debug('Request start %s %s', ctx.method, ctx.url);
const start = Date.now();
await next();
const duration = Date.now() - start;
debug('Request finished in %dms', duration);
});
中间件与事件系统结合
通过事件机制扩展中间件功能:
app.use(async (ctx, next) => {
ctx.app.emit('request_start', ctx);
try {
await next();
ctx.app.emit('request_success', ctx);
} catch (err) {
ctx.app.emit('request_error', err, ctx);
throw err;
}
});
app.on('request_error', (err, ctx) => {
console.error(`Request failed: ${ctx.url}`, err);
});
中间件执行顺序可视化
生成中间件堆栈示意图:
function inspectMiddleware(app) {
return app.middleware.map((mw, i) => ({
id: i,
name: mw._name || mw.name || 'anonymous',
toString: () => `${i}.${mw.name}`
}));
}
console.log(inspectMiddleware(app));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:自定义中间件的封装与发布
下一篇:中间件安全注意事项