关于java:SpringBoot的参数校验之Spring-Validation

前言

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服务里通常会通过以下两种模式进行传参:

  1. POSTPUT申请,应用requestBody传递参数;
  2. 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;
    }

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理