Spring MVC 与 traceId 规范
Spring MVC 与 traceId 规范
适用场景
新增或修改 Controller、REST 接口、请求响应 DTO、全局异常、CORS、日志、审计、下游 HTTP 调用或链路跟踪时加载。
MVC 分层
- HTTP 接口使用
@RestController;路径、HTTP 方法和状态码表达协议语义,不用统一POST代替所有操作。 - Controller 只做 DTO 校验、协议转换和调用应用服务,不写业务规则、事务、数据访问或供应商调用。
- 应用服务负责用例编排和事务边界;领域服务负责可测试的业务决策;Repository/Client 分别负责持久化和外部协议适配。
- 请求/响应 DTO 与领域模型分离;转换逻辑放在所属边界附近,简单转换直接写清楚,不为单个 DTO 引入映射框架。
- API 返回明确 HTTP 状态和结构化 JSON;不使用“永远 200 + 业务 code”掩盖协议错误。
traceId 传输契约
统一名称如下,不允许各接口自行命名:
| 位置 | 名称 |
|---|---|
| HTTP 请求头 | X-Trace-Id |
| HTTP 响应头 | X-Trace-Id |
| MDC key | traceId |
| 错误响应字段 | traceId |
| Servlet request attribute | traceId |
- 前端所有 HTTP 方法通过同一个请求客户端或拦截器写入
X-Trace-Id,页面和业务组件不得重复拼接请求头。 - 前端使用
crypto.randomUUID()为一次请求生成 traceId;同一次自动重试沿用原值。后端调用下游服务时继续传递当前有效值。 - traceId 只用于关联日志和审计,不作为身份、权限、幂等键或业务主键。
- 跨域场景必须允许请求头
X-Trace-Id,并通过Access-Control-Expose-Headers暴露同名响应头;CORS 只开放实际前端来源,不使用带凭据的通配来源。
后端统一接收
- 只允许一个继承
OncePerRequestFilter的 traceId 过滤器统一接收请求头;核心方法为doFilterInternal(...)。 - Controller 不重复声明
@RequestHeader("X-Trace-Id"),Service 方法也不为日志目的层层增加 traceId 参数。 - 过滤器按以下顺序处理:
- 读取
X-Trace-Id。 - 接受 8 至 64 位的字母、数字、点、下划线或连字符;空值、超长值、空白和控制字符均视为非法。
- 缺失或非法时使用
UUID.randomUUID().toString()生成新值,不因链路字段缺失拒绝业务请求。 - 将有效值写入 request attribute、MDC 和
X-Trace-Id响应头,再调用过滤器链。 - 在
finally中删除 MDC 的traceId,防止线程复用造成串号。
- 读取
- header 名、MDC key 和校验规则各保留一个常量来源,不在过滤器、异常处理器和客户端拦截器中复制字符串。
- 健康检查、静态资源和错误分派也必须返回有效 traceId;确需排除路径时先证明该路径不产生日志或审计记录。
日志、错误与审计
- 日志格式统一包含 MDC 的
traceId;业务日志使用参数化日志,不拼接用户输入,不记录令牌、完整支付信息或不必要的供应商敏感字段。 @RestControllerAdvice从 request attribute 取得当前 traceId 并写入错误响应,不重新生成。- 审计记录在应用边界显式保存当前 traceId;领域模型和纯决策服务不得依赖
HttpServletRequest、MDC 或静态请求上下文。 - 下游 HTTP 客户端使用一个统一拦截器传递
X-Trace-Id,不得由每个 Client 方法手写。 - 使用
@Async、线程池、消息队列或响应式执行时,必须显式复制并在完成后清理 traceId;不得假设 MDC 自动跨线程传播。 - 如果后续接入 OpenTelemetry,优先兼容 W3C
traceparent,并由观测层映射业务使用的traceId;不要同时维护两套互不关联的链路标识。
测试与验收
- 合法
X-Trace-Id:响应头、MDC 日志、错误体和审计记录使用原值。 - 缺失或非法
X-Trace-Id:后端生成符合规则的新值,响应头和错误体一致。 - 正常响应、参数校验失败、业务冲突和未处理异常均可通过响应中的 traceId 定位对应日志。
- 同一请求结束后 MDC 被清理;连续处理两个不同请求不会串号。
- 下游 HTTP 调用收到当前请求的
X-Trace-Id。 - 前端不再在各业务方法重复生成或设置 traceId;统一请求客户端覆盖 GET、POST、PUT、PATCH、DELETE。
禁止做法
- 不把 traceId 放入 query、path 或业务请求体,不允许客户端通过 traceId 获得额外权限。
- 不在 Controller、Service、Repository 各生成一次 traceId,不在异常处理器覆盖已有值。
- 不使用不可清理的静态变量保存当前 traceId,不把 MDC 当成业务数据源。
- 不因 traceId 缺失返回伪造成功,也不吞掉原始业务异常。