Springboot引數校驗--最全

xiaoxiaokunkunkun發表於2020-12-10

Springboot引數校驗

validator內建註解

註解詳細資訊
@Null被註釋的元素必須為 null
@NotNull被註釋的元素必須不為 null
@AssertTrue被註釋的元素必須為 true
@AssertFalse被註釋的元素必須為 false
@Min(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min)被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction)被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past被註釋的元素必須是一個過去的日期
@Future被註釋的元素必須是一個將來的日期
@Pattern(value)被註釋的元素必須符合指定的正規表示式

Hibernate Validator 附加的 constraint

註解詳細資訊
@Email被註釋的元素必須是電子郵箱地址
@Length被註釋的字串的大小必須在指定的範圍內
@NotEmpty被註釋的字串的必須非空
@Range被註釋的元素必須在合適的範圍內
@NotBlank驗證字串非null,且長度必須大於0

注意

  • @NotNull 適用於任何型別被註解的元素必須不能與NULL
  • @NotEmpty 適用於集合或者陣列不能為Null且長度必須大於0
  • @NotBlank 只能用於String上面 不能為null,呼叫trim()後,長度必須大於0

這裡寫一個手機號的正則校驗(這只是一個簡單手機號正則校驗)—建議如果想實現複雜的引數校驗可以自定義的約束(後面會寫)

/**
 * 手機號
 */
@NotNull(message = "手機號不能為空")
@NotBlank(message = "手機號不能為空")
@Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機號格式有誤")
private String mobileNo;

@Validated

這個註解來自package org.springframework.validation.annotation;包下

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})

可以 使用在類上、方法上、引數上

若要實現單一引數的效驗

(我感覺若是使用restful風格,這個好像沒用,因為若不傳遞引數會直接報錯)

@Validated註解必須設定在類上

栗子:(單個引數異常處理也配置在了***全域性異常處理***裡面了)

 /**
     * 根據id查詢使用者
     * @param id
     * @return
     */
    @GetMapping("/find")
    @ApiOperation("查詢使用者")
    public User find(@NotBlank(message = "id不能為空") String id){
        User user = new User();
        user.setId(Long.parseLong(id));
        return user;
    }

若要實現javabean物件效驗

@validated註解必須設定在引數前面

栗子:

/**
 * 新增商品
 * @param cartDTO
 */
@PostMapping
@ApiOperation("向購物車中新增商品")
public Integer addCart(@RequestBody @Validated CartDTO cartDTO){
    return cartService.addCart(cartDTO);
}

我這裡json傳遞的是 {}

響應給我的json是(若想讓響應結果像我這樣還需要設定全域性異常處理後面會說)

{
  "code": 1002,
  "msg": "引數效驗失敗",
  "data": [
    "商品id不能為空",
    "商品型別不能為空",
    "商品屬性id不能為空",
    "使用者id不能為空"
  ]
}

javabean物件是

@Data
public class CartDTO {

    @ApiModelProperty(value = "購物車表ID")
    @NotNull(message = "更新時欄位id必填",groups = {Update.class})
    private Long id;

    @ApiModelProperty(value = "使用者ID")
    @NotNull(message = "使用者id不能為空")
    private Long uid;

    @ApiModelProperty(value = "型別")
    @NotBlank(message = "商品型別不能為空")
    private String type;

    @ApiModelProperty(value = "商品ID")
    @NotNull(message = "商品id不能為空")
    private Long productId;

    @ApiModelProperty(value = "商品屬性")
    @NotBlank(message = "商品屬性id不能為空")
    private String productAttrUnique;

    @ApiModelProperty(value = "商品數量")
    @Min(message = "商品數量不能小於0",value = 0)
    private Integer cartNum;

    @ApiModelProperty(value = "0 = 未購買 1 = 已購買")
    private Boolean isPay;

    @ApiModelProperty(value = "是否為立即購買")
    private Boolean isNew;

    @ApiModelProperty(value = "拼團id")
    private Integer combinationId;

    @ApiModelProperty(value = "秒殺產品ID")
    private Integer seckillId;

    @ApiModelProperty(value = "砍價id")
    private Integer bargainId;
}

@Valid

來自package javax.validation;包下

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})

它可以使用在使用在類、欄位、構造器、引數上

若要實現javabean物件效驗和@Validated用法是一樣的(這裡就不舉例子了)

