Spring参数验证顺序问题

来源:互联网 发布:局域网共享文件软件 编辑:程序博客网 时间:2024/06/16 09:20

问题

今天遇到一个小问题,在进行表单提交之后,直接进入了400错误页面,这个比较诡异,我所做的无非就是进行了简单的参数验证,提取BindingResult中的信息放到Model中方便前台显示,如下:

1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/user/publish",method = RequestMethod.POST)
String publish(@Valid TopicForm topicForm,Model model,BindingResult result){
if(result.hasErrors()){
model.addAttribute("errors",result.allErrors)
return "/user/publish"
}
Set<Tag> tagSet = tagService.constructeTags(topicForm.tags)
topicService.publish(topicForm.build(tagSet))
return "redirect:/"
}

解决过程

1
2
3
4
5
6
7
8
9
10
11
12
13
@Canonical
class TopicForm {
@NotEmpty(message = "标题不能为空")
@Length(min = 6, max = 125, message = "标题最少6个字符")
String title
@NotEmpty
@Length(min = 15, max = 20, message = "内容必须在20-2W个字符哟")
String content //故意改成20,产生验证错误
}

直接打断点,发现根本没进入这段代码,因此猜测是验证的时候报了异常,因此将@Lengthmax改成20,产生错误结果,然后进入org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator,在isValid方法上右键Add to watches,打个断点(友情提示,本人用的IDEA-16),一步一步跟踪调试发现进入了下面这段关键的代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, webRequest);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
}

就是在这个地方抛出了一个BindException的异常,然后Spring进行了其他一些处理进去了400页面,不重要,我们看看这个判断条件,hasErrors()是用来判断是否有参数验证错误,这里很明显为true,下面还有个关键方法,我们进去一探究竟:

1
2
3
4
5
6
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}

这里一看就明晰了,getParameterIndex()获取的就是@Valid标注的方法参数索引,然会去判断紧跟其后的参数是否为Errors的子类,这时候我想到上面那个publish方法,我将Model作为其后续参数,而BindingResult为最后一个,因此肯定会返回true,导致抛出BindException异常。

总结

  1. 出了问题不要立马就去Google,先自己尝试去解决,打个断点进入源码调试下,进而分析问题可能产生的原因
  2. 平常注意看文档,对用到的东西要了若指掌
原创粉丝点击