问题
今天遇到一个小问题,在进行表单提交之后,直接进入了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
}
直接打断点,发现根本没进入这段代码,因此猜测是验证的时候报了异常,因此将@Length
的max
改成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());
}
}
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异常。
总结
出了问题不要立马就去Google,先自己尝试去解决,打个断点进入源码调试下,进而分析问题可能产生的原因 平常注意看文档,对用到的东西要了若指掌