Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpringMVC校验机制参数顺序的坑 #12

Open
aCoder2013 opened this issue Oct 10, 2017 · 0 comments
Open

SpringMVC校验机制参数顺序的坑 #12

aCoder2013 opened this issue Oct 10, 2017 · 0 comments
Labels

Comments

@aCoder2013
Copy link
Owner

问题

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

@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:/"
}

解决过程

@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),一步一步跟踪调试发现进入了下面这段关键的代码段:

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,下面还有个关键方法,我们进去一探究竟:

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. 平常注意看文档,对用到的东西要了若指掌
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant