@Valid 和 @Validated 注解區別以及自定義參數驗證器的使用方法以及原理解析以及自定義參數解析器


  在Web項目中經常需要驗證前台的參數,比如驗證param != null 或者驗證param 的長度、集合的大小等等。一種辦法就是手動驗證,那就是寫大量的if代碼塊,另一種就是使用現成的validation。

  @Valid 注解位於包 javax.validation; @Validated 注解位於包org.springframework.validation.annotation, 是Spring 提供的注解。

  @Validated是@Valid 的一次封裝,是Spring提供的校驗機制使用。@Valid不提供分組功能,而@Validated 提供分組的功能。

1. 引入相關依賴

        <!-- validate 相關注解 -->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.1.Final</version>
        </dependency>

2. 使用

1. 不帶分組的使用:

接收前端參數的Bean:

package com.xm.ggn.test;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

@Data
public class User2 implements Serializable {

    @NotNull(message = "username 不能為空")
    @Length(min = 2, max = 20, message = "username 長度必須在{min}到{max}之間")
    private String username;

    @NotNull(message = "age 不能為空")
    @Range(min = 18, max = 25, message = "年齡必須在{min}-{max}之間")
    private Integer age;

    /**
     * 愛好
     */
    @NotEmpty(message = "愛好不能為空")
    private List<String> hobbies;

    private String fullname;
}

兩個測試Controller:

    @PostMapping("/user/add2")
    public User2 addUser2(@RequestBody @Valid User2 user) {
        System.out.println(user);
        return user;
    }

    @PostMapping("/user/add3")
    public User2 addUser3(@RequestBody @Validated User2 user) {
        System.out.println(user);
        return user;
    }

全局異常處理器: (捕捉到上面接口參數驗證失敗拋出的異常,然后提取到錯誤信息返回給前端)

package com.xm.ggn.exception;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.val;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.xm.ggn.utils.JSONResultUtil;
import com.xm.ggn.utils.constant.ErrorCodeDefine;

import lombok.extern.slf4j.Slf4j;

/**
 * 全局異常處理器
 * 
 * @author Administrator
 *
 */
@RestControllerAdvice
@Slf4j
public class BXExceptionHandler {

    @ExceptionHandler(value = Throwable.class)
    public JSONResultUtil<Object> errorHandler(HttpServletRequest reqest, HttpServletResponse response, Exception e) {
        log.error("MyExceptionHandler errorHandler", e);

        // token錯誤
        /*
         * if (e instanceof InvalidAccessTokenException) {
         * InvalidAccessTokenException exception = (InvalidAccessTokenException)
         * e; return JSONResultUtil.errorWithMsg("u100004",
         * exception.getMessage()); }
         */

        // SpringMVC映射的參數沒傳值,導致映射參數失敗
        if (e instanceof HttpMessageNotReadableException) {
            return JSONResultUtil.error("必傳參數為空");
        }

        // @Valid 參數驗證失敗錯誤信息解析
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            BindingResult bindingResult = exception.getBindingResult();
            int errorCount = bindingResult.getErrorCount();
            if (errorCount > 0) {
                String defaultMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
                return JSONResultUtil.error(defaultMessage);
            }
            String message = exception.getMessage();
            return JSONResultUtil.error(message);
        }

        if (e instanceof HttpRequestMethodNotSupportedException) {
            return JSONResultUtil.error("u100001");
        }

        // 代碼用ValidateUtils 工具類進行參數校驗拋出的異常
        if (e instanceof BxIllegalArgumentException) {
            BxIllegalArgumentException exception = (BxIllegalArgumentException) e;
            return JSONResultUtil.errorWithMsg(exception.getErrorCode(), exception.getMessage());
        }

        return JSONResultUtil.error(ErrorCodeDefine.SYSTEM_ERROR);
    }
}

測試Curl:

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz"}' http://localhost:8088//user/add2                        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    94    0    76  100    18   6909   1636 --:--:-- --:--:-- --:--:--  9400{"success":false,"data":null,"msg":"age 不能為空","errorCode":"u100000"}

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz", "age": 90}' http://localhost:8088//user/add3             % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   115    0    86  100    29   3909   1318 --:--:-- --:--:-- --:--:--  5476{"success":false,"data":null,"msg":"年齡必須在18-25之間","errorCode":"u100000"}

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz", "age": 19, "hobbies": ["lq", "zq"]}' http://localhost:8088//user/add3
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   173    0   119  100    54  11900   5400 --:--:-- --:--:-- --:--:-- 19222{"success":true,"data":{"username":"zz","age":19,"hobbies":["lq","zq"],"fullname":null},"msg":"成功","errorCode":"0"}

2  測試分組的使用-分組只能針對Spring 提供的注解Validated 

  帶分組的功能是說可以在@Validated 注解可以聲明驗證的指定的分組。

1.  修改接收對象的實體如下

package com.xm.ggn.test;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

@Data
public class User2 implements Serializable {

    @NotNull(message = "username 不能為空")
    @Length(min = 2, max = 20, message = "username 長度必須在{min}到{max}之間")
    private String username;

    @NotNull(message = "age 不能為空")
    @Range(min = 18, max = 25, message = "年齡必須在{min}-{max}之間")
    private Integer age;

    /**
     * 愛好
     */
    @NotEmpty(message = "愛好不能為空")
    private List<String> hobbies;

    private String fullname;

    /**
     * 密碼,只在新增的時候進行驗證
     */
    @NotNull(message = "password 不能為空", groups = {AddUser.class})
    @Length(min = 6, max = 20, message = "password 長度必須在{min}到{max}之間", groups = {AddUser.class})
    private String password;

    /**
     * 內部累標記是新增操作
     */
    public static interface AddUser {
    }
}

  這里需要注意groups 聲明的class 必須是接口類型。

2. 修改Controller

    @PostMapping("/user/add3")
    public User2 addUser3(@RequestBody @Validated User2 user) {
        System.out.println(user);
        return user;
    }

    @PostMapping("/user/add5")
    public User2 addUser5(@RequestBody @Validated(User2.AddUser.class) User2 user) {
        System.out.println(user);
        return user;
    }

3. 測試

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz", "age": 19, "hobbies": ["lq", "zq"]}' http://localhost:8088//user/add3
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   189    0   135  100    54  10384   4153 --:--:-- --:--:-- --:--:-- 15750{"success":true,"data":{"username":"zz","age":19,"hobbies":["lq","zq"],"fullname":null,"password":null},"msg":"成功","errorCode":"0"}

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz", "age": 19, "hobbies": ["lq", "zq"]}' http://localhost:8088//user/add5
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   135    0    81  100    54   8100   5400 --:--:-- --:--:-- --:--:-- 13500{"success":false,"data":null,"msg":"password 不能為空","errorCode":"u100000"}

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"password": "111222"}' http://localhost:8088//user/add5
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   156    0   134  100    22   8375   1375 --:--:-- --:--:-- --:--:-- 10400{"success":true,"data":{"username":null,"age":null,"hobbies":null,"fullname":null,"password":"111222"},"msg":"成功","errorCode":"0"}

  可以看出這里如果@Validated 指定了class 會只驗證指定class 分組的信息,如果不指定會驗證所有不帶組號的規則。

補充:其實@Valid 相當於不帶屬性的@Validated, 默認也是驗證不帶 groups 屬性的驗證規則

比如測試如下:

(1) 接受參數的Bean

package com.xm.ggn.test;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

@Data
public class User2 implements Serializable {

    @NotNull(message = "username 不能為空")
    @Length(min = 2, max = 20, message = "username 長度必須在{min}到{max}之間")
    private String username;

    @NotNull(message = "age 不能為空")
    @Range(min = 18, max = 25, message = "年齡必須在{min}-{max}之間")
    private Integer age;

    /**
     * 愛好
     */
    @NotEmpty(message = "愛好不能為空")
    private List<String> hobbies;

    private String fullname;

    /**
     * 密碼,只在新增的時候進行驗證
     */
    @NotNull(message = "password 不能為空", groups = {AddUser.class})
    @Length(min = 6, max = 20, message = "password 長度必須在{min}到{max}之間", groups = {AddUser.class})
    private String password;

    /**
     * 內部累標記是新增操作
     */
    public static interface AddUser {
    }
}

(2) Controller

    @PostMapping("/user/add2")
    public User2 addUser2(@RequestBody @Valid User2 user) {
        System.out.println(user);
        return user;
    }

(3) curl 測試

$ curl -X POST --header 'Content-Type: application/json' -d '{"username": "zz", "age": 19, "hobbies": ["lq", "zq"]}' http://localhost:8088//user/add2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   189    0   135  100    54    182     73 --:--:-- --:--:-- --:--:--   255{"success":true,"data":{"username":"zz","age":19,"hobbies":["lq","zq"],"fullname":null,"password":null},"msg":"成功","errorCode":"0"}

  可以看出password 屬性驗證規則帶有groups 屬性,沒有被驗證到。 

 

2. @Valid、@Validated 參數驗證原理

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument 參數解析過程中進行驗證

    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }

validateIfApplicable(binder, parameter); 是進行參數校驗,並且將校驗結果收集到binder.getBindingResult() 中。

if 語句進行判斷如果有驗證不通過的,並且 isBindExceptionRequired 方法判斷是否需要拋出異常。其判斷邏輯如下:

    protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
        int i = parameter.getParameterIndex();
        Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
        boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
        return !hasBindingResult;
    }

如果當前驗證參數的下一個參數是Errors 的子類則不拋出異常,異常會封裝到 Errors 的子類中。  如果下一個參數不是Errors 的子類,則走上面拋出異常的代碼 throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());所以看到這里可以發現有兩種處理方式:

第一種是用 BindingResult bindingResult 接受錯誤結果;

    @PostMapping("/user/add4")
    public User2 addUser4(@RequestBody @Validated User2 user, BindingResult bindingResult) {
        System.out.println(bindingResult);
        System.out.println(user);
        return user;
    }

第二種是 增加全局異常攔截器,攔截上面的異常,然后給前端返回對應的錯誤信息:

Controller:

    @PostMapping("/user/add3")
    public User2 addUser3(@RequestBody @Validated User2 user) {
        System.out.println(user);
        return user;
    }

全局異常攔截器:

package com.xm.ggn.exception;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.val;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.xm.ggn.utils.JSONResultUtil;
import com.xm.ggn.utils.constant.ErrorCodeDefine;

import lombok.extern.slf4j.Slf4j;

/**
 * 全局異常處理器
 * 
 * @author Administrator
 *
 */
@RestControllerAdvice
@Slf4j
public class BXExceptionHandler {

    @ExceptionHandler(value = Throwable.class)
    public JSONResultUtil<Object> errorHandler(HttpServletRequest reqest, HttpServletResponse response, Exception e) {
        log.error("MyExceptionHandler errorHandler", e);

        // token錯誤
        /*
         * if (e instanceof InvalidAccessTokenException) {
         * InvalidAccessTokenException exception = (InvalidAccessTokenException)
         * e; return JSONResultUtil.errorWithMsg("u100004",
         * exception.getMessage()); }
         */

        // SpringMVC映射的參數沒傳值,導致映射參數失敗
        if (e instanceof HttpMessageNotReadableException) {
            return JSONResultUtil.error("必傳參數為空");
        }

        // @Valid 參數驗證失敗錯誤信息解析
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            BindingResult bindingResult = exception.getBindingResult();
            int errorCount = bindingResult.getErrorCount();
            if (errorCount > 0) {
                String defaultMessage = bindingResult.getAllErrors().get(0).getDefaultMessage();
                return JSONResultUtil.error(defaultMessage);
            }
            String message = exception.getMessage();
            return JSONResultUtil.error(message);
        }

        if (e instanceof HttpRequestMethodNotSupportedException) {
            return JSONResultUtil.error("u100001");
        }

        // 代碼用ValidateUtils 工具類進行參數校驗拋出的異常
        if (e instanceof BxIllegalArgumentException) {
            BxIllegalArgumentException exception = (BxIllegalArgumentException) e;
            return JSONResultUtil.errorWithMsg(exception.getErrorCode(), exception.getMessage());
        }

        return JSONResultUtil.error(ErrorCodeDefine.SYSTEM_ERROR);
    }
}

 

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#validateIfApplicable 方法進行驗證, 這個驗證過程會將驗證的結果收集到binder.getBindingResult()。所以核心的邏輯是在這個方法內部。這個方法里面首先拿注解Validated 或者 判斷注解是否是以Valid 開始。 這里也就確定了上面兩個注解 @Valid 和 @Validated 都會被驗證。

    protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
        Annotation[] annotations = parameter.getParameterAnnotations();
        for (Annotation ann : annotations) {
            Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
            if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
                Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
                Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
                binder.validate(validationHints);
                break;
            }
        }
    }

  這個方法里面首先拿注解Validated 或者 判斷注解是否是以Valid 開始。 這里也就確定了上面兩個注解 @Valid 和 @Validated 都會被驗證。

  然后其核心邏輯會調用到: org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateConstraints(org.hibernate.validator.internal.engine.ValidationContext<T>, org.hibernate.validator.internal.engine.ValueContext<?,V>, java.util.Set<javax.validation.ConstraintViolation<T>>)

    private <T, V> void validateConstraints(ValidationContext<T> validationContext,
            ValueContext<?, V> valueContext,
            Set<ConstraintViolation<T>> constraintViolations) {
        CompositionResult compositionResult = validateComposingConstraints(
                validationContext, valueContext, constraintViolations
        );

        Set<ConstraintViolation<T>> localViolations;

        // After all children are validated the actual ConstraintValidator of the constraint itself is executed
        if ( mainConstraintNeedsEvaluation( validationContext, constraintViolations ) ) {

            if ( log.isTraceEnabled() ) {
                log.tracef(
                        "Validating value %s against constraint defined by %s.",
                        valueContext.getCurrentValidatedValue(),
                        descriptor
                );
            }

            // find the right constraint validator
            ConstraintValidator<A, V> validator = getInitializedConstraintValidator( validationContext, valueContext );

            // create a constraint validator context
            ConstraintValidatorContextImpl constraintValidatorContext = new ConstraintValidatorContextImpl(
                    validationContext.getParameterNames(),
                    validationContext.getTimeProvider(),
                    valueContext.getPropertyPath(),
                    descriptor
            );

            // validate
            localViolations = validateSingleConstraint(
                    validationContext,
                    valueContext,
                    constraintValidatorContext,
                    validator
            );

            // We re-evaluate the boolean composition by taking into consideration also the violations
            // from the local constraintValidator
            if ( localViolations.isEmpty() ) {
                compositionResult.setAtLeastOneTrue( true );
            }
            else {
                compositionResult.setAllTrue( false );
            }
        }
        else {
            localViolations = Collections.emptySet();
        }

        if ( !passesCompositionTypeRequirement( constraintViolations, compositionResult ) ) {
            prepareFinalConstraintViolations(
                    validationContext, valueContext, constraintViolations, localViolations
            );
        }
    }

