业务场景:
同一个接口,不同的操作
的时候,校验的字段是不同的。比如更改状态,对于请假申请审批和撤销,校验的字段不同的时候。
但是呢,常规的通过@valid的注解是无法满足的(请求的时候参数拦截,触发校验规则)。而我的业务场景必须进行手动触发@valid校验
废话不多说,下面直接展示思路与核心相关代码
目录
1.设计思路
本质来说,常规@Valid不满足需求的原因就是:
1.同一个接口,根据type字段来进行触发不同的分组字段校验;
2.手动触发valid分组校验。
补充:我所说的常规@valid是指@Validated(value = {People.Student.class}),代码如下:
/**
* <p>@Description: 请求拦截-根据分组进行参数校验 </p >
* <p>@param [people]</p >
* <p>@return com.lzq.learn.domain.CommonResult</p >
* <p>@throws </p >
* <p>@date 14:37 14:37</p >
*/
@PostMapping("validByGroup")
public CommonResult validByGroup(@RequestBody @Validated(value = {
People.Student.class}) People people) {
System.out.println("People 参数:" + people);
CommonResult commonResult = CommonResult.success(people);
return commonResult;
}
2.完整代码展示
2.1 controller代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.lzq.learn.domain.CommonResult;
import com.lzq.learn.domain.People;
import com.lzq.learn.domain.User;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import javax.validation.*;
import javax.validation.groups.Default;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping("valid")
public class TestValidController {
@Autowired
private Validator validator;
/**
* <p>@Description: 测试手动触发valid分组校验</p >
* <p>@param [userList]</p >
* <p>@return com.lzq.learn.domain.CommonResult</p >
* <p>@throws </p >
* <p>@date 15:21 15:21</p >
*/
@PostMapping("validByHand")
public CommonResult validByHand(@RequestBody People people) {
System.out.println("people 参数:" + people);
String validResult = "";
List<Class> groupList = CollUtil.newArrayList(Default.class);
if (StrUtil.equals("student", people.getType())) {
groupList.add(People.Student.class);
}
if (StrUtil.equals("teacher", people.getType())) {
groupList.add(People.Teacher.class);
}
Class[] groupArray = groupList.toArray(new Class[groupList.size()]);
// 写法1:使用ValidatorFactory 获取Validator对象
Set<ConstraintViolation<People>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(people, groupArray);
// 写法2:从springboot上下文 获取Validator对象
// Set<ConstraintViolation<People>> violations = validator.validate(people, groupArray);
validResult = getResultFromValidate(violations);
CommonResult commonResult = CommonResult.success(validResult);
return commonResult;
}
/**
* <p>@Description: 对于valid校验结果进行转换结果</p >
* <p>@param </p >
* <p>@return </p >
* <p>@throws </p >
* <p>@date 13:43 13:43</p >
*/
public static <T> String getResultFromValidate(Set<ConstraintViolation<T>> violations) {
if (CollUtil.isEmpty(violations)) {
return "pass";
}
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<T> violation : violations) {
sb.append(violation.getPropertyPath()).append(": ").append(violation.getMessage()).append("\n");
}
return sb.toString();
}
}
注意:
我们需要手动触发,则在controller的接口中,不能加上@valid(或@validated)注解,不然又会走请求拦截,根本走不到我们的controller代码中!
2.2 实体类People代码
import lombok.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* @author x
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class People implements Serializable {
/** 自定义校验分组 **/
public interface Student{
}
public interface Teacher{
}
private Long id;
@NotNull(message = "年龄不能为空")
private Integer age;
@NotBlank(message = "个人名称不能为空")
private String myName;
@NotBlank(message = "学生名称不能为空", groups = Student.class)
private String studentName;
@NotBlank(message = "教师名称不能为空", groups = Teacher.class)
private String teacherName;
// dto 字段
private String type;
}
注意:对于本实体类来说,age和myName字段没有指定分组,其实它们两个被指定到了
Default.class
分组(javax.validation.groups.Default)
3.postMan接口测试
为了简要概述,本文只针对于type=student的情况进行测试分析,按照上述代码,当type=student的时候,会进行Default和Student的分组校验,如下图postman接口测试
3.1 type=student,不传default分组字段
3.1 type=student,不传student分组字段
3.1 type=student,不传teacher分组字段
3.1 type=student,全部分组字段都传
如图,发现我们编写的代码是正确的,实现了在
任意代码执行顺序
中,根据type来进行自定义分组校验。但是controller接口一定不要加@valid或@validated注解,不然请求会被拦截
,根本走不到我们的自定义方法处。
本博客也只是更改了valid参数校验的执行时机,其实上述代码只是提供最基本的解决方案,后续可以优化为在方法上加上自定义注解,实现aop参数校验拦截!抽象出来这种行为,避免重复的if else代码冗余。