grep -n 'value.trim' src/main/java/**/PhoneValidator.java curl -i -X POST http://127.0.0.1:8080/api/users -d @no_phone.json
自定义的 @Phone 校验器里调了 value.trim(),传 null 时抛 NPE,接口返回 500,异常栈被包装成 ConstraintViolationException,原始堆栈在 cause 里翻了三层才看到。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。
原因不复杂,校验器默认假设值非空,没有先判 null。Bean Validation 规范里校验器抛运行时异常会中断整个校验,被框架包装后往外抛。全局异常处理只接了校验失败,没接校验执行异常。
围绕「校验器异常被吞」排查时,命令行跑一遍比在面板里点十次有用,面板会替你做掉不少隐式操作。改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。
验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。
处理上按这个顺序:校验器第一行加 null 判断 空值直接返回 true 交给 @NotBlank 处理;异常处理里单独捕获 ValidationException 并打完整 cause;给校验器补 null 和空串两条单测;用缺 phone 字段的请求确认返回 400 而不是 500。
改完只确认一件事:缺 phone 的请求返回 400 日志里不再出现 NPE 堆栈。
