接口参数校验应统一拦截MethodArgumentNotValidException和ConstraintViolationException,结构化返回400响应;使用分组校验(Create/Update)按场景启用规则;封装@ValidMobile等自定义注解提升语义;响应体中文提示、日志记录脱敏上下文,确保准、快、可维护。

接口参数校验失败时,不建议直接抛出原始的 ConstraintViolationException 或让框架默认返回 500 错误页。应统一捕获、结构化响应,并兼顾可读性与调试效率。
统一拦截校验异常
用 @ControllerAdvice + @ExceptionHandler 拦截 MethodArgumentNotValidException(用于 @Valid 注解在 DTO 上)和 ConstraintViolationException(用于 @Valid 注解在方法参数上)。
- 区分两种异常来源:前者有
BindingResult,后者需从Set提取字段和消息> - 提取错误字段名(
violation.getPropertyPath().toString())、校验规则(如@NotBlank)、自定义提示(violation.getMessage()) - 组装成标准错误响应体,例如:
{"code": 400, "message": "参数校验失败", "details": [{"field": "email", "reason": "邮箱格式不正确"}]}
使用分组校验控制场景
避免一个 DTO 承担所有接口的校验逻辑。通过校验分组(interface 标记)按业务场景启用不同规则。
- 定义分组:
public interface Create {}和public interface Update {} - Dubbo 接口或 Controller 方法中指定分组:
@Validated(Create.class) @RequestBody UserDTO dto - 字段上标注分组:
@NotBlank(groups = Create.class)、@NotNull(groups = Update.class)
自定义校验注解提升语义
对业务强相关的规则(如“手机号必须是 11 位且以 1 开头”),不要堆砌多个基础注解,而是封装为可复用的自定义注解。
立即学习“Java免费学习笔记(深入)”;
- 写一个
@ValidMobile注解,配合ConstraintValidator实现 - 在 message 属性中支持占位符,如
message = "手机号 {value} 不合法",并在校验器中传入上下文值 - 注解可标注在字段或整个类上,便于未来扩展(如校验对象间关联逻辑)
日志记录与前端友好提示
异常响应要对前端友好,但后端日志需保留完整上下文,方便排查。
- 响应体中的
reason字段用中文、简洁明确(如“用户名不能为空”),不暴露技术细节 - 同步记录 WARN 日志,包含请求 ID、URI、参数快照(脱敏)、完整校验失败列表
- 对高频失败字段(如 token 过期、sign 签名校验)可单独分类,便于监控告警
基本上就这些。校验不是越严越好,而是要准、要快、要可维护。把规则收口、分层、可配置,比堆 @NotNull 更重要。










