责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿处理者链传递,直到某个处理者处理请求或链结束为止。它通过解耦请求发送者和处理者,提供灵活的请求处理机制。
设计模式原理
责任链模式通过构建一个处理者链,允许请求在链中传递,每个处理者决定是否处理请求或传递给下一个处理者。在Express.js中,中间件天然适合责任链模式,每个中间件处理请求的一部分逻辑,并通过next()函数传递给后续中间件。
结构
- 处理者接口(Handler):定义处理请求的方法和设置下一个处理者的方法。
- 具体处理者(ConcreteHandler):实现处理逻辑,决定是否处理或传递请求。
- 客户端(Client):发起请求并通过处理链处理。
优点
- 解耦性:请求发送者与处理者解耦,客户端无需知道具体处理者。
- 灵活性:动态调整中间件顺序或添加新中间件。
- 可扩展性:新增处理者只需实现新中间件,无需修改现有代码。
- 模块化:每个中间件职责单一,易于维护。
缺点
- 性能开销:长链可能导致请求传递开销。
- 未处理风险:若链配置不当,请求可能无人处理。
- 复杂性增加:复杂中间件链可能增加调试难度。
- 状态管理:中间件间共享状态需小心设计。
适用场景
- 请求处理管道:如Web应用的认证、日志、数据验证。
- 多级处理:如权限检查、请求预处理。
- 动态流程:处理者顺序或逻辑需动态调整。
- 中间件系统:如Express、Koa的中间件机制。
TypeScript 实现示例
我们实现一个Express.js应用,通过责任链模式处理API请求的认证和授权流程。中间件链包括日志记录、认证检查和权限验证,处理用户请求(如访问受限资源)。
项目结构
express-middleware/
├── src/
│ ├── middlewares/
│ │ ├── logging.middleware.ts
│ │ ├── auth.middleware.ts
│ │ ├── permission.middleware.ts
│ ├── routes/
│ │ ├── user.route.ts
│ ├── app.ts
│ ├── main.ts
├── tsconfig.json
├── package.json
1. 安装依赖
npm init -y
npm install express @types/express typescript @types/node ts-node
npx tsc --init
配置 tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src"
}
}
配置 package.json:
{
"scripts": {
"start": "npx ts-node src/main.ts"
}
}
2. 定义日志中间件 (middlewares/logging.middleware.ts)
import { Request, Response, NextFunction } from 'express';
export function loggingMiddleware(req: Request, res: Response, next: NextFunction): void {
console.log(`日志中间件:处理请求 ${req.method} ${req.url}`);
next();
}
说明:loggingMiddleware记录请求的HTTP方法和URL,调用next()传递给下一个中间件。
3. 定义认证中间件 (middlewares/auth.middleware.ts)
import { Request, Response, NextFunction } from 'express';
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
const authHeader = req.headers['authorization'];
if (authHeader && authHeader === 'Bearer valid-token') {
console.log('认证中间件:用户认证通过');
next();
} else {
console.log('认证中间件:用户认证失败,无效令牌');
res.status(401).json({ error: '未授权:无效令牌' });
}
}
说明:authMiddleware检查请求头中的令牌,验证通过则调用next(),否则返回401错误。
4. 定义权限中间件 (middlewares/permission.middleware.ts)
import { Request, Response, NextFunction } from 'express';
export function permissionMiddleware(req: Request, res: Response, next: NextFunction): void {
const userRole = req.headers['user-role'] as string;
if (userRole === 'admin') {
console.log('权限中间件:用户具有管理员权限');
next();
} else {
console.log('权限中间件:用户权限不足');
res.status(403).json({ error: '禁止访问:权限不足' });
}
}
说明:permissionMiddleware检查用户角色,管理员通过,否则返回403错误。
5. 定义路由 (routes/user.route.ts)
import { Router } from 'express';
import { loggingMiddleware } from '../middlewares/logging.middleware';
import { authMiddleware } from '../middlewares/auth.middleware';
import { permissionMiddleware } from '../middlewares/permission.middleware';
const router = Router();
router.get(
'/protected-resource',
loggingMiddleware,
authMiddleware,
permissionMiddleware,
(req: Request, res: Response) => {
console.log('路由处理:访问受限资源成功');
res.json({ message: '成功访问受限资源' });
}
);
export default router;
说明:路由定义中间件链(日志→认证→权限),成功通过所有中间件后返回响应。
6. 定义应用 (app.ts)
import express, { Express } from 'express';
import userRoutes from './routes/user.route';
export function createApp(): Express {
const app = express();
app.use(express.json());
app.use('/api', userRoutes);
return app;
}
说明:createApp配置Express应用,注册路由。
7. 入口文件 (main.ts)
import { createApp } from './app';
async function bootstrap() {
const app = createApp();
await app.listen(3000);
console.log('应用启动在 http://localhost:3000');
}
bootstrap();
8. 编译与运行
npx tsc
npm start
9. 测试示例
使用工具(如Postman)发送HTTP请求测试:
-
有效请求(通过所有中间件):
GET http://localhost:3000/api/protected-resource Authorization: Bearer valid-token user-role: admin -
无效令牌请求:
GET http://localhost:3000/api/protected-resource Authorization: Bearer invalid-token user-role: admin -
权限不足请求:
GET http://localhost:3000/api/protected-resource Authorization: Bearer valid-token user-role: user
运行后,控制台输出类似:
-
有效请求:
应用启动在 http://localhost:3000 日志中间件:处理请求 GET /api/protected-resource 认证中间件:用户认证通过 权限中间件:用户具有管理员权限 路由处理:访问受限资源成功 -
无效令牌请求:
日志中间件:处理请求 GET /api/protected-resource 认证中间件:用户认证失败,无效令牌 -
权限不足请求:
日志中间件:处理请求 GET /api/protected-resource 认证中间件:用户认证通过 权限中间件:用户权限不足
响应示例:
- 有效请求:
{ "message": "成功访问受限资源" } - 无效令牌:
{ "error": "未授权:无效令牌" } - 权限不足:
{ "error": "禁止访问:权限不足" }
解释输出:
- 中间件按顺序执行(日志→认证→权限),每个中间件决定是否调用
next()。 - 认证失败或权限不足时,链中断,返回错误响应。
- 责任链模式通过Express中间件实现,解耦请求处理逻辑。
总结
责任链模式的优点在于其解耦性、灵活性、可扩展性和模块化。客户端(路由)与处理者(中间件)解耦;中间件顺序可动态调整;新增中间件无需修改现有代码;每个中间件职责单一。该模式特别适用于请求处理管道、多级处理、动态流程和中间件系统场景,如Express.js的认证授权、日志记录、数据验证和Web请求处理管道。