ConstraintValidator類如何實現(xiàn)自定義注解校驗前端傳參
前言
今天項目碰到這么一個問題,前端傳遞的json格式到我的微服務后端轉(zhuǎn)換為vo類,其中有一個Integer的字段后端希望它在固定的幾個數(shù)里面取值,例如只能取值1、2、4。
一般咱們的思路是啥呢,找一些spring為我們提供的類似@Length、@NotBlank這些注解加在參數(shù)上面。
像下面這樣

不過我這個校驗一時間想不起來用哪個注解了,咋整呢?行吧,咱不求人,自己實現(xiàn)一個。
補充一句話,千萬別直接拿著實體類往后傳遞到service層校驗哈,太low了這樣子。
一、利用@Constraint定義注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import 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 "數(shù)字不合法,不在要求的取值范圍之內(nèi)";
/**
* 標識要校驗的值所屬組,這個后面詳細解釋
*
*/
Class<?>[] groups() default {};
/**
* 這個字段一般不需要我們關(guān)注
*
*/
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) {
// 如果是必選的話,假設為我們傳遞的參數(shù)為空那肯定不行
if (require) {
if (number == null) {
return false;
}
return valueList.contains(number);
} else {
// 如果不為必選參數(shù),為空返回true,不為空還是得校驗
if (StringUtils.isEmpty(number)) {
return true;
} else {
return valueList.contains(number);
}
}
}
}
增強類繼承ConstraintValidator類,實現(xiàn)的initialize()方法是初始化方法,啥意思呢,啥目的呢?在你真正執(zhí)行校驗之前,可以做一些準備性工作,發(fā)生在要校驗的值上面的注解的IllegalNumber 已經(jīng)給咱們傳進來了。我做的初始化工作就是load一下Integer類型的可選值,方便一會執(zhí)行真正的校驗。
然后在isValid()方法中你可以做真正的校驗了,很簡單,我看下傳遞的Integer類型的值是不是acceptValues里面的可選值就行了。
定義一個前端傳遞的類,方便調(diào)試注解
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數(shù)據(jù)并parse為TestVO類
/**
* 測試自定義注解
*
* @param vo json將會映射的實體
* @return 默認信息
*/
@PostMapping(value = "/v1.0/test2", name = "測試自定義注解")
public String test2(@Valid @RequestBody TestVO vo) {
log.info("get vo success , detail message is:{}", vo);
return RETURN_MESSAGE;
}
注意,如果說前端傳遞數(shù)據(jù)不符合注解的校驗,其實是會拋出異常的來自@Constraint注解實現(xiàn)的注解都有此特點,例如@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 {
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.getMessage());
return Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
}
}
2.測試效果
下面測試一下。打開postman。直接干!取值的限定是0、1、2。咱們先試下錯誤的

ok,再試下正確的

3.注解中的groups參數(shù)詳解
groups參數(shù),代表所屬組的意思。演示下怎么用,大家也就知道這個參數(shù)啥意思了。 建立Group1接口
public interface Group1 {
}
建立Group2接口
public interface Group2 {
}
給TestVO增加一個參數(shù),方便一會進行比較
@Data
public class TestVO {
@NotNull
@IllegalNumber(acceptValues = {0, 1,2},required = true,message = "請正確取值",groups = Group1.class)
private Integer number;
@NotNull
@IllegalNumber(acceptValues = {0, 1,2},required = true,message = "請正確取值ha",groups = Group2.class)
private Integer number2;
@NotNull
@Length(min = 1)
private String password;
}
使用注解的時候標明所屬組:

接口處也進行標識:

現(xiàn)在咱們分別測試下兩個接口,看groups參數(shù)是否能生效
test2接口

test3接口

ok,相信大家對此參數(shù)已經(jīng)掌握了,這里不再多余贅述。
總結(jié)
本篇介紹了自定義注解的另外一種手法,其實還有許許多多的手法,例如利用反射實現(xiàn)、利用攔截器實現(xiàn)等等。遇見的時候咱們再介紹。 以上僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家
相關(guān)文章
springboot整合logback實現(xiàn)日志管理操作
本章節(jié)是記錄logback在springboot項目中的簡單使用,本文將會演示如何通過logback將日志記錄到日志文件或輸出到控制臺等管理操作,感興趣的朋友跟隨小編一起看看吧2024-02-02
spring profile 多環(huán)境配置管理詳解
這篇文章主要介紹了 spring profile 多環(huán)境配置管理詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01
netflix.discovery.shared.transport.TransportException:Cannot
這篇文章主要介紹了netflix.discovery.shared.transport.TransportException:Cannot execute request on any known server報錯問題及解決方法,感興趣的朋友一起看看吧2023-09-09
java使用mybatis調(diào)用存儲過程返回一個游標結(jié)果集方式
這篇文章主要介紹了java使用mybatis調(diào)用存儲過程返回一個游標結(jié)果集方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Mybatis-Plus處理Mysql?Json類型字段的詳細教程
這篇文章主要給大家介紹了關(guān)于Mybatis-Plus處理Mysql?Json類型字段的詳細教程,Mybatis-Plus可以很方便地處理JSON字段,在實體類中可以使用@JSONField注解來標記JSON字段,同時在mapper.xml中使用json函數(shù)來操作JSON字段,需要的朋友可以參考下2024-01-01

