本文引用的文件
本文件面向AI有声书生成平台的中间件扩展与实践,围绕认证、错误处理、缓存、安全与限流等关键中间件,系统阐述其执行顺序、上下文传递、异常处理机制,并提供自定义中间件开发模板、配置管理、性能优化、链路设计、调试与监控、测试策略与部署注意事项。目标是帮助开发者在保持现有中间件体系稳定的同时,安全地扩展新的中间件能力。
中间件位于 server/src/middleware 目录,应用入口 server/src/app.ts 统一注册中间件;各业务模块通过路由控制器使用中间件。Redis 服务封装于 server/src/services/redis.service.ts,供缓存与限流等中间件使用。
graph TB
A["应用入口<br/>server/src/app.ts"] --> B["错误处理中间件<br/>server/src/middleware/errorHandler.ts"]
A --> C["性能监控中间件<br/>server/src/middleware/performance.ts"]
A --> D["日志中间件<br/>server/src/services/logger.service.ts"]
A --> E["安全中间件XSS/SQL注入<br/>server/src/middleware/security.ts"]
A --> F["CORS 中间件<br/>@koa/cors"]
A --> G["请求体解析<br/>koa-body/bodyparser"]
A --> H["静态文件挂载<br/>koa-mount/serve"]
subgraph "业务路由"
R1["认证路由<br/>server/src/modules/auth/auth.controller.ts"]
R2["TTS 路由<br/>server/src/modules/tts/tts.controller.ts"]
end
A --> R1
A --> R2
subgraph "服务层"
S1["Redis 服务<br/>server/src/services/redis.service.ts"]
end
B -. 使用 .-> S1
C -. 使用 .-> S1
E -. 使用 .-> S1
图表来源
章节来源
章节来源
中间件在应用启动时按顺序注册,形成“洋葱模型”调用链。每个中间件在 next() 前后可进行前置处理与后置处理,异常在错误处理中间件集中捕获。
sequenceDiagram
participant Client as "客户端"
participant App as "Koa 应用<br/>server/src/app.ts"
participant EH as "错误处理中间件"
participant PM as "性能监控中间件"
participant SEC as "安全中间件"
participant RL as "限流中间件"
participant AUTH as "认证中间件"
participant USAGE as "使用配额中间件"
participant CTRL as "业务控制器"
Client->>App : HTTP 请求
App->>EH : 进入错误处理
EH->>PM : 调用 next()
PM->>SEC : 调用 next()
SEC->>RL : 调用 next()
RL->>AUTH : 调用 next()
AUTH->>USAGE : 调用 next()
USAGE->>CTRL : 调用 next()
CTRL-->>USAGE : 返回响应或抛错
USAGE-->>AUTH : 返回响应或抛错
AUTH-->>RL : 返回响应或抛错
RL-->>SEC : 返回响应或抛错
SEC-->>PM : 返回响应或抛错
PM-->>EH : 返回响应或抛错
EH-->>Client : 结构化响应
图表来源
上下文传递:将用户信息写入 ctx.state.user,后续中间件与控制器可读取。
flowchart TD
Start(["进入认证中间件"]) --> CheckDev{"开发模式且禁用认证?"}
CheckDev --> |是| UseTestUser["写入测试用户到 ctx.state.user"] --> Next1["调用 next()"]
CheckDev --> |否| GetHeader["读取 Authorization 头"]
GetHeader --> HasHeader{"存在且格式正确?"}
HasHeader --> |否| ThrowUnauthorized["抛出未授权错误"] --> End
HasHeader --> |是| Verify["校验 JWT 签名与有效期"]
Verify --> OK{"校验通过?"}
OK --> |否| ThrowAuthFail["抛出认证失败/过期/无效"] --> End
OK --> |是| SetUser["写入用户信息到 ctx.state.user"] --> Next2["调用 next()"] --> End
图表来源
章节来源
提供多种业务错误类(未授权、禁止、资源不存在、请求错误、配额超限)以表达不同语义。
flowchart TD
Enter(["进入错误处理中间件"]) --> TryNext["尝试执行下游中间件/控制器"]
TryNext --> Catch{"是否抛出异常?"}
Catch --> |否| ReturnResp["返回正常响应"]
Catch --> |是| BuildResp["构造错误响应体<br/>状态码/错误码/消息"]
BuildResp --> DevEnv{"开发环境?"}
DevEnv --> |是| AddStack["附加堆栈信息"] --> Send
DevEnv --> |否| Send["发送错误响应"]
ReturnResp --> End
Send --> End
图表来源
章节来源
清理缓存:提供按前缀批量删除能力,便于灰度与维护。
flowchart TD
Enter(["进入缓存中间件"]) --> RedisOK{"Redis 可用?"}
RedisOK --> |否| Next["跳过缓存,直接调用 next()"] --> End
RedisOK --> |是| GenKey["生成缓存键支持自定义"]
GenKey --> GetCache["从 Redis 读取缓存"]
GetCache --> Hit{"命中?"}
Hit --> |是| SetHeaderHit["设置 X-Cache: HIT"] --> ReturnCache["返回缓存内容"] --> End
Hit --> |否| Exec["调用 next() 执行下游"]
Exec --> StatusOK{"状态码为 200 且有 body?"}
StatusOK --> |否| End
StatusOK --> |是| SetHeaderMiss["设置 X-Cache: MISS"] --> SaveCache["写入 Redis带 TTL"] --> End
图表来源
章节来源
敏感数据脱敏:对响应体中密码、令牌等字段进行脱敏处理。
flowchart TD
Enter(["进入安全中间件"]) --> XSS["递归过滤请求体/查询参数中的 XSS"]
XSS --> SetHeaders["设置安全响应头"]
SetHeaders --> Next["调用 next()"]
Next --> PostFilter{"是否需要脱敏响应?"}
PostFilter --> |是| Mask["递归脱敏敏感字段"] --> End
PostFilter --> |否| End
图表来源
章节来源
限流触发时设置 Retry-After 并返回 429。
flowchart TD
Enter(["进入限流中间件"]) --> GetLimiter["获取/创建限流器Redis/内存"]
GetLimiter --> GenKey["生成限流键IP/用户/手机号等"]
GenKey --> Consume["尝试消费 1 个配额"]
Consume --> OK{"是否允许?"}
OK --> |是| Next["调用 next()"] --> End
OK --> |否| Reject["设置 Retry-After 与 429 响应"] --> End
图表来源
章节来源
提供指标查询路由与重置能力。
flowchart TD
Enter(["进入性能监控中间件"]) --> Start["记录开始时间"]
Start --> Next["调用 next()"]
Next --> Done{"执行成功?"}
Done --> |是| Calc["计算耗时并更新全局与端点指标"] --> Slow{"是否慢请求?"}
Slow --> |是| Warn["记录慢请求警告"] --> SetHeader["设置 X-Response-Time"] --> End
Slow --> |否| SetHeader --> End
Done --> |否| IncErr["增加错误计数"] --> Throw["向上抛出异常"] --> End
图表来源
章节来源
将用户配额与用户信息写入 ctx.state,供后续中间件与控制器使用。
flowchart TD
Enter(["进入使用配额中间件"]) --> CheckUser{"是否存在用户ID?"}
CheckUser --> |否| SetUnlimited["写入无限制配额到 ctx.state.userQuota"] --> Next["调用 next()"] --> End
CheckUser --> |是| LoadUser["查询用户并校验存在性"]
LoadUser --> ResetDaily{"是否跨日?"}
ResetDaily --> |是| UpdateDaily["重置每日使用次数"] --> CheckQuota["按会员等级获取配额"]
ResetDaily --> |否| CheckQuota
CheckQuota --> DailyOK{"是否超过每日次数?"}
DailyOK --> |是| ThrowDaily["抛出配额超限错误"] --> End
DailyOK --> |否| WriteState["写入 ctx.state.userQuota 与 ctx.state.userInfo"] --> Next --> End
图表来源
章节来源
Redis 服务被缓存与限流中间件共享,需保证连接可用性与稳定性。
graph LR
EH["错误处理"] --> PM["性能监控"]
PM --> SEC["安全中间件"]
SEC --> RL["限流中间件"]
RL --> AUTH["认证中间件"]
AUTH --> USAGE["使用配额中间件"]
USAGE --> CTRL["业务控制器"]
EH -.-> REDIS["Redis 服务"]
PM -.-> REDIS
RL -.-> REDIS
CACHE["缓存中间件"] -.-> REDIS
图表来源
章节来源
章节来源
该中间件体系以“洋葱模型”清晰划分职责边界,通过统一的错误处理、安全防护、性能监控与缓存/限流机制,保障了平台在高并发与复杂业务场景下的稳定性与可运维性。扩展新中间件时,应遵循现有顺序与上下文约定,确保异常可兜底、性能可观测、配置可治理。
章节来源
章节来源
章节来源
章节来源
章节来源
章节来源
章节来源