IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> @Valid和@Validated -> 正文阅读

[Java知识库]@Valid和@Validated

简介

@Validation是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置Validation可以很轻松的完成对数据的约束,配合BindingResult可以直接提供参数验证结果

所有参数注解含义

在这里插入图片描述
参考
@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated

区别

javax.validation.Valid.@Valid

① 首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@Min,如下代码(age属于Girl类中的属性):

@Min(value = 18,message = "未成年禁止入内")  
private Integer age;  

② 其次在controller层的方法的要校验的参数上添加@Valid注解,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息,如下代码:

@PostMapping("/girls")  
public Girl addGirl(@Valid Girl girl, BindingResult bindingResult) {  
    if(bindingResult.hasErrors()){  
        System.out.println(bindingResult.getFieldError().getDefaultMessage());  
        return null;  
    }  
    return girlResposity.save(girl);  
}  

bindingResult.getFieldError.getDefaultMessage()用于获取相应字段上添加的message中的内容,如:@Min注解中message属性的内容

javax.validation.@Validated

@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Valid不提供分组功能

分组

当一个实体类需要多种验证方式时,例:对于一个实体类的id来说,新增的时候是不需要的,对于更新时是必须的。 可以通过groups对验证进行分组

package com.valid.pojo;  
  
import javax.validation.constraints.Size;  
import org.hibernate.validator.constraints.NotEmpty;  
  
import com.valid.interfaces.First;  
  
public class People {  
      
    //在First分组时,判断不能为空  
    @NotEmpty(groups={First.class})  
    private String id;  
      
    //name字段不为空,且长度在3-8之间  
    @NotEmpty  
    @Size(min=3,max=8)  
    private String name;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
    
	public interface First {}
}  
(1)不分配groups,默认每次都要进行验证

(2)对一个参数需要多种验证方式时,也可通过分配不同的组达到目的。例:

@NotEmpty(groups={First.class})  
@Size(min=3,max=8,groups={Second.class})  
private String name; 
@Controller  
public class FirstController {  
      
    @RequestMapping("/addPeople")  
    //不需验证ID  
    public @ResponseBody String addPeople(@Validated People p,BindingResult result) {  
        ...
    }  
      
    @RequestMapping("/updatePeople")  
    //需要验证ID  
    public @ResponseBody String updatePeople(@Validated({First.class}) People p,BindingResult result) {  
        ...
    }  
}  

@Validated没有添加groups属性时,默认验证没有分组的验证属性,如该例子:People的name属性。如果所有参数的验证类型都设置了分组(即本例中People的name的@NotEmpty@Size都添加groups属性),则不验证任何参数

注意

controller 接口中使用 @Validated@Validated(groups = A.class) 设置了 groups 属性,则只有 配置了 groups = A.class 的效验注解会生效,没有配置的全不生效
@Validated  没有设置 groups 属性,则所有效验注解都会生效

controller中,不使用@Valid注解,而是要使用@Validated,里面value代表的是,在User类里面@NotNull注解里面配置了groups里面有TestNotNull.class的字段判断会生效
那么当前配置的话,就只会判断username是否为空,而password因为没有配置同样的groups属性,所以不会生效

@GetMapping("test")
public Result test(@Validated(value = {TestNotNull.class}) User user) {
    System.out.println("测试@notNull注解");
    //验证密码操作省略
    return Result.suc();
}

在这里插入图片描述这里没有传password,代码没有抛异常,说明@Validated注解不会判断groups属性没有当前class的注解,再试一下不传username会不会抛异常
在这里插入图片描述
这里没有传username,返回的是用户名不能为空,说明配置成功了,如果以后开发中,在多个接口中有不同的判断体系,可以用groups的方式分组

验证多个对象

一个功能方法上处理多个模型对象时,需添加多个验证结果对象

@Controller  
public class FirstController {  
      
    @RequestMapping("/addPeople")  
    public @ResponseBody String addPeople(@Validated People p,BindingResult result,@Validated Person p2,BindingResult result2) {  
        ...
    }  
}  

总结

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<?>[] value() default {};
}
@Valid:没有分组的功能。

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

嵌套验证

public class Item {
    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "至少要有一个属性")
    private List<Prop> props;
}

Item带有很多属性,属性Prop里面有属性id,属性值vid,属性名和属性值,如下所示:

