Spring MVC自定义BindingResult导致日期格式异常求助
问题根源分析
你的问题出在新建的BeanPropertyBindingResult完全丢失了原BindingResult中的绑定上下文信息:
- 原BindingResult不仅包含
@Valid触发的验证错误,还包含数据绑定阶段的错误(比如日期格式转换失败的TypeMismatch错误),以及字段绑定的状态上下文(比如字段的原始输入值、绑定失败的细节)。 - 你只提取了
getFieldErrors()(仅字段级验证错误),然后新建了一个空的BeanPropertyBindingResult,再把排序后的错误加进去——这个新对象完全没有继承原BindingResult的绑定状态,导致Spring MVC无法正确识别created字段的绑定错误,进而表现为格式异常。 - 同时,你忽略了原BindingResult中的全局错误、绑定错误(比如日期转换错误),这些错误没有被加入到新的BindingResult中,也会导致字段状态异常。
修复方案
不要新建BindingResult对象,直接修改原对象的错误顺序,或者完整复制原对象的所有状态后再调整错误顺序:
方案一:直接修改原BindingResult的错误顺序
@RequestMapping(value = {"create"}, method = RequestMethod.POST) public String create(ModelMap modelMap, HttpSession session, RedirectAttributes ra, @ModelAttribute("product") @Valid Product product, BindingResult bindingResult) { if(bindingResult.hasErrors()) { // 获取所有错误并按规则排序 List<ObjectError> sortedErrors = customErrorMessageOrder(bindingResult.getAllErrors()); // 清空原错误列表,添加排序后的错误 bindingResult.getAllErrors().clear(); bindingResult.addAllErrors(new AbstractBindingResult() { @Override protected Object getActualBean() { return product; } @Override public String getObjectName() { return "product"; } @Override public List<ObjectError> getAllErrors() { return sortedErrors; } }); modelMap.put("categories", categoryService.findAll()); return "admin/product/create"; } return "redirect:/admin/product/index"; } private List<ObjectError> customErrorMessageOrder(List<ObjectError> allErrors){ // 筛选NotEmpty类字段错误 List<ObjectError> notEmptyErrors = allErrors.stream() .filter(e -> e instanceof FieldError && ("NotEmpty".equals(((FieldError) e).getCode()) || "password.notEmpty".equals(((FieldError) e).getCode()))) .collect(Collectors.toList()); if(notEmptyErrors.isEmpty()) { return allErrors; } // 筛选剩余错误:排除已在NotEmpty列表中的同字段错误 List<ObjectError> otherErrors = allErrors.stream() .filter(e -> !(e instanceof FieldError) || notEmptyErrors.stream().noneMatch(ne -> ((FieldError) ne).getField().equals(((FieldError) e).getField()))) .collect(Collectors.toList()); // 合并:NotEmpty错误优先,其余错误在后 notEmptyErrors.addAll(otherErrors); return notEmptyErrors; }
方案二:完整复制原BindingResult状态后调整顺序
如果必须新建BindingResult,要先复制原对象的所有绑定状态和错误,再调整顺序:
@RequestMapping(value = {"create"}, method = RequestMethod.POST) public String create(ModelMap modelMap, HttpSession session, RedirectAttributes ra, @ModelAttribute("product") @Valid Product product, BindingResult bindingResult) { if(bindingResult.hasErrors()) { // 新建BindingResult并完整复制原对象的所有状态和错误 BeanPropertyBindingResult newBindingResult = new BeanPropertyBindingResult(product, "product"); newBindingResult.addAllErrors(bindingResult); // 获取排序后的错误列表 List<ObjectError> sortedErrors = customErrorMessageOrder(newBindingResult.getAllErrors()); // 清空原有错误,添加排序后的错误 newBindingResult.getAllErrors().clear(); newBindingResult.addAllErrors(new AbstractBindingResult() { @Override protected Object getActualBean() { return product; } @Override public String getObjectName() { return "product"; } @Override public List<ObjectError> getAllErrors() { return sortedErrors; } }); modelMap.put("org.springframework.validation.BindingResult.product", newBindingResult); modelMap.put("categories", categoryService.findAll()); return "admin/product/create"; } return "redirect:/admin/product/index"; } // 同方案一的customErrorMessageOrder方法 private List<ObjectError> customErrorMessageOrder(List<ObjectError> allErrors){ List<ObjectError> notEmptyErrors = allErrors.stream() .filter(e -> e instanceof FieldError && ("NotEmpty".equals(((FieldError) e).getCode()) || "password.notEmpty".equals(((FieldError) e).getCode()))) .collect(Collectors.toList()); if(notEmptyErrors.isEmpty()) { return allErrors; } List<ObjectError> otherErrors = allErrors.stream() .filter(e -> !(e instanceof FieldError) || notEmptyErrors.stream().noneMatch(ne -> ((FieldError) ne).getField().equals(((FieldError) e).getField()))) .collect(Collectors.toList()); notEmptyErrors.addAll(otherErrors); return notEmptyErrors; }
内容的提问来源于stack exchange,提问作者FalAn
相关产品推荐
相关产品推荐

