缘起
联调时,前端同学说接口报500了。。。
到PINPOINT上看了下,只有一个ConstraintViolationException null
一下子也没搞明白问题在哪
这是个老接口,本次需求也没有涉及这个点。
感觉凭空飞来一口锅。。。
追查
拿PINPOINT上报错请求的TransactionId 去ELK上查了下,只看到一个null。 说明:调用栈因为换行在ELK中查不到,好烦
继续
没有办法,只能登陆k8s,从容器日志中找找:
日志是找到了。
从执行栈的package上看,的确是与业务代码没有关系。
10分钟过去。。。
复现
从日志中拿到请求的数据,使用Postman调API。
稳稳复现
本地Debug
在Debug模式下,启动本地服务,准备Debug
打断点时,发现这个老接口有一些地方很奇怪:
看报错日志中有validation及异常ConstraintViolationException,隐隐觉得与参数校验有关。
在想,如果在接口的入口处把数据校验做了,不就好了?!
先定位问题,再确定fix方案
在本地swagger中发起请求。
执行到日志中看到的业务代码
User user = userPersonAccountService.userRegister(userPersonInfo);
时,按F7【step into】。
果然没有走到service层的userRegister方法中,而是跑到这个地方,
如图所示:
继续debug。。。
一路debug下来,终于找到关键所在。
如下图所示:
可以看到,这部分逻辑的确是做数据校验,校验结果也与期望的相同。
但日志为什么只是个null呢?
继续Debug。。。
参数校验的结果也拿到了,但没有赋值给ConstraintViolationException的message属性。
原来如此!
Spring参数校验中validation-api的作用,进门左手边有篇文章有讲到: 一个奇怪的HV000030: No validator could be found for constraint
validation-api 1.x,对应hibernate-validator 5.x validation-api 2.x,对应hibernate-validator 6.x
升级下版本?
spring 5.x仍是
throw new ConstraintViolationException(result);
无效!
点进去看下,message的值不再是Hard code的null,而是前面校验的结果。
看来升级validation-api版本,有戏!
在项目的pom.xml中添加validation-api 2.x的依赖,并且exclusion掉validation-api 1.x【一定要做】:
<exclusions>
<exclusion>
<artifactId>validation-api</artifactId>
<groupId>javax.validation</groupId>
</exclusion>
<exclusion>
<artifactId>hibernate-validator</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
work!
至此,未通过校验日志只打印了null的问题解决。 但不完美:返回的message除了期望的提示信息外,还有别的噪音。
调整参数校验的位置
在API入口进行校验。这也是改动最小的。
如下图所示,在需要校验的入参前增加@Validated或@Valid注解:
至此,问题解决。
前端传的参数没有通过校验导致。
问题是个小问题,但从发现到排查结束花了1个多小时!
小结: (1)validation-api 1.x时,Spring的方法级别的数据校验,校验结果会丢失
(2)接口的参数校验尽量前置
(3)推荐使用Spring MVC的参数校验,因为比较合适