前言
@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束。
这个技术一直都想进行梳理了很多复杂校验不会使用,本章节用来进行梳理
validated 用于参数的校验,支持简单和复杂(分组/嵌套)校验
@Validated / @Valid 的区别
功能
@validated 提供了分组能力而@Valid没有
位置
@Valid作用于 方法\构造方法\方法参数\成员属性
@Validdated作用于 类\方法\方法参数
注意事项
可能不生效的几个原因:
1. @validate 默认将所有校验加入到了Default组中,如果指定了自定义组将不会校验Default组中的校验,如果需要校验默认组需要加上Default组,面对复杂的校验很有可能会搞混
2. 如果需要使用分组,那么定义分组类型需要是接口类型,否则会报错~~
3. 如果使用了正则校验查看 正则是否正确
注解
逻辑图
Validatad 工作机制描述
调用者发送请求后端 -> Validatad拦截器将请求体进行拦截后校验
-> 校验成功 -> 通过
-> 校验失败 -> 抛出异常 -> 对异常进行处理(通过@RestControllerAdvice + @ExceptionHandler 注解进行异常处理)
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
校验
目前有关于商品的接口需要进行校验
异常的处理
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
@RestControllerAdvice
public class MyControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public String exceptionHandler(MethodArgumentNotValidException exception) {
BindingResult result = exception.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
if (errors != null) {
errors.forEach(p -> {
FieldError fieldError = (FieldError) p;
stringBuilder.append(fieldError.getDefaultMessage());
});
}
}
return stringBuilder.toString();
}
}
简单校验
目前商品添加了一个新增接口需要进行校验
校验内容:
商品的名称不能为空
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
@Data
@Accessors(chain = true)
public class MyTestDTO {
@NotEmpty(message = "产品名称不能为空")
private String product;
}
@RestController
@RequestMapping("api")
public class MyController {
@PostMapping("addProduct")
public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {
return myTestDTO;
}
}
分组校验
分组: 如果在Controller中设置了分组,那么校验的时候只会走对应的分组拦截器,默认没有分组的校验都在javax.validation.groups.Default.class组中
目前关于商品又增加了一个删除的接口原限制不变
新增接口:
产品名称不能为空
修改接口:
产品名称长度不能超过10位
//定义分组接口
public interface Group1 { //新增商品的分组
}
public interface Group2 { //修改商品的分组
}
import com.chen.advice.Group1;
import com.chen.advice.Group2;
import com.chen.pojo.MyTestDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api")
public class MyController {
@PostMapping("addProduct")
public MyTestDTO addProduct(@RequestBody @Validated(Group1.class) MyTestDTO myTestDTO) {
return myTestDTO;
}
@PutMapping("putProduct")
public MyTestDTO putProduct(@RequestBody @Validated(Group2.class) MyTestDTO myTestDTO) {
return myTestDTO;
}
}
import com.chen.advice.Group1;
import com.chen.advice.Group2;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
@Data
@Accessors(chain = true)
public class MyTestDTO {
@NotEmpty(message = "产品名称不能为空",groups = Group1.class)
@Size(max = 10,message = "产品名称长度不能超过10位",groups = Group2.class)
private String product;
}
嵌套校验
注意事项:
嵌套校验: 对象中的对象属性需要加上@valid的注解
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Data
@Accessors(chain = true)
public class MyTestDTO {
@Size(max = 10,message = "产品名称长度不能超过10位")
private String product;
@Valid
@NotNull(message = "医疗实体类不能为null")
private YiLiaoDTO yiLiaoDTO;
}
import lombok.Data;
import javax.validation.constraints.Positive;
@Data
public class YiLiaoDTO {
@Positive(message = "产品数量必须为正整数")
private Integer count;
}
import com.chen.pojo.MyTestDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api")
public class MyController {
@PostMapping("addProduct")
public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {
return myTestDTO;
}
}
注解的尝试
自定义注解校验
注解类 -- 模仿自带的注解类进行编写
@Constraint(validateBy = 指定配置类)
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyValidCheck.class)
public @interface MyValid {
int min() default 0;
String message() default "{javax.validation.constraints.Size.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
配置类
initalize() 初始化方法用于获取注解设置的值
isValid() 判断是否通过校验
import com.example.demo.annotation.MyValid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MyValidCheck implements ConstraintValidator<MyValid,Object> {
int min ;
@Override
public void initialize(MyValid constraintAnnotation) {
min = constraintAnnotation.min();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
if (o != null && o.toString().length() <= min) {
return true;
}
return false;
}
}
Controller类
@RestController
@RequestMapping("api")
public class MyController {
@PostMapping("addProduct")
public MyTestDTO addProduct(@RequestBody @Validated MyTestDTO myTestDTO) {
return myTestDTO;
}
}
import com.example.demo.annotation.MyValid;
import lombok.Data;
@Data
public class MyTestDTO {
private String id;
private String product;
@MyValid(min = 10,message = "你小子不按规则走呀,输入10位以内")
private Integer size;
}
正则校验
@Pattern(regexp = "^[A-Za-z0-9]+$",message = "不符合正则规定了哦小伙子")
private String name;
特殊校验
校验List<对象>
// 自行封装一个 List 没有经过尝试
@***Mapping("/**")
public *** apiName(@RequestBody @Validated(Add.class) ValidList<Bean> aObject)
package com.analog.greatbolderserver.config.valid;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.Valid;
import java.util.*;
/**
* @ClassName ValidList
* @Description ValidList
* @Author TY
* @Date 2020/8/26 16:05
* @Version 1.0
**/
@Data
@NoArgsConstructor
public class ValidList<E> implements List<E> {
@Valid
private List<E> list = new LinkedList<>();
public ValidList(List<E> paramList) {
this.list = paramList;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(0);
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(E e) {
return list.add(e);
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
public E set(int index, E element) {
return list.set(index, element);
}
@Override
public void add(int index, E element) {
list.add(index, element);
}
@Override
public E remove(int index) {
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
@Override
public ListIterator<E> listIterator() {
return list.listIterator();
}
@Override
public ListIterator<E> listIterator(int index) {
return list.listIterator(index);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
return list.subList(fromIndex, toIndex);
}
}