前言
Java API 标准 (JSR303) 定义了 Bean 校验的规范 validation-api,但没有提供实现。hibernate validation 是对这个标准的实现,并减少了校验注解如 @Email、@Length 等。Spring Validation 是对 hibernate validation 的二次封装,用于反对 spring mvc 参数主动校验。接下来,咱们以 spring-boot 我的项目为例,介绍 Spring Validation 的应用。简直涵盖你须要的 SpringBoot 所有操作
引入依赖
形式一:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>
形式二:
(本文采纳此形式)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
实战局部
此文须要仍需引入 web 的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在 web 服务里通常会通过以下两种模式进行传参:
POST
、PUT
申请,应用requestBody
传递参数;GET
申请,应用 requestParam/PathVariable 传递参数。
requestBody
参数校验
POST、PUT 申请个别会应用 requestBody 传递参数,这种状况下,后端应用 DTO 对象进行接管。只有给 DTO 对象加上 @Validated 注解就能实现主动参数校验。比方,有一个保留 User 的接口,要求 userName 长度是 2 -10,account 和 password 字段长度是 6 -20。如果校验失败,会抛出 MethodArgumentNotValidException 异样,Spring 默认会将其转为 400(Bad Request)申请。
DTO 示意数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输应用的。在 spring-web 我的项目中能够示意用于接管申请参数的 Bean 对象。
-
在
DTO
字段上申明束缚注解import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotNull; public class UserDTO { private Long userId; @NotNull @Length(min = 2,max = 10) private String userName; @NotNull @Length(min = 6,max = 20) private String account; @NotNull @Length(min = 6,max = 20) private String password; public Long getUserId() {return userId;} public void setUserId(Long userId) {this.userId = userId;} public String getUserName() {return userName;} public void setUserName(String userName) {this.userName = userName;} public String getAccount() {return account;} public void setAccount(String account) {this.account = account;} public String getPassword() {return password;} public void setPassword(String password) {this.password = password;} }
-
在办法参数上申明校验注解
这种状况下,应用 @Valid 和 @Validated 都能够。import java.util.HashMap; import java.util.Map; @RestController public class UserController {@PostMapping("/save") public Map<String,Object> saveUser(@RequestBody @Validated UserDTO userDTO){Map<String,Object> res = new HashMap(); res.put("code","200"); res.put("message","ok"); return res; } }
requestParam/PathVariable 参数校验
GET 申请个别会应用 requestParam/PathVariable 传参。如果参数比拟多 (比方超过 6 个),还是举荐应用 DTO 对象接管。否则,举荐将一个个参数平铺到办法入参中。在这种状况下,必须在 Controller 类上标注 @Validated 注解,并在入参上申明束缚注解 (如 @Min 等)。如果校验失败,会抛出 ConstraintViolationException 异样。代码示例如下:
@RestController @Validated public class SelectController {@GetMapping("/{userId}") public Map<String,Object> detail(@PathVariable("userId") @Min(1000000000L) Long userId){Map<String,Object> res = new HashMap(); res.put("code","200"); res.put("message","ok"); return res; } @GetMapping("/getByAccount") public Map<String,Object> getByAccount(@Length(min = 6,max = 20)@NotNull String account){Map<String,Object> res = new HashMap(); res.put("code","200"); res.put("message","ok"); return res; } }
对立异样解决
校验失败会抛出
MethodArgumentNotValidException
或者ConstraintViolationException
异样,咱们须要通过对立异样解决来返回一个更敌对的报错。import org.springframework.http.HttpStatus; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class CommonExceptionHandler {@ExceptionHandler({MethodArgumentNotValidException.class}) @ResponseStatus(HttpStatus.OK) public Map<String,Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {BindingResult bindingResult = ex.getBindingResult(); StringBuilder sb = new StringBuilder("校验失败:"); for (FieldError fieldError : bindingResult.getFieldErrors()) {sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(","); } String msg = sb.toString(); Map<String,Object> err = new HashMap<>(); err.put("code","-200"); err.put("message",msg); return err; } @ExceptionHandler({ConstraintViolationException.class}) @ResponseStatus(HttpStatus.OK) public Map<String,Object> handleConstraintViolationException(ConstraintViolationException ex) {Map<String,Object> err = new HashMap<>(); err.put("code","-200"); err.put("message",ex.getMessage()); return err; } }
进阶应用
分组校验
在理论我的项目中,可能多个办法须要应用同一个 DTO 类来接管参数,而不同办法的校验规定很可能是不一样的。这个时候,简略地在 DTO 类的字段上加束缚注解无奈解决这个问题。因而,spring-validation 反对了分组校验的性能,专门用来解决这类问题。还是下面的例子,比方保留 User 的时候,UserId 是可空的,然而更新 User 的时候,UserId 的值必须 >=10000000000000000L;其它字段的校验规定在两种状况下一样。这个时候应用分组校验的代码示例如下:
-
束缚注解上申明实用的分组信息 groups
public class UserDTO {@Min(value = 10000000000000000L, groups = Update.class) private Long userId; @NotNull(groups = {Save.class, Update.class}) @Length(min = 2, max = 10, groups = {Save.class, Update.class}) private String userName; @NotNull(groups = {Save.class, Update.class}) @Length(min = 6, max = 20, groups = {Save.class, Update.class}) private String account; @NotNull(groups = {Save.class, Update.class}) @Length(min = 6, max = 20, groups = {Save.class, Update.class}) private String password; public interface Save { } public interface Update {}}
@Validated 注解上指定校验分组
@PostMapping("/save") public Map<String,Object> saveUser(@RequestBody @Validated(UserDTO.Save.class) UserDTO userDTO) {Map<String,Object> res = new HashMap(); res.put("code","200"); res.put("message","ok"); return res; } @PostMapping("/update") public Map<String,Object> updateUser(@RequestBody @Validated(UserDTO.Update.class) UserDTO userDTO) {Map<String,Object> res = new HashMap(); res.put("code","200"); res.put("message","ok"); return res; }