public class Prop {

    @NotNull(message = "pid不能为空")
    @Min(value = 1, message = "pid必须为正整数")
    private Long pid;

    @NotNull(message = "vid不能为空")
    @Min(value = 1, message = "vid必须为正整数")
    private Long vid;

    @NotBlank(message = "pidName不能为空")
    private String pidName;

    @NotBlank(message = "vidName不能为空")
    private String vidName;
}

属性Prop这个实体也有自己的验证机制,比如属性和属性值vid不能为空,属性名和属性值不能为空等。
现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示:

@RestController
public class ItemController {

    @RequestMapping("/item/add")
    public void addItem(@Validated Item item, BindingResult bindingResult) {
        doSomething();
    }
}

如果Item实体的props属性不额外加注释只有@NotNull和@Size,无论入参采用@Validated还是@Valid验证,Spring Validation框架只会对Item的id和props做非空和数量验证不会对props字段里的Prop实体进行字段验证

也就是@Validated@Valid加在方法参数前,都不会自动对参数进行嵌套验证

也就是说如果传的List<Prop>中有Prop的pid为空或者是负数,入参验证不会检测出来。

为了能够进行嵌套验证,必须手动Item实体的props字段上 明确指出这个字段里面的实体也要进行验证

所以 
request  中的参数 尽量不要是其他的 dto , 可以是 enum ,如果必须用其他 dto,可以作为 静态内部类 

由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且@Valid类注解上也说明了它支持嵌套验证功能

那么我们能够推断出:

@Valid加在方法参数时并不能够自动进行嵌套验证
而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated@Valid来进行嵌套验证
public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private List<Prop> props;
}

然后我们在ItemControlleraddItem函数上再使用@Validated或者@Valid,就能对Item的入参进行嵌套验证。此时Item里面的props如果含有Prop的相应字段为空的情况,Spring Validation框架就会检测出来bindingResult就会记录相应的错误

好的实现方式

List<包装类>包装类的效验

@Data
@ApiModel
public class xxx {

    @ApiModelProperty("xxx")
    @Size(max = 100)
    @NotEmpty
    private List<@NotBlank String> xxx;
}

自定义封装全局异常类

package com.example.apps.advice;
 
import com.example.apps.result.ServiceResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
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 javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
 
@RestControllerAdvice
public class GlobalControllerAdvice {
 
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ServiceResult errorHandler(ConstraintViolationException ex) {
        ServiceResult serviceResult = new ServiceResult(400);
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if (!CollectionUtils.isEmpty(constraintViolations)) {
            StringBuilder stringBuilder = new StringBuilder();
            for (ConstraintViolation constraintViolation : constraintViolations) {
                stringBuilder.append(constraintViolation.getMessage()).append(",");
            }
            String errorMessage = stringBuilder.toString();
            if (errorMessage.length() > 1) {
                errorMessage = StringUtils.removeEnd(errorMessage, ",");
                serviceResult.setMessage(errorMessage);
                return serviceResult;
            }
        }
        serviceResult.setMessage(ex.getMessage());
        return serviceResult;
    }
 
 
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ServiceResult errorHandler(MethodArgumentNotValidException ex) {
        ServiceResult serviceResult = new ServiceResult(400);
        List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
        if(!CollectionUtils.isEmpty(objectErrors)) {
            StringBuilder builder = new StringBuilder();
            for (ObjectError objectError : objectErrors) {
                builder.append(objectError.getDefaultMessage()).append(",");
            }
            String errorMessage = builder.toString();
            if (errorMessage.length() > 1) {
                errorMessage = StringUtils.removeEnd(errorMessage,",");
            }
            serviceResult.setMessage(errorMessage);
            return serviceResult;
        }
        serviceResult.setMessage(ex.getMessage());
        return serviceResult;
    }
}

对Controller里的方法的多个参数进行校验

Controller里的方法的多个参数进行校验(扁平化参数):在Controller类上加注解@Validated

@RestController
@RequestMapping
@Validated
public class HelloController {
    @PutMapping("/hello/id/{id}/status/{status}")
    public Object helloGet(@Max(5) @PathVariable Integer id, @Min(5) @PathVariable Integer status) {
        return "hello world";
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 11:39:48  更:2022-04-28 11:43:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:46:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码