TypeScript设计模式:责任链模式

54 阅读5分钟

责任链模式(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请求处理管道。