前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot系列之自定义枚举类的数据校验注解

SpringBoot系列之自定义枚举类的数据校验注解

作者头像
SmileNicky
发布2021-12-17 17:27:51
2.7K1
发布2021-12-17 17:27:51
举报
文章被收录于专栏:Nicky's blog

SpringBoot系列之自定义枚举类的数据校验注解 业务场景:数据校验,需要对枚举类型的数据传参,进行数据校验,不能随便传参。拓展,支持多个参数的枚举数据校验

在网上找到很多参考资料,所以本博客基于这些博客进行拓展补充,ok,先建一个springboot项目

项目环境:

  • JDK 1.8
  • SpringBoot2.2.1
  • Maven 3.2+
  • 开发工具
    • IntelliJ IDEA
    • smartGit 创建一个SpringBoot Initialize项目
    在这里插入图片描述
    在这里插入图片描述

    选择jdk8

    在这里插入图片描述
    在这里插入图片描述

    选择lombok和spring web

    在这里插入图片描述
    在这里插入图片描述

    项目建好之后,在maven配置文件加上:

代码语言:javascript
复制
<dependency>
     <groupId>javax.validation</groupId>
     <artifactId>validation-api</artifactId>
     <version>2.0.1.Final</version>
 </dependency>

这里可以先写个例子进行验证,写个枚举类,表示多种支付类型,比如支付宝执法,微信支付等等

代码语言:javascript
复制
package com.example.common.util.validator.sample.enums;

public enum PayTypeEnum {

    Cash("1","现金"),
    Alipay("2","支付宝"),
    WeChatPay("3","微信支付"),
    BankCard("4","银行卡支付"),
    CreditCard("5","信用卡支付");

    PayTypeEnum(String code , String desc) {
        this.code = code;
        this.desc = desc;
    }

    private String code;
    private String desc;

    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }

}

因为要校验传入参数是否为枚举类里的类型,可以在PayTypeEnum类里新增一个校验方法

代码语言:javascript
复制
public static boolean isValueValid(String value) {
    if(!StringUtils.isEmpty(value)){
        for (PayTypeEnum enumObj : PayTypeEnum.values()) {
            if (enumObj.getCode().equals(value)) {
                return true;
            }
        }
        return false;
    }
    return true;
}

这里是加一下自定义的元注解类,然后通过@Constraint指定具体的校验类,通过反射机制获取对应的方法,比如isValueValid这个方法

代码语言:javascript
复制
package com.example.common.util.validator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;


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

    Logger log = LoggerFactory.getLogger(EnumValueValidator.class);

    String message() default "参数有误";

    Class<? extends Enum<?>> enumClass();

    String enumMethod();
    
    class Validator implements ConstraintValidator<EnumValueValidator , Object> {
        private Class<? extends Enum<?>> enumClass;
        private String enumMethod;

        @Override
        public void initialize(EnumValueValidator constraintAnnotation) {
            enumMethod = constraintAnnotation.enumMethod();
            enumClass = constraintAnnotation.enumClass();
        }
        @Override
        public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
          // 值没传的情况,直接返回true
            if (StringUtils.isEmpty(o)) return Boolean.TRUE;
            if (enumClass == null || StringUtils.isEmpty(enumMethod)) return Boolean.TRUE;
            Class<?> vclass = o.getClass();
            try {
            // 反射机制获取具体的校验方法
                Method method = enumClass.getMethod(enumMethod,vclass);
                if (!Boolean.TYPE.equals(method.getReturnType()) &&
                        !Boolean.class.equals(method.getReturnType())) {
                    throw new RuntimeException("校验方法不是布尔类型!");
                }
                if (!Modifier.isStatic(method.getModifiers())) {
                    throw new RuntimeException("校验方法不是静态方法!");
                }
                method.setAccessible(true);
                // 调用具体的方法
                Boolean res = (Boolean) method.invoke(null,o);
                return res != null ? res : false;
            } catch (NoSuchMethodException e) {
                log.error("NoSuchMethodException:{}" ,e);
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                log.error("IllegalAccessException:{}" ,e);
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                log.error("InvocationTargetException:{}" ,e);
                throw new RuntimeException(e);
            }
        }
    }

}

具体的bean类,加上@EnumValueValidator(enumClass = PayTypeEnum.class , enumMethod = "isStrsValid" , message = "支付类型校验有误")指向具体的枚举类和校验方法

代码语言:javascript
复制
package com.example.common.util.validator.sample.model;

import com.example.common.util.validator.EnumValueValidator;
import com.example.common.util.validator.sample.enums.PayTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;

import javax.validation.constraints.NotNull;

@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder(toBuilder = true)
@ToString
public class ShopOrder {

    @EnumValueValidator(enumClass = PayTypeEnum.class , enumMethod = "isStrsValid" , message = "支付类型校验有误")
    @NotNull(message = "支付类型必须传")
    private String payType;

}

加上@Validated开启校验

代码语言:javascript
复制
package com.example.common.util.validator.sample.controller;

import com.example.common.util.validator.sample.model.ShopOrder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/api/orders")
public class SampleController {

    @PostMapping
    public String saveOrder(@Validated ShopOrder shopOrder) {
        return shopOrder.toString();
    }
}

校验出错返回:

代码语言:javascript
复制
{
    "timestamp": "2021-12-16T10:01:27.801+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "EnumValueValidator.shopOrder.payType",
                "EnumValueValidator.payType",
                "EnumValueValidator.java.lang.String",
                "EnumValueValidator"
            ],
            "arguments": [
                {
                    "codes": [
                        "shopOrder.payType",
                        "payType"
                    ],
                    "arguments": null,
                    "defaultMessage": "payType",
                    "code": "payType"
                },
                "com.example.common.util.validator.sample.enums.PayTypeEnum",
                {
                    "defaultMessage": "isStrsValid",
                    "arguments": null,
                    "codes": [
                        "isStrsValid"
                    ]
                }
            ],
            "defaultMessage": "支付类型校验有误",
            "objectName": "shopOrder",
            "field": "payType",
            "rejectedValue": "2,111",
            "bindingFailure": false,
            "code": "EnumValueValidator"
        }
    ],
    "message": "Validation failed for object='shopOrder'. Error count: 1",
    "path": "/api/orders"
}

拓展,这里要求传payType类型,也就是支持多选的情况,参数payType=2,111,这里要修改校验方法:

代码语言:javascript
复制
public static boolean isStrsValid(String value) {
    if (!value.contains(","))
        return isValueValid(value);
    String[] arr = StringUtils.split(value , ",");
    for (String s : arr) {
        if (!isValueValid(s)) {
            return false;
        }
    }
   return true;
}

加上注解,enumMethod 改成isStrsValid

代码语言:javascript
复制
@EnumValueValidator(enumClass = PayTypeEnum.class , enumMethod = "isStrsValid" , message = "支付类型校验有误")

调用这个接口:

代码语言:javascript
复制
http://127.0.0.1:8080/api/orders?payType=2,111
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/12/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档