<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      SpringBoot-表單驗證-統一異常處理-自定義驗證信息源

      1. 簡介

      我們都知道前臺的驗證只是為了滿足界面的友好性、客戶體驗性等等。但是如果僅靠前端進行數據合法性校驗,是遠遠不夠的。因為非法用戶可能會直接從客戶端獲取到請求地址進行非法請求,所以后臺的校驗是必須的;特別是應用如果不允許輸入空值,對數據的合法行有要求的情況下。

      2. 開擼

      2.1 項目結構

      結構說明:

      ├── java
      │   └── com
      │       └── ldx
      │           └── valid
      │               ├── ValidApplication.java # 啟動類
      │               ├── annotation
      │               │   └── Phone.java # 自定義驗證注解
      │               ├── config
      │               │   └── ValidatorConfig.java # 表單驗證配置類
      │               ├── controller
      │               │   └── SysUserController.java # 用戶管理控制器
      │               ├── exception
      │               │   ├── BusinessException.java # 業務異常類
      │               │   └── GlobalExceptionHandler.java # 統一異常處理類
      │               ├── model
      │               │   ├── SysUser.java # 用戶信息實體
      │               │   └── ValidationInterface.java # 表單驗證的通用分組接口
      │               ├── util
      │               │   └── CommonResult.java # 接口返回封裝類
      │               └── validation
      │                   └── PhoneValidator.java #自定義驗證實現類
      └── resources
          ├── application.yaml # 配置文件
          └── messages
              └── validation
                  └── messages.properties # 自定義驗證信息源
      

      2.1 quick start

      2.1.1 導入依賴

      創建springboot項目導入以下依賴

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.5.3</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
          <groupId>com.ldx</groupId>
          <artifactId>valid</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <name>valid</name>
          <description>表單驗證demo</description>
          <properties>
              <java.version>1.8</java.version>
          </properties>
          <dependencies>
              <!-- web支持 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <!-- 表單驗證 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-validation</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
          </dependencies>
      
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                      <configuration>
                          <excludes>
                              <exclude>
                                  <groupId>org.projectlombok</groupId>
                                  <artifactId>lombok</artifactId>
                              </exclude>
                          </excludes>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
      

      2.1.2 添加配置類

      創建表單驗證配置類,配置快速校驗,不用等全部的參數校驗完,只要有錯,馬上拋出。

      import org.hibernate.validator.HibernateValidator;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
      import javax.validation.Validation;
      import javax.validation.Validator;
      import javax.validation.ValidatorFactory;
      
      /**
       * 配置 Hibernate 參數校驗
       * @author ludangxin
       * @date 2021/8/5
       */
      @Configuration
      public class ValidatorConfig {
          @Bean
          public MethodValidationPostProcessor methodValidationPostProcessor() {
              MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
              //快速校驗,只要有錯馬上返回
              postProcessor.setValidator(validator());
              return postProcessor;
          }
      
          @Bean
          public Validator validator() {
              ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                  .configure()
                  .addProperty("hibernate.validator.fail_fast", "true")
                  .buildValidatorFactory();
              return validatorFactory.getValidator();
          }
      }
      

      2.1.3 添加實體類

      import lombok.*;
      import javax.validation.constraints.*;
      import java.io.Serializable;
      
      /**
       * 用戶信息管理
       * @author ludangxin
       * @date 2021/8/5
       */
      @Data
      public class SysUser  implements Serializable {
          private static final long serialVersionUID = 1L;
          /**
           * 主鍵
           */
          private Long id;
      
          /**
           * 用戶名
           */
          @NotEmpty(message = "用戶名稱不能為空")
          private String username;
      
          /**
           * 密碼
           */
          @Size(min = 6, max = 16, message = "密碼長度必須在{min}-{max}之間")
          private String password = "123456";
      
          /**
           * 郵箱地址
           */
          @Email(message = "郵箱地址不合法")
          @NotEmpty(message = "郵箱不能為空")
          private String email;
      
          /**
           * 電話
           */
          @Size(min = 11, max = 11, message = "手機號不合法")
          @NotEmpty(message = "手機號不能為空")
          private String phone;
      }
      

      2.1.4 接口返回封裝類

      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      /**
       * 操作消息提醒
       * @author ludangxin
       * @date 2021/8/5
       */
      @Data
      @NoArgsConstructor
      public class CommonResult {
          /** 狀態碼 */
          private int code;
      
          /** 返回內容 */
          private String msg;
      
          /** 數據對象 */
          private Object data;
      
          /**
           * 初始化一個新創建的 CommonResult 對象
           * @param type 狀態類型
           * @param msg 返回內容
           */
          public CommonResult(Type type, String msg) {
              this.code = type.value;
              this.msg = msg;
          }
      
          /**
           * 初始化一個新創建的 CommonResult 對象
           * @param type 狀態類型
           * @param msg 返回內容
           * @param data 數據對象
           */
          public CommonResult(Type type, String msg, Object data) {
              this.code = type.value;
              this.msg = msg;
              if (data != null) {
                  this.data = data;
              }
          }
      
          /**
           * 返回成功消息
           * @return 成功消息
           */
          public static CommonResult success() {
              return CommonResult.success("操作成功");
          }
      
          /**
           * 返回成功數據
           * @return 成功消息
           */
          public static CommonResult success(Object data) {
              return CommonResult.success("操作成功", data);
          }
      
          /**
           * 返回成功消息
           * @param msg 返回內容
           * @return 成功消息
           */
          public static CommonResult success(String msg) {
              return CommonResult.success(msg, null);
          }
      
          /**
           * 返回成功消息
           * @param msg 返回內容
           * @param data 數據對象
           * @return 成功消息
           */
          public static CommonResult success(String msg, Object data) {
              return new CommonResult(Type.SUCCESS, msg, data);
          }
      
          /**
           * 返回警告消息
           * @param msg 返回內容
           * @return 警告消息
           */
          public static CommonResult warn(String msg) {
              return CommonResult.warn(msg, null);
          }
      
          /**
           * 返回警告消息
           * @param msg 返回內容
           * @param data 數據對象
           * @return 警告消息
           */
          public static CommonResult warn(String msg, Object data) {
              return new CommonResult(Type.WARN, msg, data);
          }
      
          /**
           * 返回錯誤消息
           * @return 錯誤信息
           */
          public static CommonResult error() {
              return CommonResult.error("操作失敗");
          }
      
          /**
           * 返回錯誤消息
           * @param msg 返回內容
           * @return 錯誤消息
           */
          public static CommonResult error(String msg) {
              return CommonResult.error(msg, null);
          }
      
          /**
           * 返回錯誤消息
           * @param msg 返回內容
           * @param data 數據對象
           * @return 錯誤消息
           */
          public static CommonResult error(String msg, Object data) {
              return new CommonResult(Type.ERROR, msg, data);
          }
      
          /**
           * 狀態類型
           */
          public enum Type {
              /** 成功 */
              SUCCESS(200),
              /** 警告 */
              WARN(301),
              /** 錯誤 */
              ERROR(500);
              private final int value;
      
              Type(int value){
                  this.value = value;
              }
      
              public int value() {
                  return this.value;
              }
          }
      }
      

      2.1.5 控制器

      使用@Validated注解標識需要驗證的類,使用BindingResult類接收錯誤信息

      import com.ldx.valid.exception.BusinessException;
      import com.ldx.valid.model.SysUser;
      import com.ldx.valid.model.ValidationInterface;
      import com.ldx.valid.util.CommonResult;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.validation.BindingResult;
      import org.springframework.validation.FieldError;
      import org.springframework.validation.annotation.Validated;
      import org.springframework.web.bind.annotation.*;
      import javax.validation.constraints.NotEmpty;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Objects;
      import java.util.stream.Collectors;
      
      /**
       * 用戶管理控制器
       *
       * @author ludangxin
       * @date 2021/8/5
       */
      @Slf4j
      @RestController
      @RequestMapping("sys/user")
      public class SysUserController {
         private static final List<SysUser> USERS = new ArrayList<>();
      
         // 數據初始化
         static {
            SysUser user = new SysUser();
            user.setId(1L);
            user.setUsername("zhangsan");
            user.setPhone("13566666666");
            user.setEmail("example@qq.com");
            USERS.add(user);
            SysUser user1 = new SysUser();
            user1.setId(2L);
            user1.setUsername("lisi");
            user1.setPhone("13588888888");
            user1.setEmail("example1@qq.com");
            USERS.add(user1);
         }
      
         /**
          * 新增用戶信息
          * @param sysUser 用戶信息
          * @return 成功標識
          */
         @PostMapping
         public CommonResult add(@Validated @RequestBody SysUser sysUser, BindingResult result) {
            FieldError fieldError = result.getFieldError();
      
            if(Objects.nonNull(fieldError)) {
               String field = fieldError.getField();
               Object rejectedValue = fieldError.getRejectedValue();
               String msg = "[" + fieldError.getDefaultMessage() + "]";
               log.error("{}:字段=={}\t值=={}", msg, field, rejectedValue);
               return CommonResult.error(msg);
            }
      
            USERS.add(sysUser);
            return CommonResult.success("新增成功");
         }
      }
      

      2.1.5 啟動測試

      新增時,故意將email信息填錯,測試結果符合預期。

      log日志:

      [nio-8080-exec-9] c.l.valid.controller.SysUserController   : [郵箱地址不合法]:字段==email	值==123
      

      3. 分組校驗

      groups是用來干什么的?

      因為一個實體不可能只干一種操作,一個實體必然存在增刪改查操作,那么問題就來了
      如果我要根據id進行更新操作,那么id肯定不能為空
      這時候我還要進行新增操作,因為id是新增數據庫操作才產生的,接受數據的時候我肯定是沒有id的
      所以就產生矛盾了
      那么groups這個參數就起作用了,它可以表示我這個注解屬于哪個組,這樣就解決這個尷尬的問題了。
      

      當在controller中校驗表單數據時,如果使用了groups,那么沒有在這個分組下的屬性是不會校驗的

      3.1 添加分組接口

      /**
       * 用于表單驗證的通用分組接口
       * @author ludangxin
       * @date 2021/8/5
       */
      public interface ValidationInterface {
          /**
           * 新增分組
           */
          interface add{}
      
          /**
           * 刪除分組
           */
          interface delete{}
      
          /**
           * 查詢分組
           */
          interface select{}
      
          /**
           * 更新分組
           */
          interface update{}
      }
      

      如果還有其它特殊的分組要求 直接在DO中創建interface即可
      例:如果還有個需要驗證username 和 password(只有這兩個參數) 的 select操作
      直接在SysUser中創建UsernamePasswordValidView 的接口即可

      3.2 修改實體類

      將屬性進行分組

      import lombok.Data;
      import javax.validation.constraints.Email;
      import javax.validation.constraints.NotEmpty;
      import javax.validation.constraints.NotNull;
      import javax.validation.constraints.Size;
      import java.io.Serializable;
      
      /**
       * 用戶信息管理
       * @author ludangxin
       * @date 2021/8/5
       */
      @Data
      public class SysUser implements Serializable {
          private static final long serialVersionUID = 1L;
          /**
           * 主鍵
           */
          @NotNull(message = "id不能為空", groups = {ValidationInterface.update.class})
          private Long id;
      
          /**
           * 用戶名
           */
          @NotEmpty(message = "用戶名稱不能為空", groups = {
                    ValidationInterface.update.class, 
                    ValidationInterface.add.class})
          private String username;
      
          /**
           * 密碼
           */
          @Size(min = 6, max = 16, message = "密碼長度必須在{min}-{max}之間", groups = {
                ValidationInterface.update.class, 
                ValidationInterface.add.class})
          private String password = "123456";
      
          /**
           * 郵箱地址
           */
          @Email(message = "郵箱地址不合法", groups = {
                 ValidationInterface.update.class, 
                 ValidationInterface.add.class, 
                 ValidationInterface.select.class})
          @NotEmpty(message = "郵箱不能為空", groups = ValidationInterface.add.class)
          private String email;
      
          /**
           * 電話
           */
          @Size(min = 11, max = 11, message = "手機號不合法", groups = {
                ValidationInterface.update.class,
                ValidationInterface.add.class,
                ValidationInterface.select.class})
          @NotEmpty(message = "手機號不能為空",groups = {ValidationInterface.add.class})
          private String phone;
      }
      
      

      3.3 修改控制器

      添加操作方法,并且方法形參上指定驗證的分組

      import com.ldx.valid.exception.BusinessException;
      import com.ldx.valid.model.SysUser;
      import com.ldx.valid.model.ValidationInterface;
      import com.ldx.valid.util.CommonResult;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.validation.BindingResult;
      import org.springframework.validation.FieldError;
      import org.springframework.validation.annotation.Validated;
      import org.springframework.web.bind.annotation.*;
      import javax.validation.constraints.NotEmpty;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Objects;
      import java.util.stream.Collectors;
      
      /**
       * 用戶管理控制器
       * @author ludangxin
       * @date 2021/8/5
       */
      @Slf4j
      @RestController
      @RequestMapping("sys/user")
      public class SysUserController {
         private static final List<SysUser> USERS = new ArrayList<>();
      
         // 數據初始化
         static {
            SysUser user = new SysUser();
            user.setId(1L);
            user.setUsername("zhangsan");
            user.setPhone("13566666666");
            user.setEmail("example@qq.com");
            USERS.add(user);
            SysUser user1 = new SysUser();
            user1.setId(2L);
            user1.setUsername("lisi");
            user1.setPhone("13588888888");
            user1.setEmail("example1@qq.com");
            USERS.add(user1);
         }
      
         /**
          * 根據手機號或郵箱查詢用戶信息
          * @param sysUser 查詢條件
          * @return 用戶list
          */
         @GetMapping
         public CommonResult queryList(@Validated(value = ValidationInterface.select.class) SysUser sysUser,
                                       BindingResult result)
         {
            FieldError fieldError = result.getFieldError();
      
            if(Objects.nonNull(fieldError)) {
               return CommonResult.error(getErrorMsg(fieldError));
            }
      
            String phone = sysUser.getPhone();
            String email = sysUser.getEmail();
      
            if(phone == null && email == null) {
               return CommonResult.success(USERS);
            }
      
            List<SysUser> queryResult = USERS.stream()
                  .filter(obj -> obj.getPhone().equals(phone) || obj.getEmail().equals(email))
                  .collect(Collectors.toList());
            return CommonResult.success(queryResult);
         }
      
         /**
          * 新增用戶信息
          * @param sysUser 用戶信息
          * @return 成功標識
          */
         @PostMapping
         public CommonResult add(@Validated(value = ValidationInterface.add.class)
                                 @RequestBody SysUser sysUser,
                                 BindingResult result)
         {
            FieldError fieldError = result.getFieldError();
      
            if(Objects.nonNull(fieldError)) {
               return CommonResult.error(getErrorMsg(fieldError));
            }
           
            Long id = (long) (USERS.size() + 1);
            sysUser.setId(id);
            USERS.add(sysUser);
            return CommonResult.success("新增成功");
         }
      
         /**
          * 根據Id更新用戶信息
          * @param sysUser 用戶信息
          * @return 成功標識
          */
         @PutMapping("{id}")
         public CommonResult updateById(@PathVariable("id") Long id,
                                        @Validated(value = ValidationInterface.update.class)
                                        @RequestBody SysUser sysUser,
                                        BindingResult result)
         {
            FieldError fieldError = result.getFieldError();
      
            if(Objects.nonNull(fieldError)) {
               return CommonResult.error(getErrorMsg(fieldError));
            }
      
            for(int i = 0; i < USERS.size(); i++) {
               if(USERS.get(i).getId().equals(id)) {
                  USERS.set(i,sysUser);
               }
            }
            return CommonResult.success("更新成功");
         }
      
         /**
          * 根據Id刪除用戶信息
          * @param id 主鍵
          * @return 成功標識
          */
         @DeleteMapping("{id}")
         public CommonResult deleteById(@PathVariable Long id) {
            USERS.removeIf(obj -> obj.getId().equals(id));
            return CommonResult.success("刪除成功");
         }
      
         /**
          * 獲取表單驗證錯誤msg
          * @param fieldError 報錯字段
          * @return msg
          */
         public String getErrorMsg(FieldError fieldError) {
            String field = fieldError.getField();
            Object rejectedValue = fieldError.getRejectedValue();
            String msg = "[" + fieldError.getDefaultMessage() + "]";
            log.error("{}:字段=={}\t值=={}", msg, field, rejectedValue);
            return msg;
         }
      }
      

      3.4 啟動測試

      查詢:

      ? 輸入不合法手機號

      新增:

      ? 正常情況

      ? 去掉郵箱

      修改:

      ? 去掉id

      刪除:

      4. 自定義驗證

      很多時候框架提供的功能并不能滿足我們的業務場景,這時我們需要自定義一些驗證規則來完成驗證。

      4.1 添加注解

      import com.ldx.valid.validation.PhoneValidator;
      import javax.validation.Constraint;
      import javax.validation.Payload;
      import java.lang.annotation.Documented;
      import java.lang.annotation.Retention;
      import java.lang.annotation.Target;
      import static java.lang.annotation.ElementType.*;
      import static java.lang.annotation.ElementType.TYPE_USE;
      import static java.lang.annotation.RetentionPolicy.RUNTIME;
      
      /**
       * 驗證手機號是否合法
       * @author ludangxin
       * @date 2021/8/7
       */
      @Documented
      @Constraint(validatedBy = {PhoneValidator.class})
      @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
      @Retention(RUNTIME)
      public @interface Phone {
         //默認錯誤消息
         String message() default "不是一個合法的手機號";
      
         //分組
         Class<?>[] groups() default {};
      
         //載荷 將某些元數據信息與給定的注解聲明相關聯的方法
         Class<? extends Payload>[] payload() default {};
      
         //指定多個時使用
         @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
         @Retention(RUNTIME)
         @Documented
         @interface List {
            Phone[] value();
         }
      }
      

      4.2 編寫驗證邏輯

      import javax.validation.ConstraintValidator;
      import com.ldx.valid.annotation.Phone;
      import javax.validation.ConstraintValidatorContext;
      import java.util.Objects;
      import java.util.regex.Pattern;
      
      /**
       * 手機號校驗器
       * @author ludangxin
       * @date 2021/8/7
       */
      public class PhoneValidator implements ConstraintValidator<Phone, String> {
      
         /**
          * 手機號正則表達式
          */
         private static final String REGEXP_PHONE = "^1[3456789]\\d{9}$";
      
         @Override
         public boolean isValid(String value, ConstraintValidatorContext context) {
            if(Objects.isNull(value)) {
               return true;
            }
      
            return Pattern.matches(REGEXP_PHONE, value);
         }
      }
      

      4.3 修改實體

      SysUser.phone 屬性添加注解@Phone

      @Phone(groups = {
                      ValidationInterface.update.class,
                      ValidationInterface.add.class,
                      ValidationInterface.select.class})
      @NotEmpty(message = "手機號不能為空", groups = {ValidationInterface.add.class})
      private String phone;
      

      4.4 啟動測試

      輸入錯誤的手機號進行測試

      4.5 @Pattern

      當然validation也提供了基于正則匹配的注解@Pattern

      @Pattern(message = "手機號不合法", regexp = "^1[3456789]\\d{9}$", groups = {ValidationInterface.add.class})
      @NotEmpty(message = "手機號不能為空", groups = {ValidationInterface.add.class})
      private String phone;
      

      注意是javax.validation.constraints包下的

      測試

      5. 調用過程驗證

      有的時候我們在參數傳輸過程中需要對傳入的對象做參數驗證,但是上面介紹的都是對參數綁定時的驗證,那能不能使用validation進行驗證呢?

      答案肯定是可以的。

      5.1 使用 spring bean

      5.1.1 注入validator

      bean validator 是我們在config文件中定義的bean,如果使用了springboot默認的配置ValidationAutoConfiguration::defaultValidator(),直接注入bean name defaultValidator即可

      @Resource(name = "validator")
      javax.validation.Validator validator;
      

      5.1.2 定義驗證方法

      public void validateParams(SysUser user) {
         validator.validate(user, ValidationInterface.add.class).stream().findFirst().ifPresent(obj -> {
            String objName = obj.getRootBean().getClass().getSimpleName();
            String fieldName = obj.getPropertyPath().toString();
            Object val = obj.getInvalidValue();
            String msg = obj.getMessage();
            String errMsg = MessageFormat.format(msg + ":對象:{0},字段:{1},值:{2}", objName, fieldName, val);
            throw new RuntimeException(errMsg);
         });
      

      5.1.2 啟動驗證

      調用新增方法,通過新增方法調用validateParams方法

      報錯日志如下

      java.lang.RuntimeException: 手機號不合法:對象:SysUser,字段:phone,值:135999
      

      5.2 非spring環境驗證

      5.2.1 定義驗證方法

      直接獲取默認的工廠類,然后獲取驗證對象進行驗證

      public static void main(String[] args) {
         ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
         Validator validator = vf.getValidator();
         SysUser user = new SysUser();
         user.setId(1L);
         user.setUsername("zhangsan");
         user.setPhone("1356666");
         user.setEmail("example@qq.com");
         validator.validate(user, ValidationInterface.add.class).stream().findFirst().ifPresent(obj -> {
            String objName = obj.getRootBean().getClass().getSimpleName();
            String fieldName = obj.getPropertyPath().toString();
            Object val = obj.getInvalidValue();
            String msg = obj.getMessage();
            String errMsg = MessageFormat.format(msg + ":對象:{0},字段:{1},值:{2}", objName, fieldName, val);
            throw new RuntimeException(errMsg);
         });
      }
      

      5.2.2 啟動驗證

      報錯信息如下,符合預期

      Exception in thread "main" java.lang.RuntimeException: 手機號不合法:對象:SysUser,字段:phone,值:1356666
      	at com.ldx.valid.controller.SysUserController.lambda$main$4(SysUserController.java:215)
      	at java.util.Optional.ifPresent(Optional.java:159)
      	at com.ldx.valid.controller.SysUserController.main(SysUserController.java:209)
      

      6. 方法參數驗證

      有的時候我們想在方法上直接進行參數驗證,步驟如下

      6.1 修改控制器

      直接在類上添加注解@Validated,并在方法上直接進行驗證

      @Slf4j
      @Validated
      @RestController
      @RequestMapping("sys/user")
      public class SysUserController {
        ... 省略代碼
          /**
           * 根據手機號和郵箱查詢用戶信息
           * @param phone 手機號
           * @return 用戶list
           */
          @GetMapping("selectByPhone")
          public CommonResult queryByPhone(@NotEmpty(message = "手機號不能為空") String phone) {
             List<SysUser> queryResult = USERS.stream()
                   .filter(obj -> obj.getPhone().equals(phone))
                   .collect(Collectors.toList());
             return CommonResult.success(queryResult);
          }
      }
      

      6.2 啟動驗證

      不給phone字段賦值,操作結果符合預期

      錯誤日志:

      javax.validation.ConstraintViolationException: queryByPhone.phone: 手機號不能為空
      

      7. 統一異常處理

      在上面的參數驗證中,驗證的錯誤信息是通過BindingResult result參數進行接收的,在每個方法中異常處理如出一轍,特別麻煩。甚至在step 5,6都是直接將異常的堆棧信息返回給前端,這對于用來說是非常不友好的。而且有的情況下需要我們主動拋出業務異常,比方用戶不能直接刪除已綁定用戶的角色。

      所以,開擼。

      7.1 創建業務異常類

      /**
       * 業務異常
       * @author ludangxin
       * @date 2021/8/5
       */
      public class BusinessException extends RuntimeException {
          private static final long serialVersionUID = 1L;
      
          protected final String message;
      
          public BusinessException(String message) {
              this.message = message;
          }
      
          public BusinessException(String message, Throwable e) {
              super(message, e);
              this.message = message;
          }
      
          @Override
          public String getMessage() {
              return message;
          }
      }
      

      7.2 創建全局異常處理器

      import com.ldx.valid.util.CommonResult;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.validation.BindException;
      import org.springframework.validation.BindingResult;
      import org.springframework.validation.FieldError;
      import org.springframework.web.HttpRequestMethodNotSupportedException;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.RestControllerAdvice;
      import javax.servlet.http.HttpServletRequest;
      import javax.validation.ConstraintViolation;
      import javax.validation.ConstraintViolationException;
      import javax.validation.ValidationException;
      import java.util.Set;
      
      /**
       * 全局異常處理器
       *
       * @author ludangxin
       * @date 2021/8/5
       */
      @Slf4j
      @RestControllerAdvice
      public class GlobalExceptionHandler {
          /**
           * 參數綁定異常類 用于表單驗證時拋出的異常處理
           */
          @ExceptionHandler(BindException.class)
          public CommonResult validatedBindException(BindException e){
              log.error(e.getMessage(), e);
              BindingResult bindingResult = e.getBindingResult();
              FieldError fieldError = e.getFieldError();
              String message = "[" + e.getAllErrors().get(0).getDefaultMessage() + "]";
              return CommonResult.error(message);
          }
      
          /**
           * 用于方法形參中參數校驗時拋出的異常處理
           * @param e
           * @return
           */
          @ExceptionHandler(ConstraintViolationException.class)
          public CommonResult handle(ValidationException e) {
              log.error(e.getMessage(), e);
              String errorInfo = "";
              if(e instanceof ConstraintViolationException){
                  ConstraintViolationException exs = (ConstraintViolationException) e;
                  Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
                  for (ConstraintViolation<?> item : violations) {
                      errorInfo = errorInfo + "[" + item.getMessage() + "]";
                  }
              }
              return CommonResult.error(errorInfo);
          }
      
          /**
           * 請求方式不支持
           */
          @ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
          public CommonResult handleException(HttpRequestMethodNotSupportedException e){
              log.error(e.getMessage(), e);
              return CommonResult.error("不支持' " + e.getMethod() + "'請求");
          }
      
          /**
           * 攔截未知的運行時異常
           */
          @ExceptionHandler(RuntimeException.class)
          public CommonResult notFount(RuntimeException e) {
              log.error("運行時異常:", e);
              return CommonResult.error("運行時異常:" + e.getMessage());
          }
      
          /**
           * 系統異常
           */
          @ExceptionHandler(Exception.class)
          public CommonResult handleException(Exception e) {
              log.error(e.getMessage(), e);
              return CommonResult.error("服務器錯誤,請聯系管理員");
          }
      
          /**
           * 業務異常
           */
          @ExceptionHandler(BusinessException.class)
          public CommonResult businessException(HttpServletRequest request, BusinessException e) {
              log.error(e.getMessage());
              return CommonResult.error(e.getMessage());
          }
      }
      

      7.3 修改控制器

      刪除方法中的BindingResult result參數,將錯誤直接拋給統一異常處理類去解決即可。

      import com.ldx.valid.exception.BusinessException;
      import com.ldx.valid.model.SysUser;
      import com.ldx.valid.model.ValidationInterface;
      import com.ldx.valid.service.UserService;
      import com.ldx.valid.util.CommonResult;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.validation.FieldError;
      import org.springframework.validation.annotation.Validated;
      import org.springframework.web.bind.annotation.*;
      import javax.annotation.Resource;
      import javax.validation.*;
      import javax.validation.constraints.NotEmpty;
      import java.text.MessageFormat;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.stream.Collectors;
      
      /**
       * 用戶管理控制器
       * @author ludangxin
       * @date 2021/8/5
       */
      @Slf4j
      @Validated
      @RestController
      @RequestMapping("sys/user")
      public class SysUserController {
         private static final List<SysUser> USERS = new ArrayList<>();
      
         // 數據初始化
         static {
            SysUser user = new SysUser();
            user.setId(1L);
            user.setUsername("zhangsan");
            user.setPhone("13566666666");
            user.setEmail("example@qq.com");
            USERS.add(user);
            SysUser user1 = new SysUser();
            user1.setId(2L);
            user1.setUsername("lisi");
            user1.setPhone("13588888888");
            user1.setEmail("example1@qq.com");
            USERS.add(user1);
         }
      
         /**
          * 根據手機號或郵箱查詢用戶信息
          * @param sysUser 查詢條件
          * @return 用戶list
          */
         @GetMapping
         public CommonResult queryList(@Validated(value = ValidationInterface.select.class) SysUser sysUser) {
            String phone = sysUser.getPhone();
            String email = sysUser.getEmail();
      
            if(phone == null && email == null) {
               return CommonResult.success(USERS);
            }
      
            List<SysUser> queryResult = USERS.stream()
                  .filter(obj -> obj.getPhone().equals(phone) || obj.getEmail().equals(email))
                  .collect(Collectors.toList());
            return CommonResult.success(queryResult);
         }
      
         /**
          * 根據手機號和郵箱查詢用戶信息
          * @param phone 手機號
          * @return 用戶list
          */
         @GetMapping("selectByPhone")
         public CommonResult queryByPhone(@NotEmpty(message = "手機號不能為空") String phone) {
            List<SysUser> queryResult = USERS.stream()
                  .filter(obj -> obj.getPhone().equals(phone))
                  .collect(Collectors.toList());
            return CommonResult.success(queryResult);
         }
      
         /**
          * 新增用戶信息
          * @param sysUser 用戶信息
          * @return 成功標識
          */
         @PostMapping
         public CommonResult add(@Validated(value = ValidationInterface.add.class) @RequestBody SysUser sysUser) {
            Long id = (long) (USERS.size() + 1);
            sysUser.setId(id);
            USERS.add(sysUser);
            return CommonResult.success("新增成功");
         }
      
         /**
          * 根據Id更新用戶信息
          * @param sysUser 用戶信息
          * @return 成功標識
          */
         @PutMapping("{id}")
         public CommonResult updateById(@PathVariable("id") Long id,
                                        @Validated(value = ValidationInterface.update.class)
                                        @RequestBody SysUser sysUser)
         {
            for(int i = 0; i < USERS.size(); i++) {
               if(USERS.get(i).getId().equals(id)) {
                  USERS.set(i,sysUser);
               }
            }
            return CommonResult.success("更新成功");
         }
      
         /**
          * 根據Id刪除用戶信息
          * @param id 主鍵
          * @return 成功標識
          */
         @DeleteMapping("{id}")
         public CommonResult deleteById(@PathVariable Long id) {
            USERS.removeIf(obj -> obj.getId().equals(id));
            return CommonResult.success("刪除成功");
         }
      
         /**
          * 測試業務異常
          */
         @GetMapping("testException")
         public CommonResult testException(String name) {
            if(!"張三".equals(name)){
               throw new BusinessException("只有張三才可以訪問");
            }
            return CommonResult.success();
         }
      }
      

      7.4 啟動測試

      查詢:

      ? 輸出錯誤的郵箱

      根據手機號查詢:

      ? 輸入空值手機號

      新增:

      ? 輸入錯誤的手機號

      測試主動拋出業務異常:

      8. 自定義驗證信息源

      8.1 修改配置文件

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.support.ReloadableResourceBundleMessageSource;
      import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
      import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
      import javax.validation.Validator;
      import java.util.Properties;
      
      /**
       * 配置 Hibernate 參數校驗
       * @author ludangxin
       * @date 2021/8/5
       */
      @Configuration
      public class ValidatorConfig {
          @Bean
          public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
              MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
              postProcessor.setValidator(validator);
              return postProcessor;
          }
      
          /**
           * 實體類字段校驗國際化引入
           */
          @Bean
          public Validator validator() {
              LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
              // 設置messages資源信息
              ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
              // 多個用逗號分割
              messageSource.setBasenames("classpath:/messages/validation/messages");
              // 設置字符集編碼
              messageSource.setDefaultEncoding("UTF-8");
              validator.setValidationMessageSource(messageSource);
              // 設置驗證相關參數
              Properties properties = new Properties();
              // 快速失敗,只要有錯馬上返回
              properties.setProperty("hibernate.validator.fail_fast", "true");
              validator.setValidationProperties(properties);
              return validator;
          }
      }
      

      8.2 添加信息源文件

      ├───resources
          └── messages
              └── validation
                  └── messages.properties
      
      # messages.properties
      name.not.empty=用戶名不能為空
      email.not.valid=${validatedValue}是郵箱地址?
      email.not.empty=郵箱不能為空
      phone.not.valid=${validatedValue}是手機號?
      phone.not.empty=手機號不能為空
      password.size.valid=密碼長度必須在{min}-{max}之間
      id.not.empty=主鍵不能為空
      

      8.3 修改實體類

      import com.ldx.valid.annotation.Phone;
      import lombok.Data;
      import org.hibernate.validator.constraints.Range;
      import javax.validation.constraints.*;
      import java.io.Serializable;
      
      /**
       * 用戶信息管理
       * @author ludangxin
       * @date 2021/8/5
       */
      @Data
      public class SysUser implements Serializable {
          private static final long serialVersionUID = 1L;
          /**
           * 主鍵
           */
          @NotNull(message = "{id.not.empty}", groups = {ValidationInterface.update.class})
          private Long id;
      
          /**
           * 用戶名
           */
          @NotEmpty(message = "{name.not.empty}", groups = {
                    ValidationInterface.update.class,
                    ValidationInterface.add.class})
          private String username;
      
          /**
           * 密碼
           */
          @Size(min = 6, max = 16, message = "{password.size.valid}", groups = {
                ValidationInterface.update.class,
                ValidationInterface.add.class})
          private String password = "123456";
      
          /**
           * 郵箱地址
           */
          @Email(message = "{email.not.valid}",
                 groups = {
                 ValidationInterface.update.class,
                 ValidationInterface.add.class,
                 ValidationInterface.select.class})
          @NotEmpty(message = "{email.not.empty}", groups = ValidationInterface.add.class)
          private String email;
      
          /**
           * 電話
           */
          @Pattern(message = "{phone.not.valid}", regexp = "^1[3456789]\\d{9}$", 
                   groups = {ValidationInterface.add.class})
          @NotEmpty(message = "{phone.not.empty}", groups = {ValidationInterface.add.class})
          private String phone;
      }
      

      8.4 啟動測試

      ? 輸入錯誤的郵箱地址測試:

      9. 預置注解清單

      注解 說明
      @Null 限制只能為null
      @NotNull 限制必須不為null
      @AssertFalse 限制必須為false
      @AssertTrue 限制必須為true
      @DecimalMax(value) 限制必須為一個不大于指定值的數字
      @DecimalMin(value) 限制必須為一個不小于指定值的數字
      @Digits(integer,fraction) 限制必須為一個小數,且整數部分的位數不能超過integer,小數部分的位數不能超過fraction
      @Future 限制必須是一個將來的日期
      @Max(value) 限制必須為一個不大于指定值的數字
      @Min(value) 限制必須為一個不小于指定值的數字
      @Past 限制必須是一個過去的日期
      @Pattern(value) 限制必須符合指定的正則表達式
      @Size(max,min) 限制字符長度必須在min到max之間
      @Past 驗證注解的元素值(日期類型)比當前時間早
      @NotEmpty 驗證注解的元素值不為null且不為空(字符串長度不為0、集合大小不為0)
      @NotBlank 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同于@NotEmpty,@NotBlank
      @Email 驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email格式
      posted @ 2021-08-08 01:25  張鐵牛  閱讀(1202)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 亚洲AV日韩AV激情亚洲| av鲁丝一区鲁丝二区鲁丝三区| 国产精品国三级国产av| 亚洲综合无码日韩国产加勒比| 99亚洲男女激情在线观看| 午夜福利精品国产二区| 2021亚洲va在线va天堂va国产| 综合色一色综合久久网| 黑人猛精品一区二区三区| 最新日韩精品视频在线| 韩国三级+mp4| 国产精品欧美福利久久| 国产综合av一区二区三区| 中文字幕日韩精品有码| 夜夜添狠狠添高潮出水| 免费无码午夜理论电影| 日韩av一区二区三区在线| 婷婷99视频精品全部在线观看 | 华人在线亚洲欧美精品| 高清有码国产一区二区| 伊人久久大香线蕉av色婷婷色| 国产一区二区波多野结衣| 亚洲熟妇色xxxxx欧美老妇| 欧美成年性h版影视中文字幕| 日韩精品一区二区三区激情视频| 色吊丝中文字幕在线观看| 色护士极品影院| 亚洲av不卡电影在线网址最新| 精品国产乱弄九九99久久| 唐人社导航福利精品| 国产精品一线天在线播放| 亚洲欧美日韩成人综合一区| A级毛片免费完整视频| 色综合视频一区二区三区| 精品久久8x国产免费观看| 日本高清久久一区二区三区| 中文字幕av国产精品| 久久亚洲日韩精品一区二区三区| 九九热爱视频精品视频| 91精品91久久久久久| 久久久无码精品亚洲日韩蜜臀浪潮 |