1. 程式人生 > >【註解】用註解來優雅校驗

【註解】用註解來優雅校驗

前言
現在公司中 Springboot 框架中欄位校驗使用Assert


但遇到要校驗多個欄位,一個欄位多重限制,那麼這時候Assert便變得很笨重,且不優雅


這時候 JSR 303JSR 349 閃亮登場 (~ ̄▽ ̄)~

一、最佳實踐

配置
版本:Spring boot 2.0


如果開發web,spring-boot-starter-web包中就有 hibernate-validate包。
當然,不匯入這個包,匯入 spring-boot-starter-validation 也可以

(1)整合配置

為了能與原先web配置(攔截器、過濾器等)統一管理,希望他們能在同一檔案或同一資料夾下。

  1. 為了能使用 Spring MVC 部分特性,可能會選擇使用繼承(extend)WebMvcConfigurerAdapter,然而這個已經過時了 @Deprecated

  2. 突然發現還有個 WebMvcConfigurationSupport ,也要extend,欣喜若狂。然而發現繼承這個會將配置完全交給 Spring MVC,這樣會與自定義的配置和Spring boot的某些配置相沖突。

  3. 最後種,引用(implementsWebMvcConfigurer,解決了我的需求。同時我發現
    public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer

o(╥﹏╥)o

  1. 使用JSR 303校驗,我可能需要:
  • 快速失敗功能(即:遇到錯誤直接返回,而不是把所有的都校驗一般,這樣減少CPU資源使用)
  • 我想讓其在方法之間傳遞引數時候也能生效

這樣我的配置大概形成了:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(timeInterceptor
()).addPathPatterns("/**"); } @Bean public FilterRegistrationBean timeFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); return registrationBean; } @Bean public TimeInterceptor timeInterceptor() { return new TimeInterceptor(); } // 設定校驗器 @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); processor.setValidator(validator()); return processor; } // 設定模式 @Bean public Validator validator(){ // failFast(true)啟動快速失敗 ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class). configure(). failFast(true). buildValidatorFactory(); return validatorFactory.getValidator(); } }


(2)異常捕獲

@ControllerAdvice
public class RestExceptionHandler {

    /**
     * 校驗JSR-303
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ServerResponse validate(ConstraintViolationException exception) {
        log.error(exception.getMessage());
        exception.printStackTrace();
        Iterator iter = exception.getConstraintViolations().iterator();
        ConstraintViolation constraintViolation = (ConstraintViolation) iter.next();
        
        // ServerResponse.createByErrorCodeMessage 為自己配置的返回包裝體
        return ServerResponse.createByErrorCodeMessage(Const.HttpStatusCode.BAD_REQUEST.getCode(),
                constraintViolation.getConstraintDescriptor().getMessageTemplate());
    }

}


(3)常用搭配

  1. 字串 String
    @Length@Size 均可
@NotBlank(message = "請填寫專案名稱")
@Length(min = 3, max = 50, message = "專案名稱長度大於3且小於50")
private String projectName;
  1. 整型 Integer
    @Range@Max @Min 都可
@NotNull(message = "請選擇專案背景")
@Digits(integer = 1,  fraction = 0, message = "請選擇專案背景")
@Range(min = 0, max = 3, message = "請重新選擇專案背景")
private Integer projectType;
  1. 用於小數 BigDecimal
@NotNull(message = "請填寫融資金額")
@Digits(integer = 10, fraction = 5, message = "請填寫正確的金額")
private BigDecimal amount;
  1. Boolean 校驗
@AssertTrue     驗證 Boolean 物件是否為 true  
@AssertFalse    驗證 Boolean 物件是否為 false  
  1. 物件資料校驗
    @Valid可校驗物件裡面的屬性,同時可遞迴校驗
@Valid
Person person;


二、實際運用

(1)Controller 層使用

controller層 校驗物件引數

@RequestMapping(value = "/user")
@RestController
public class UserController {
    
    @Autowired
    private UserService userService;

    @PostMapping
    public User getUser(@Valid User user, BindingResult errors) {
        
        if (errors.hasErrors()) {
            errors.getAllErrors().stream().forEach(
                error -> System.out.println(error.getDefaultMessage())
            );
        }

        user.setUsername("Donald");
        return user;
    }
    
}

User物件

@Data
public class User {
    
    @NotBlank(message = "使用者類中使用者名稱不能為空")
    private String username;

    @NotBlank(message = "密碼不能為空")
    private String password;

    @NotNull(message = "生日不能為空")
    private Date birthday;

}


(2) Service 層使用

  1. @Validated 是 Spring boot 對 @Valid 的增強

UserService.java

@Validated
public interface UserService {

    public List<User> getUserListByName(@NotBlank(message = "使用者名稱不能為空")
                                        @Length(min = 3, max = 10, message = "使用者名稱長度大於3小於10")String username,
                                        @Valid User user);

}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {
     @Override
    public List<User> getUserListByName(String username, User _user) {
    
        List<User> users = new ArrayList<>();
        User user = new User();
        users.add(user);
        return users;
        
    }

}


普通介面傳遞