JSR標準和Spring校驗框架
Java
的JSR-303
標準的數據校驗的核心接口是javax.validation.Validator
,該接口根據目標對象中標註的校驗註解進行數據校驗,並得到校驗結果。
Spring
也有自己的校驗框架,同時支持JSR-303
標準的校驗框架。Spring
的DataBinder
在進行數據綁定時,同時調用校驗框架完成數據校驗工作。
Spring
的校驗框架包是org.springframework.validation
,其中LocalValidatorFactoryBean
同時實現了Spring
的Validator
和JSR-303
的Validator
接口,但是Spring
本身沒有提供JSR-303
的實現,因此必須將實現了JSR-303
的jar
放在類路徑下。
配置校驗框架的處理器
<mvc:annotation-driven validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor" />
上面配置了LocalValidatorFactoryBean
和MethodValidationPostProcessor
兩個Bean
,它們的區別:
LocalValidatorFactoryBean
同時實現了Spring
的Validator
和JSR-303
的Validator
接口LocalValidatorFactoryBean
可以完成i18n
MethodValidationPostProcessor
可以實現在方法上校驗基本數據包裝類型和String
類型等單獨的對象,但是要在異常處理器中處理ConstraintViolationException
異常- 兩個
Bean
可以同時存在
重要的兩個註解@Valid和@Validated
這兩個註解都是進行數據校驗的標誌,但是有區別:
@Valid
可以在方法/成員變量/構造函數/方法參數上使用;@Validated
可以在類/方法/方法參數上使用;區別在於成員變量上是否可以使用- 由於
@Valid
可以在成員變量上使用,因此可以嵌套校驗 @Valid
會把校驗不通過的信息交給BindingResult
中,因此在controller
的方法中,@Valid
和BindingResult
要同時存在@Validated
可以在類上使用,可以配合MethodValidationPostProcessor
實現校驗基本數據包裝類型和String
類型等單獨的對象
案例
-
依賴
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> <exclusions> <exclusion> <artifactId>validation-api</artifactId> <groupId>javax.validation</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency>
-
@Validated
@RestController @RequestMapping("/validated") @Validated public class ValidatedController { @RequestMapping("/test") public Object test(@NotNull(message = "不能爲null") String name) { return new Object(); } } @RestControllerAdvice public class ExceptionHandle { @ExceptionHandler(ConstraintViolationException.class) @ResponseBody public Object constraintViolationException(ConstraintViolationException e) { StringBuilder stringBuilder = new StringBuilder(); for (ConstraintViolation constraintViolation : e.getConstraintViolations()) { stringBuilder.append(constraintViolation.getMessage()).append(","); } if (stringBuilder.length() > 0) { stringBuilder.deleteCharAt(stringBuilder.length() - 1); } return stringBuilder.toString(); } }
-
@Valid
public class Person implements Serializable { @NotNull(message = "person.name_is_null") private String name; @Valid private Phone phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public Phone getPhone() { return phone; } public void setPhone(Phone phone) { this.phone = phone; } } public class Phone implements Serializable { @NotNull(message = "phone.name_is_null") private String name; private String price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } } @ResponseBody @RequestMapping("/test") public Object test(@Valid Person person, BindingResult result) { if(result != null){ if(result.hasErrors()){ for (FieldError fieldError : result.getFieldErrors()) { System.out.println(fieldError.getDefaultMessage()); } } } return new Object(); }
-
出錯的使用
@ResponseBody @RequestMapping("/test") public Object testBindingResult(@NotBlank String name, BindingResult result) { if (result.hasErrors()) { System.out.println(result.getFieldError("name").getDefaultMessage()); } return new Object(); }
會報錯:
public Object test(@NotBlank String name, BindingResult result) 嚴重: Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the @RequestBody or the @RequestPart arguments to which they apply: public com.p7.framework.common.ResultBean com.p7.framework.controller.BindingResultController.testBindingResult(java.lang.String,org.springframework.validation.BindingResult)]