@Validated和@Valid的区别?校验级联属性(内部类)
- 2019 年 10 月 3 日
- 笔记
????
NBA???????????????????????
????
???Java??????????Java Bean Validation 2.0?JSR303?JSR349?JSR380?Hibernate-Validation 6.x????
???Spring??Controller????????????????Spring MVC??@Valid???JavaBean?????
???Spring?Spring?????????@Validated + MethodValidationPostProcessor???????????
??
???? ???Spring?????????????????????????????Spring MVC?Controller???????????????????Spring MVC??????~
?????????????????????Controller????????????????????????????~??????????????????????????????????
- ???????????????
Spring????????????????Spring MVC???????@Valid??????- ????????????
SpringBoot?????????????????????Spring MVC??????????????????
- ????????????
- ?????
Spring MVC?HandlerInterceptor?AOP????????????????????@EnableAspectJAutoProxy?????????~
??????????????????????????????????????????????????????????????????????????~
????
??????????Spring MVC?????????????????Java???????????????????????????????“??”???????????????Spring???????????Controller???@Validated????JavaBean???~
???????????????????
@Getter @Setter @ToString public class Person { @NotNull private String name; @NotNull @Positive private Integer age; @Valid // ?InnerChild???????? @NotNull private InnerChild child; @Getter @Setter @ToString public static class InnerChild { @NotNull private String name; @NotNull @Positive private Integer age; } } @RestController @RequestMapping public class HelloController { @PostMapping("/hello") public Object helloPost(@Valid @RequestBody Person person, BindingResult result) { System.out.println(result.getErrorCount()); System.out.println(result.getAllErrors()); return person; } }
??post???/hello Content-Type=application/json????json????
{ "name" : "fsx", "age" : "-1", "child" : { "age" : 1 } }
?????????
2 [Field error in object 'person' on field 'child.name': rejected value [null]; codes [NotNull.person.child.name,NotNull.child.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.child.name,child.name]; arguments []; default message [child.name]]; default message [???null], Field error in object 'person' on field 'age': rejected value [-1]; codes [Positive.person.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age]]; default message [?????]]
????????????????????????????????????????
????????????
@RequestBody????????????json????????????????????????~- ???????
BindingResult result?????????????400????????????????????org.springframework.web.bind.MethodArgumentNotValidException???????????????~
????????????????????????????????90%???????????????????????????~
????????????????????????@Valid???????????????????????????????????if else?????????????????????????case??@Valid?????????????JavaBean
??????????????????????????????????????????????????~
????
Controller?????@Valid????JavaBean?????Spring???????????????????????Spring MVC????Spring AOP?????~????????????
?????
??????????????????????????????????????????????????bug????~
??DataBinder/WebDataBinder
??Spring?????????????????????????????????
- ???Spring???Spring?????? — DataBinder????????
- ???Spring???Spring?????? — WebDataBinder?ServletRequestDataBinder?WebBindingInitializer…
DataBinder???????????org.springframework.validation??????Spring???????????????????????????????????????Spring???????????????????????????????????
????DataBinder????????bind(PropertyValues pvs)?validate()???????????/??????????????
public class DataBinder implements PropertyEditorRegistry, TypeConverter { ... @Nullable private AbstractPropertyBindingResult bindingResult; // ???BindingResult @Nullable private MessageCodesResolver messageCodesResolver; private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); // ????????org.springframework.validation.Validator // ??DataBinder ??????????????????Bean?????????????????????????????~~~? private final List<Validator> validators = new ArrayList<>(); public void bind(PropertyValues pvs) { MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ? (MutablePropertyValues) pvs : new MutablePropertyValues(pvs)); doBind(mpvs); } ... public void validate() { Object target = getTarget(); Assert.state(target != null, "No target to validate"); BindingResult bindingResult = getBindingResult(); // ???????? ??????target????~~~ // Call each validator with the same binding result for (Validator validator : getValidators()) { validator.validate(target, bindingResult); } } }
DataBinder?????????????????? + ?????????????????+?????????????~
????????
DataBinder???????????????????????????????????????????????~
Spring MVC???????
Spring MVC??????????????????????Spring MVC?????????HandlerMethodReturnValueHandler????
???Spring?Spring MVC???web?????—HandlerAdapter????—??????????????HandlerMethodReturnValueHandler
???????????@RequestBody???????????~
?????????HandlerMethodArgumentResolver???@RequestBody??????????RequestResponseBodyMethodProcessor?Spring??????????????????????????????~
RequestResponseBodyMethodProcessor
??????????????????MVC??????????????????@ResponseBody???????????supportsReturnType()??~?
???????????????????????????@RequestBody??~?
????????????HandlerMethodReturnValueHandler??????????HandlerMethodArgumentResolver???????Processor???Resolver/Handler??????????~
// @since 3.1 public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } // ??????????@ResponseBody???? @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } // ??????????????????????? @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // ????`Optional`??? parameter = parameter.nestedIfOptional(); // ???????HttpInputMessage?request?????? // ????????????Person?????????????????Person???????? Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); // ???????? // ????????????????????????????????????????personAAA???name????person String name = Conventions.getVariableNameForParameter(parameter); // ????binderFactory?????????????~ // ??web????ServletRequestDataBinderFactory if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); // ??????????????? if (arg != null) { // ????????+????~~~~~????????????????Errors?? // Applicable??? validateIfApplicable(binder, parameter); // ??????hasErrors()?????????????Errors???Spring MVC???????MethodArgumentNotValidException?? // ?????????? if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } // ???????? ??????????~~~ // ???????MODEL_KEY_PREFIX??key?~~~~ if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return adaptArgumentIfNecessary(arg, parameter); } // ????????????WebDataBinder???????????????~ ??????????? // ???MethodParameter parameter protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { // ?????????????????????@Valid?@RequestBody????? Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { // ??????@Validated Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); // ?????????????????@Validated?? ???????Valid??? ????? //???????????@Valid??????????????????Valid????~~~~~ if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { // ????group????binder?validate()????~~~~ // ???????????????????break?~~~ // ???????????@Validated?@Valid???????~ Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); binder.validate(validationHints); break; } } } ... }
??????????@Valid?????????????????????????
- ???
@RequestBody???????name(???)?????????????????????????????????????????????? @Validated?@Valid??????????????????????????"Valid"???????????????
1. ????????Valid???????value????????Group??
2. ????????@Validated???????@Valid??????????????????????~- ???
Errors(BindingResult)???????@Valid??????Spring MVC??????????????????????????????????MethodArgumentNotValidException??~
????@RequestBody??@Valid???????????????Spring MVC???@RequestPart?????????????????????????????RequestPartMethodArgumentResolver????????????????Multipart???????~
==??????????????????????????????????????@RequestBody????==
?????????????????????????
@PostMapping("/hello") public Object helloPost(@Valid @RequestBody Person personAAA, BindingResult result, @Valid @RequestBody Person personBBB) { ... }
???????
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed]
?????????????Body??????????????????????
??????????????URL????????????????????????????????????????????????~
???????Map?List??????
@RequestBody??????????????????Map?List????????????????????
???????????????????????????
@InitBinder???~~~
?????????@Validated?????????????????????????????????????~
????????
???????Spring???MethodArgumentNotValidException???????????????BindingResult????????????????????????
@RestControllerAdvice public class MethodArgumentNotValidExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleMethodArgumentNotValid(MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); StringBuilder stringBuilder = new StringBuilder(); for (FieldError error : bindingResult.getFieldErrors()) { String field = error.getField(); Object value = error.getRejectedValue(); String msg = error.getDefaultMessage(); String message = String.format("?????%s?????%s????%s?", field, value, msg); stringBuilder.append(message).append("rn"); } return Result.error(MsgDefinition.ILLEGAL_ARGUMENTS.codeOf(), stringBuilder.toString()); } }
????
????????Spring MVC???????????????????????????????????????????JavaBean
?????????????Body?????get???????JavaBean????????????
????????????????Controller????????????????????????????
@PutMapping("/hello/id/{id}/status/{status}") public Object helloGet(@PathVariable Integer id, @PathVariable Integer status) { ... return "hello world"; }
??????get???case?@RequestParam?????????????????????????????case???????????if else??????
?????????????????????????????????Spring??Controller????????????????Spring MVC??@Valid???JavaBean?????
==@Validated?@Valid???==
?????????????????????????????????????????????????????
@Valid???JSR-303?????????????????????????????????@Validated?Spring???????JSR-303?????????????????????????????????????????????- ?
Controller???????????@Valid?@Validated?????????????????? @Validated??????????????Spring????????????@Valid????????????????????@Validated??????????????@Valid????????????????
???????
Spring Boot?Web Starter?????Bean Validation???????????????????Spring MVC????????~
??
?????????????????????????@Validated??Controller?????????????????????????????????@Validated?@Valid??????????????????????????????~
????
????????????????-????-????-????-????
==The last????????????????????????????????????????????????~==
?????????????wx????Java??????3??
??????????wx??fsx641385712???????wx??????????"java??" ??????????