使用 spring-boot-starter-validation实现入参校验
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
定义分组
由于新增、更新、查询、删除都可以使用一个实体接受入参,而不同情况需要校验的情况也不同,比如,新增时不需要校验id必填,删除时需要校验id必填,分组可以理解成不同策略,不同分组下可以使用不同的校验规则
package com.param_validate_demo.groups;
import javax.validation.groups.Default;
public interface Insert extends Default {
}
定义校验规则
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 规则
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可以传入一些角色,但是这个角色必须在系统定义的枚举中,如下自定义一个校验枚举的校验器
① 新增注解
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 {};
}
② 新增校验器
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;
}
}
枚举实现的接口如下
package com.param_validate_demo.enums;
public interface MyEnum {
String getCode();
String getDesc();
}
角色枚举
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;
}
}
性别枚举
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;
}
}
③ 使用注解
@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;