1. 程式人生 > 程式設計 >ConstraintValidator類如何實現自定義註解校驗前端傳參

ConstraintValidator類如何實現自定義註解校驗前端傳參

前言

今天專案碰到這麼一個問題,前端傳遞的json格式到我的微服務後端轉換為vo類,其中有一個Integer的欄位後端希望它在固定的幾個數裡面取值,例如只能取值1、2、4。

一般咱們的思路是啥呢,找一些spring為我們提供的類似@Length、@NotBlank這些註解加在引數上面。

像下面這樣

在這裡插入圖片描述

不過我這個校驗一時間想不起來用哪個註解了,咋整呢?行吧,咱不求人,自己實現一個。

補充一句話,千萬別直接拿著實體類往後傳遞到service層校驗哈,太low了這樣子。

一、利用@Constraint定義註解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
im
程式設計客棧
port java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint( validatedBy = {IllegalNumberValidator.class} ) public @interface IllegalNumber { /** * 允許前端取的幾個值 * */ int[] acceptValues(); /** * 標識此欄位是否為必選項 * */ boolean required() default true; /** * 標識此欄位是否為必選項 * */ String message() default "數字不合法,不在要求的取值範圍之內"; /** * 標識要校驗的值所屬組,這個後面詳細解釋 * */ Class<?>[] groups() default {}; /** * 這個欄位一般不需要我們關注 * */ Class<? extends Payload>[] payload() default {}; }

二、增強註解

1.編寫增強類

注意到剛才註解中的@Constraint註解了嗎

在這裡插入圖片描述

validatedBy屬性標識這個註解要被哪個類所增強,我們把增強類IllegalNumberValidator定義出來

import com.google.common.collect.Lists;
import org.springframework.util.StringUtils;
import java.util.List;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IllegalNumberValidator implements ConstraintValidator<IllegalNumber,Integer> {
    private final List<Integer> valueList = Lists.newArrayList();
    private boolean require = false;
    @Override
    public void initialize(IllegalNumber constraintAnnotation) {
        require = constraintAnnotation.required();
        int[] ints = constraintAnnotation.acceptValues();
        for (int anInt : ints) {
            valueList.add(anInt);
        }
    }
    @Override
    public boolean isValid(Integer number,ConstraintValidatorContext constraintValidatorContext) {
        // 如果是必選的話,假設為我們傳遞的引數為空那肯定不行
        if (require) {
            if (number == null) {
                return false;
            }
            return valueList.contains(number);
        } else {
            // 如果不為必選引數,為空返回true,不為空還是得校驗
            if (StringUtils.isEmpty(number)) {
                return true;
            } else {
                return valueList.contains(number);
            }
        }
    }
}

增強類繼承ConstraintValidator類,實現的initialize()方法是初始化方法,啥意思呢,啥目的呢?在你真正執行校驗之前,可以做一些準備性工作,發生在要校驗的值上面的註解的IllegalNumber 已經給咱們傳進來了。我做的初始化工作就是load一下Integer型別的可選值,方便一會執行真正的校驗。

然後在isValid()方法中你可以做真正的校驗了,很簡單,我看下傳遞的Integer型別的值是不是acceptValues裡面的可選值就行了。

定義一個前端傳遞的類,方便除錯註解

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
@Data
public class TestVO {
    @NotNull
    @IllegalNumber(acceptValues = {0,1,2},required = true,message = "請正確取值")
    private Integer number;
    @NotNull
    @Length(min = 1)
    private String password;
}

定義介面,用來接收前端傳遞的json資料並parse為TestVO類

    /**
     * 測試自定義註解
     *
     * @param vo json將會對映的實體
     * @return 預設資訊
     */
    @PostMapping(value = "/v1.0/test2",name = "測試自定義註解")
    public String test2(@Valid @RequestBo程式設計客棧dy TestVO vo) {
        log.info("get vo success,detail message is:{}",vo);
        return RETURN_MESSAGE;
    }

注意,如果說前端傳遞資料不符合註解的校驗,其實是會丟擲異常的來自@Constraint註解實現的註解都有此特點,例如@Length、@Max等。咱們需要在異常丟擲的時候給出攔截 這裡咱們做一個通用攔截:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.Objects;
import javax.validation.ConstraintViolationException;
@ControllerAdvice
public class RestResponseEntityExceptionHandler www.cppcns.com{
    private static final Logger LOG = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
    @Autowired
    private ApplicationContext applicationContext;
    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public String handleConstraintViolationException(ConstraintViolationException e) {
        LOG.info("ConstraintViolationException intercept success:{}",e.getMessage());
        return e.getMessage();
    }
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        LOG.info("MethodArgumentNotValidException intercept success:{}",e.getMmhCNUIjRaessage());
        return Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
    }
}

2.測試效果

下面測試一下。開啟postman。直接幹!取值的限定是0、1、2。咱們先試下錯誤的

在這裡插入圖片描述

ok,再試下正確的

在這裡插入圖片描述

3.註解中的groups引數詳解

groups引數,代表所屬組的意思。演示下怎麼用,大家也就知道這個引數啥意思了。 建立Group1介面

public interface Group1 {
}

建立Group2介面

public interface Group2 {
}

給TestVO增加一個引數,方便一會進行比較

@Data
public class TestVO {
    @NotNull
    @IllegalNumber(acceptValues = {0,message = "請正確取值",groups = Group1.class)
    private Integer number;
    @NotNull
    @IllegalNumber(acceptValues = {0,message = "請正確取值ha",groups = Group2.class)
    private Integer number2;
    @NotNull
    @Length(min = 1)
    private String password; 
}

使用註解的時候標明所屬組:

在這裡插入圖片描述

介面處也進行標識:

在這裡插入圖片描述

現在咱們分別測試下兩個介面,看groups引數是否能生效

test2介面

在這裡插入圖片描述

test3介面

在這裡插入圖片描述

ok,相信大家對此引數已經掌握了,這裡不再多餘贅述。

總結

本篇介紹了自定義註解的另外一種手法,其實還有許許多多的手法,例如利用反射實現、利用攔截器實現等等。遇見的時候咱們再介紹。 以上僅為程式設計客棧個人經驗,希望能給大家一個參考,也希望大家多多支援我們