@Validated和@Valid的區別

1使用@Validated可以實現分組校驗

栗子:

1、首先你要建立分組(我這裡是建立了兩個,新建驗證、更新驗證)而且必須要繼承Default(javax.validation.groups.Default;)介面

package cn.newhopedairy.newshop.mall.common.cart.groups;

import javax.validation.groups.Default;

public interface Create extends Default {
}
package cn.newhopedairy.newshop.mall.common.cart.groups;

import javax.validation.groups.Default;

public interface Update extends Default {
}

2、在欄位上配置分組屬性(可以配置多個)

@Data
public class CartDTO {

    @ApiModelProperty(value = "購物車表ID")
    @NotNull(message = "更新時欄位id必填",groups = {Update.class})
    @NotNull(message = "新增時欄位id必填",groups = {Create.class})//這裡我只是舉個例子,實際中不會這樣驗證
    private Long id;
    ......
}

3、在驗證的入口處(一般在controller的方法中)

@RestController
@Api("購物車介面")
@RequestMapping("/cart")
@Validated
public class CartController {
    /**
     * 新增商品
     * @param cartDTO
     */
    @PostMapping
    @ApiOperation("向購物車中新增商品")
    //
    public Integer addCart(@RequestBody @Validated(value = {Create.class}) CartDTO cartDTO){
        return cartService.addCart(cartDTO);
    }
    
        /**
     * 修改商品數量
     * @param cartDTO
     * @return
     */
    @PatchMapping
    @ApiOperation("修改購物車中的商品資料")
    public Integer updateCart(@RequestBody @Validated(Update.class) CartDTO cartDTO){
        return cartService.updateCart(cartDTO);
    }
}

4、測試

呼叫新增商品的方法

http://localhost:8080/cart

請求時的json資料為:

{
  "bargainId": 0,
  "cartNum": 0,
  "combinationId": 0,
  "isNew": true,
  "isPay": true,
  "productAttrUnique": "string",
  "productId": 0,
  "seckillId": 0,
  "type": "string",
  "uid": 0
}

響應的json資料為:

{
  "code": 1002,
  "msg": "引數效驗失敗",
  "data": [
    "新增時欄位id必填"
  ]
}

呼叫修改商品的方法

http://localhost:8080/cart

請求時的json資料為:

{
  "bargainId": 0,
  "cartNum": 0,
  "combinationId": 0,
  "isNew": true,
  "isPay": true,
  "productAttrUnique": "string",
  "productId": 0,
  "seckillId": 0,
  "type": "string",
  "uid": 0
}

響應的json資料為:

{
  "code": 1002,
  "msg": "引數效驗失敗",
  "data": [
    "更新時欄位id必填"
  ]
}

這樣就是實現了分組效驗的效果

“data”: [
“更新時欄位id必填”
]

“data”: [
“新增時欄位id必填”
]

2@Validated和@Valid配合使用key實現巢狀驗證(@Valid和@Valid配合使用也可以實現使用方式差不多)

@PostMapping
@ApiOperation("查詢使用者")
public User add(@RequestBody @Validated User user){

     return user;
}

@Data
@ApiModel(value="User物件", description="")
public class User implements Serializable {


    @ApiModelProperty(value = "年齡")
    @Min(value = 1,message = "年齡不能小於1")
    private Integer age;

    @ApiModelProperty(value = "郵箱")
    private String email;

    @Valid
    private Car car;
}

@Data
@ApiModel(value="User物件", description="")
public class Car {

    @NotBlank(message = "車的顏色不能為空")
    private String color;

    @Min(value = 5,message = "車長必須超過5米")
    private Integer type;
}

請求引數:
 {
  "age": 10,
  "car": {"color": "",
"type": 10},
  "email": "string",
  "id": 0,
  "name": "string"
  
}

響應引數:
{
  "code": 1002,
  "msg": "引數效驗失敗",
  "data": [
    "車的顏色不能為空"
  ]
}    

自定義異常

package com.example.hk_jsr303.exception;

import com.example.hk_jsr303.result.ResultVO;
import lombok.Getter;
import lombok.Setter;

/**
 * @Classname MyException
 * @Description TODO
 * @Date 2020/12/9 23:46
 * @Created by hekun
 */
@Getter
@Setter
public class MyException extends RuntimeException {
    private int code;
    private String msg;
    
    public MyException(){}
    
    public MyException(ResultVO resultVO) {
        this.code=resultVO.getCode();
        this.msg=resultVO.getMsg();
    }
}

定義全域性異常處理

更具體的定義可以看一下這篇blog:

https://juejin.cn/post/6872226869605826573#heading-6

package com.example.hk_jsr303.exception;

import com.example.hk_jsr303.result.ResultVO;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @Classname GlobalExceptionHandler
 * @Description TODO
 * @Date 2020/12/9 15:11
 * @Created by hekun
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 方法引數錯誤異常
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
        //log.error("方法引數錯誤異常");
        List<String> list=new ArrayList<>();
        // 從異常物件中拿到ObjectError物件
        if (!e.getBindingResult().getAllErrors().isEmpty()){
            for(ObjectError error:e.getBindingResult().getAllErrors()){
                list.add(error.getDefaultMessage().toString());
            }
        }
        // 然後提取錯誤提示資訊進行返回
        return new ResultVO(1002,"引數效驗失敗",list);
    }

    /**
     * 單個引數異常處理
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Object constraintViolationException(ConstraintViolationException ex) {

        // 獲取具體的錯誤資訊
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        // 列印資料
        violations.forEach(e -> System.out.println(e.getMessage()));

        return "單個-請求引數錯誤";
    }


}



//下面這個結果類要使用到
@Getter
@Setter
public class ResultVO<T> {
    /**
     * 狀態碼,比如1000代表響應成功
     */
    private int code;
    /**
     * 響應資訊,用來說明響應情況
     */
    private String msg;
    /**
     * 響應的具體資料
     */
    private T data;


    public ResultVO(int code,String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    
    public ResultVO(int code,String msg) {
        this.code = code;
        this.msg = msg;
    }
}

自定義約束

一般情況內建的約束一般是夠用了,如果有別的情況也是可以自己定義約束條件的

假設我們要給車設定一個顏色,而且顏色只能設定為red或者green

1、注意:@ListValue這個註解是我們自定義的

@Data
@ApiModel(value="User物件", description="")
public class Car {

    @NotBlank(message = "車的顏色不能為空")
    @ListValue(vals = {"red","green"},message = "車的顏色不對")
    private String color;
    
        @Min(value = 5,message = "車長必須超過5米")
    private Integer type;
}

2、我們要建立這個註解@ListValue,我們可以模仿官方寫的註解比如@Min

package com.example.hk_jsr303.groups;


import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Constraint(validatedBy = {ListValueConstraintValidator.class})//自定義的約束校驗器 ---很重要
//自定義的約束校驗器 作用我們要在裡面寫一下校驗規則 isValid() 方法返回true表示校驗成功
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {

    //錯誤資訊的提示
    String message() default "{com.sjl.common.valid.ListValue.message}";

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

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

    //自定義值的型別
    String[] vals() default {};
}

3、寫我們自己的約束校驗器ListValueConstraintValidator ,這裡要注意繼承ConstraintValidator

package com.example.hk_jsr303.groups;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname ListValueConstraintValidator
 * @Description TODO
 * @Date 2020/12/9 22:58
 * @Created by hekun
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,String> {

    private static Set<String> set = new HashSet<>();

    @Override
    public void initialize(ListValue constraintAnnotation) {
        for (String val : constraintAnnotation.vals()) {
            set.add(val);
        }
    }

    /**
     * 判斷是否通過校驗
     *
     * @param value   傳入的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

5、進行測試

/**
 * 新增車輛
 * @param car
 * @return
 */
@PostMapping("/car")
@ApiOperation("新增車輛")
public Car addCar(@RequestBody @Validated Car car){
    return car;
}

請求json:
{
  "color": "red1",
  "type": 10
}

響應json:
{
  "code": 1002,
  "msg": "引數效驗失敗",
  "data": [
    "車的顏色不對"
  ]
}

參考文件

https://juejin.cn/post/6844904118536912904#heading-7

https://juejin.cn/post/6844904016380461070#heading-6

https://juejin.cn/post/6872226869605826573#heading-7

https://juejin.cn/post/6844904133296652296#heading-6

https://www.cnblogs.com/songjilong/p/12663564.html

https://blog.csdn.net/justry_deng/article/details/86571671?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control

相關文章