使用spring-boot-starter-validation实现入参校验

使用 spring-boot-starter-validation实现入参校验

引入依赖

复制代码
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

定义分组

由于新增、更新、查询、删除都可以使用一个实体接受入参,而不同情况需要校验的情况也不同,比如,新增时不需要校验id必填,删除时需要校验id必填,分组可以理解成不同策略,不同分组下可以使用不同的校验规则

Java 复制代码
package com.param_validate_demo.groups;

import javax.validation.groups.Default;

public interface Insert  extends Default {
}

定义校验规则

Java 复制代码
package com.param_validate_demo.model;

import com.param_validate_demo.annotation.AgeValidate;
import com.param_validate_demo.annotation.EnumValidate;
import com.param_validate_demo.enums.GenderEnum;
import com.param_validate_demo.enums.RoleEnum;
import com.param_validate_demo.groups.Insert;
import com.param_validate_demo.groups.Query;
import com.param_validate_demo.groups.Update;
import lombok.Data;

import javax.validation.constraints.NotNull;
import java.math.BigInteger;
import java.util.List;

@Data
@AgeValidate(groups = {Insert.class, Update.class})
public class User {
    private BigInteger id;
    @NotNull(groups = {Insert.class, Update.class, Query.class},message = "name 不能为空")
    private String name;
    private int age;
    private String address;
    private String phone;
    private String email;

    @EnumValidate(enumClass = GenderEnum.class, groups = {Insert.class, Update.class})
    private String gender;

    @EnumValidate(enumClass = RoleEnum.class, message = "入参roles中存在值不在枚举RoleEnum中", groups = {Insert.class, Update.class})
    private List<String> roles;
}

在 controller 中使用

如下,新增使用 Insert.class 规则,查询使用 Query.class 规则

Java 复制代码
package com.param_validate_demo.controller;


import com.param_validate_demo.groups.Insert;
import com.param_validate_demo.groups.Query;
import com.param_validate_demo.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/user")
public class UserController {

    @PostMapping("/add")
    @ResponseBody
    public String addUser(@Validated(Insert.class) @RequestBody User user) {
        return "新增成功";
    }

    @PostMapping("/query")
    @ResponseBody
    public String query(@Validated(Query.class) @RequestBody User user) {
        return "查询成功";
    }
}

自定义校验

有些时候框架提供的校验不满足当前业务,需要自己定义校验规则,比方上述user中的roles可以传入一些角色,但是这个角色必须在系统定义的枚举中,如下自定义一个校验枚举的校验器

① 新增注解
Java 复制代码
package com.param_validate_demo.annotation;

import com.param_validate_demo.validator.EnumValidator;

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

@Documented
@Constraint(validatedBy = EnumValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface EnumValidate {

    /**
     * 是否必填,默认必填
     */
    boolean required() default true;

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

    /**
     * 必要参数,否则报错
     */
    String message() default "";

    /**
     * 必要参数,否则报错
     * javax.validation.ConstraintDefinitionException: HV000074: com.param_validate_demo.annotation.EnumValidate contains Constraint annotation, but does not contain a groups parameter.
     */
    Class<?>[] groups() default { };

    /**
     * 必要参数,否则报错
     */
    Class<? extends Payload>[] payload() default {};

}
② 新增校验器
Java 复制代码
package com.param_validate_demo.validator;

import com.param_validate_demo.annotation.EnumValidate;
import com.param_validate_demo.enums.MyEnum;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class EnumValidator implements ConstraintValidator<EnumValidate, Object> {

    private boolean required;
    private Class<? extends Enum<?>> enumClass;

    @Override
    public void initialize(EnumValidate constraintAnnotation) {
        this.required = constraintAnnotation.required();
        this.enumClass = constraintAnnotation.enumClass();
    }

    @Override
    public boolean isValid(Object param, ConstraintValidatorContext constraintValidatorContext) {
        // 如果不需要校验,并且参数为空则不校验,直接放行,否则如果 required = false、param不是空也要校验
        if (!required && Objects.isNull(param)) {
            return true;
        }
        if (Objects.isNull(enumClass) || Objects.isNull(param)) {
            return false;
        }
        if (MyEnum.class.isAssignableFrom(enumClass)) {
            MyEnum[] myEnums = (MyEnum[]) enumClass.getEnumConstants();
            if (Objects.isNull(myEnums)) {
                return false;
            }
            List<String> codes = Arrays.stream(myEnums).map((MyEnum::getCode)).collect(Collectors.toList());
            if (param instanceof Collection) {
                Collection<?> collection = ((Collection<?>) param);
                return collection.stream().allMatch((Object o) -> codes.contains(o.toString()));
            } else if (param.getClass().isArray()) {
                return Arrays.stream((Object[]) param).allMatch((Object o) -> codes.contains(o.toString()));
            } else {
                // 单个参数
                return codes.stream().anyMatch((String item) -> Objects.equals(item, param));
            }
        }
        return false;
    }
}
枚举实现的接口如下
Java 复制代码
package com.param_validate_demo.enums;

public interface MyEnum {
    String getCode();
    String getDesc();
}
角色枚举
Java 复制代码
package com.param_validate_demo.enums;

public enum RoleEnum implements MyEnum {
    USER_MANAGE("user_manage", "用户管理员"), ORDER_MANAGE("order_manage", "订单管理员");
    private String code;
    private String desc;

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

    @Override
    public String getCode() {
        return this.code;
    }

    @Override
    public String getDesc() {
        return this.desc;
    }
}
性别枚举
Java 复制代码
package com.param_validate_demo.enums;

public enum GenderEnum implements MyEnum{
    MALE("male","男"),FEMALE("female","女");
    private String code;
    private String desc;

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

    @Override
    public String getCode() {
        return this.code;
    }

    @Override
    public String getDesc() {
        return this.desc;
    }
}
③ 使用注解
Java 复制代码
@EnumValidate(enumClass = GenderEnum.class, groups = {Insert.class, Update.class})
private String gender;

@EnumValidate(enumClass = RoleEnum.class, message = "入参roles中存在值不在枚举RoleEnum中", groups = {Insert.class, Update.class})
private List<String> roles;

参考:https://developer.aliyun.com/article/1670620

上述源代码:https://gitee.com/husong_zone/param_validate_demo

相关推荐
mct12310 分钟前
c++ iconv 字符utf-8转换gb2312失败
开发语言·c++
龙虾PRO2 小时前
能源与股票量化核心差异,AI全链路落地实操手册
java·面试·职场和发展
Marst Code2 小时前
Python 3.9 已停止维护!从 3.9 到 3.14 全版本深度对比,生产环境该选哪个?
开发语言·python
llwszx2 小时前
【Java/Go后端手撸原生Agent(第九篇):Plan-and-Execute规划模式——从“走一步看一步“到“先谋后动“】
java·python·golang·agent开发·plan模式·规划执行模式
ZJH__GO2 小时前
网络编程v2--多客户端互通
java·运维·服务器·开发语言·计算机网络
霸道流氓气质2 小时前
SpringBoot中基于 AES-GCM + KMS 密钥管理的数据加解密 Starter 实践
java·数据库·spring boot
Gofarlic_OMS2 小时前
NX浮动许可调度黑名单机制,对比两款谁更合理
java·大数据·运维·开源·制造
万岳科技系统开发2 小时前
智慧医院小程序开发推动医疗服务流程全面线上化
大数据·开发语言·人工智能
一只小灿灿2 小时前
C++ 修饰符全面详解
开发语言·c++
马里马里奥-2 小时前
从零搭建AI Agent工具链的技术文章大纲
开发语言·qt