下面這行代碼會獲取到合適的驗證器ConstraintValidator, 每個驗證注解都有對應的validator 與之對應。

ConstraintValidator<A, V> validator = getInitializedConstraintValidator( validationContext, valueContext ); 尋找合適的validater,比如對於 @NotNull 獲取到對應的Validator 是org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator

最后請求到達: org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager#getInitializedValidator    方法內部從緩存中沒有獲取到相關的信息,然后調用下面方法進行獲取

    org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager#createAndInitializeValidator    創建和初始化相關的validator
    然后將validator 放到緩存中
    最后調用 javax.validation.ConstraintValidator#isValid 進行驗證。

 

org.hibernate.validator.internal.metadata.core.ConstraintHelper#ConstraintHelper 構造里面維護了驗證注解與驗證器的關系, 會在容器啟動過程中進行調用然后維護其關系

 

3. 自定義自己的Validator 

  模仿org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator 進行書寫

(1) 定義注解

package com.xm.ggn.test.contraint;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NumberValidator.class)
public @interface Number {

    String message() default "非法的數字";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int min() default 0;

    int max() default Integer.MAX_VALUE;
}

(2) 編寫驗證器

package com.xm.ggn.test.contraint;

import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class NumberValidator implements ConstraintValidator<Number, Integer> {

    private static final Log log = LoggerFactory.make();

    private int min;
    private int max;

    @Override
    public void initialize(Number parameters) {
        min = parameters.min();
        max = parameters.max();
        validateParameters();
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
        if (value == null) {
            return true;
        }
        return value >= min && value <= max;
    }

    private void validateParameters() {
        if (min < 0) {
            throw log.getMinCannotBeNegativeException();
        }
        if (max < 0) {
            throw log.getMaxCannotBeNegativeException();
        }
        if (max < min) {
            throw log.getLengthCannotBeNegativeException();
        }
    }
}

(3) 測試

package com.xm.ggn.test;

import com.xm.ggn.test.contraint.Number;
import lombok.Data;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

@Data
public class User3 implements Serializable {

    @NotNull(message = "age 不能為空")
    @Number(min = 18, max = 25, message = "年齡必須在{min}-{max}之間")
    private Integer age;
}

測試Controller

    @PostMapping("/user/add6")
    public User3 addUser6(@RequestBody @Validated User3 user) {
        System.out.println(user);
        return user;
    }

curl 測試:

qiaoliqiang@A022296-NC01 MINGW64 /e/xiangmu/bs-media (master)
$ curl -X POST --header 'Content-Type: application/json' -d '{"age": 2}' http://localhost:8088//user/add6
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    96    0    86  100    10    174     20 --:--:-- --:--:-- --:--:--   194{"success":false,"data":null,"msg":"年齡必須在18-25之間","errorCode":"u100000"}

 

補充:嵌套檢查

有的時候驗證需要嵌套驗證,比如接參數的是一個集合,集合里面是具體的bean。這時候集合需要加@Valid注解

(1) controller

    @PostMapping("/user/batchAdd")
    public void batchAdd(@RequestBody @Validated UserRequestVO requestVO) {
    }

(2) UserRequestVO

package com.xm.ggn.test;

import lombok.Data;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;

@Data
public class UserRequestVO {

    @NotNull
    @Valid
    private List<User> users;
}

(3) User

package com.xm.ggn.test;

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

@Data
public class User implements Serializable {

    @NotNull(message = "username 不能為空")
    @Length(min = 2, max = 20, message = "username 長度必須在{min}到{max}之間")
    private String username;

    @NotNull(message = "age 不能為空")
    @Range(min = 18, max = 25, message = "年齡必須在{min}-{max}之間")
    private Integer age;

    private String fullname;

    private String createDate;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM