SpringBoot使用validator进行参数校验

news2024/11/27 21:02:12
  1. @Validated、@Valid和BindingResult

Bean Validation是Java定义的一套基于注解的数据校验规范,比如@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。

hibernate validator是对这个规范的实现,并增加了一些其他校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。

依赖

hibernate validator框架已经集成在 spring-boot-starter-web中,所以无需再添加其他依赖。如果不是Spring Boot项目,需要添加如下依赖。

@Valid和@Validated 区别

Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种)。

javax提供了@Valid,配合BindingResult可以直接提供参数验证结果(标准JSR-303规范)。

@Validation对@Valid进行了二次封装,在使用上并没有区别,但在分组、注解位置、嵌套验证等功能上有所不同

  • 分组

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。

@Valid:没有分组校验的功能。

  • 注解地方

@Validated:用在类型、方法和方法参数上(类, 方法, 参数)。但不能用于成员属性。

@Valid:可以用在方法、构造函数、方法参数和成员属性上(方法, 构造器, 参数,字段, 泛型),可以用@Valid实现嵌套验证

两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能
如A类中引用B类,且A、B二类都有内部校验,为了使B类也生效,在A类中引用B类时,在B类变量上加@Valid注解,如果B类为集合等类型且不能为空还需要再加@NotEmpty。

BindingResult

BindingResult用在实体类校验信息返回结果绑定。

该类作为方法入参,要写在实体对象后面。

@PostMapping("/menus")
public Result addMenu(@RequestBody @Valid Menu menu, BindingResult result) {}
  1. 规则注解

validator内置注解

hibernate validator扩展注解

分类

空与非空

注解

支持Java类型

说明

@Null

Object

为null

@NotNull

Object

不为null

@NotBlank

CharSequence

不为null,且必须有一个非空格字符

@NotEmpty

CharSequence、Collection、Map、Array

不为null,且不为空(length/size>0)

Boolean

注解

支持Java类型

说明

备注

@AssertTrue

boolean、Boolean

为true

为null有效

@AssertFalse

boolean、Boolean

为false

为null有效

日期

注解

支持Java类型

说明

备注

@Future

Date、

Calendar、

Instant、

LocalDate、

LocalDateTime、

LocalTime、

MonthDay、

OffsetDateTime、

OffsetTime、

Year、

YearMonth、

ZonedDateTime、

HijrahDate、

JapaneseDate、

MinguoDate、

ThaiBuddhistDate

验证日期为当前时间之后

为null有效

@FutureOrPresent

Date、

Calendar、

Instant、

LocalDate、

LocalDateTime、

LocalTime、

MonthDay、

OffsetDateTime、

OffsetTime、

Year、

YearMonth、

ZonedDateTime、

HijrahDate、

JapaneseDate、

MinguoDate、

ThaiBuddhistDate

验证日期为当前时间或之后

为null有效

@Past

Date、

Calendar、

Instant、

LocalDate、

LocalDateTime、

LocalTime、

MonthDay、

OffsetDateTime、

OffsetTime、

Year、

YearMonth、

ZonedDateTime、

HijrahDate、

JapaneseDate、

MinguoDate、

ThaiBuddhistDate

验证日期为当前时间之前

为null有效

@PastOrPresent

Date、

Calendar、

Instant、

LocalDate、

LocalDateTime、

LocalTime、

MonthDay、

OffsetDateTime、

OffsetTime、

Year、

YearMonth、

ZonedDateTime、

HijrahDate、

JapaneseDate、

MinguoDate、

ThaiBuddhistDate

验证日期为当前时间或之前

为null有效

数值

注解

支持Java类型

说明

备注

@Max

BigDecimal、BigInteger,

byte、short、int、long以及包装类

小于或等于

为null有效

@Min

BigDecimal、BigInteger,

byte、short、int、long以及包装类

大于或等于

为null有效

@DecimalMax

BigDecimalBigInteger、CharSequence,

byte、short、int、long以及包装类

小于或等于

为null有效

@DecimalMin

BigDecimal、BigIntegerCharSequence,

byte、short、int、long以及包装类

大于或等于

为null有效

@Negative

BigDecimal、BigInteger,

byte、short、int、long、float、double以及包装类

负数

为null有效,0无效

@NegativeOrZero

BigDecimal、BigInteger,

byte、short、int、long、float、double以及包装类

负数或零

为null有效

@Positive

BigDecimal、BigInteger,

byte、short、int、long、float、double以及包装类

正数

为null有效,0无效

@PositiveOrZero

BigDecimal、BigInteger,

byte、short、int、long、float、double以及包装类

正数或零

为null有效

@Digits(integer = 3, fraction = 2)

BigDecimal、BigInteger、CharSequence,

byte、short、int、long以及包装类

整数位数和小数位数上限

为null有效

@Length

String

字符串长度范围

@Length

@Range

数值类型和String

指定范围

@Range

其他

注解

支持Java类型

说明

备注

@Pattern

CharSequence

匹配指定的正则表达式

为null有效

@Email

CharSequence

邮箱地址

为null有效,默认正则 '.*'

@Size

CharSequence、Collection、Map、Array

大小范围(length/size>0)

为null有效

@URL

URL地址验证

@URL

  1. 使用

单参数校验

需要在参数前添加注解,而且controller类上必须添加@Validated注解。

@RestController
@RequestMapping("/menu")
@Validated // 单参数校验需要加的注解
public class SysMenuController {
    @DeleteMapping("/menus")
    public Result deleteMenu(@NotNull(message = "id不能为空") Long id) {
    }
}

对象参数校验

先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Valid、@Validated

// 对象
public class Menu {
    private Long menuId;
    @NotNull(message =parentId不能为空")
    private Long parentId;
}
@PostMapping("/menus")
public Result addMenu(@RequestBody @Valid Menu menu, BindingResult result) {
}

对象嵌套

// 对象
public class PagedQueryReqBody<T> {
    private Integer page_no;
    private Integer page_row_no;
    @NotNull
    private String page_flg;
    @Valid
    private T data_request;
}

public class DataReqPQ {
    @NotNull
    private String car_no;
}
// 接口
@PostMapping(value = "/queryParameter")
public Result queryParameter(@RequestBody @Validated PagedQueryReqBody<DataReqPQ> requestMsg, BindingResult result){
}

分组校验

新建组

Validated有自己默认的组 Default.class

public interface Update {
}
public interface Add  extends Default {
}
// 对象
public class User {
    @NotBlank(message = "id不能为空",groups = {Update.class})
    private String id;
    private String name;
    @NotBlank(message = "密码不能为空",groups = {Add.class})
    private String password;
}
id属性的校验属于Update分组的校验
password属性的校验属于Add、Default分组的校验

使用分组

使用默认分组:Add分组继承Default,所以校验password,不校验id

@PostMapping("/addUser")
public Resp addUser(@Validated @RequestBody User uer) {
}

使用Update分组:只校验id,不校验password

@PostMapping("/updateUser")
public Resp updateUser(@Validated(Update.class) @RequestBody User user) {
}
  1. 异常处理

全局异常处理类

缺少参数抛出的异常是MissingServletRequestParameterException

单参数校验失败后抛出的异常是ConstraintViolationException

get请求的对象参数校验失败后抛出的异常是BindException

post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException

不同异常对象的结构不同,对异常消息的提取方式也就不同。

@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)// 设置状态码为500
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String postExceptionHandler(MethodArgumentNotValidException e){
        log.error("执行异常",e);
         BindingResult exceptions = e.getBindingResult();
         if (exceptions.hasErrors()) {}
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)// 设置状态码为500
    @ExceptionHandler(ConstraintViolationException.class)
    public String paramExceptionHandler(ConstraintViolationException e){
        log.error("执行异常",e);
        
    }
}

BindingResult异常

Controller方法的中处理

@PostMapping("addUser")
public Result addUser(@RequestBody @Valid User user,BindingResult result){
    //校验到错误
    if (result.hasErrors()) {
    //获得错误信息列表
    List<String> errMsgs = result.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(toList());
    String lists = StringUtils.join(lists, ";");
    return new Result(“” "", lists);
    }
    return new Result(“”, "", null);
}

AOP校验

/**
*将此注解加在需要进行参数校验的方法上,
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamValid {
}
@Aspect
@Component
public class ParamValidAspect {
 
    private static final Logger log = LoggerFactory.getLogger(ParamValidAspect.class);
 
    @Before("@annotation(paramValid)")
    public void paramValid(JoinPoint point, ParamValid paramValid){
        Object[] paramObj = point.getArgs();
        if (paramObj.length > 0){
            Arrays.stream(paramObj).forEach(
                e ->{
                    if (e instanceof BindingResult) {
                        BindingResult result = (BindingResult) e;
                        Result errorMap = this.validRequestParams(result);
                        if (errorMap != null){
                            ServletRequestAttributes res = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                            HttpServletResponse response = res.getResponse();
                            response.setCharacterEncoding("UTF-8");
                            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                            response.setStatus(HttpStatus.BAD_REQUEST.value());

                            OutputStream output = null;
                            try {
                                output = response.getOutputStream();
                                String error = objectMapper.writeValueAsString(errorMap);
                                //响应错误信息
                                output.write(error.getBytes("UTF-8"));
                           }catch (IOException e){
                               log.error(e.getMessage());
                           }finally{
                               try{
                                   if (output != null){
                                       output.close();
                                   }
                               } catch (IOException e) {
                                   log.error(e.getMessage());
                               }
                          }
                      }
                  }
              });
          }
      }
 
    /**
     * 校验
     */
    private Result validRequestParams(BindingResult result) {
        if (result.hasErrors()) {
            List<String> errMsgs = result.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(toList());
            String lists = StringUtils.join(lists, ";");
            return new Result("", "", lists);
        }
        return null;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

业务流程建模标注(BPMN)详细介绍

1、基本信息摘要&#xff1a;该文章的目的是对BPMN(Business Process Modeling Notation)的概要描述和介绍。描述基本的BPMN符号&#xff0c;包括这些图元如何组合成一个业务流程图&#xff08;Business Process Diagram&#xff09;2、BPMN简介2.1概述该文章的目的是对BPMN(Bu…

pytest之fixture用法

特点及优势1、命令灵活&#xff1a;对于setup.teardown&#xff0c;可以不起这两个名字2、数据共享&#xff1a;在conftest.py配置里写的方法可以实现数据共享&#xff0c;不需要import导入&#xff0c;可以跨文件共享3、scope的层次及神奇的yield组合相当于各种setup和teardow…

MySQL —— 表的操作

文章目录1. 创建表2. 查看表结构3. 修改表3.1 向表中插入数据3.2删除表中的数据3.3 修改表的性质3.3.1 添加字段3.3.2 修改字段的长度3.3.3 删除字段3.3.4 修改字段名3.3.5 修改表名4. 删除表5. 备份表前言&#xff1a; 本文会详细的讲解&#xff0c;在MySQL中表的操作。1. 创建…

Linux基础命令-uname显示系统内核信息

前言 这个命令主要是显示系统内核的相关信息&#xff0c;一般需要查看内核信息才会使用到这个命令&#xff0c;一起来看看吧。 一 命令的介绍 uname命令来自于英文词组“Unix name”的缩写&#xff0c;其功能是用于查看系统主机名、内核及硬件架构等信息。如果不加任务参数&am…

UVM实战(张强)-- UVM中的寄存器模型

目录一.整体的设计结构图二.各个组件代码详解2.1 DUT2.2 bus_driver2.3 bus_sequencer2.4 bus_monitor2.5 bus_agent2.6 bus_transaction2.7 bus_if2.8 my_if2.9 my_transaction2.10 my_sequencer2.11 my_driver2.12 my_monitor2.13 my_agent2.14 my_scoreboard2.15 my_env2.16…

龙芯GS232(MIPS 32)架构cache管理笔记

1 mips32架构 MIPS架构是一种基于精简指令集&#xff08;Reduced Instruction Set Computer&#xff0c;RISC&#xff09;的计算机处理器架构。MIPS架构由MIPS Technologies公司在1981年开发&#xff0c;并在1984年发布了第一款MIPS处理器。 MIPS架构的特点包括&#xff1a; …

Alkyne choline,685082-61-5,炔基胆碱,炔基可通过铜催化的点击化学进行修饰和共轭

1、基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;685082-61-5中文名&#xff1a;炔胆碱&#xff0c;炔基胆碱英文名&#xff1a;Alkyne-choline &#xff0c;Alkyne choline2、详细产品数据&#xff08;Detailed Product Data&#xf…

深入讲解CFS组调度!(下)

接上文深入讲解CFS组调度&#xff01;&#xff08;上&#xff09; 六、task group时间片 6.1. 时间片分配 若使能CFS组调度会从上到下逐层通过权重比例来分配上层分得的时间片&#xff0c;分配函数是sched_slice()。但是从上到下不便于遍历&#xff0c;因此改为从下到上进行…

盘点全网好评最多的7款团队协同软件,你用过哪款?

能亲自带团队管理项目当然是一件开心和兴奋的事&#xff0c;但是突然成为团队负责人后开始不大适应。如何转换角色&#xff0c;还有自己和团队成员之间在心理、行为等方面的互动也变得很敏感。新手领导上任的过程&#xff0c;是团队秩序再造的过程&#xff1b;是晋升者个人职业…

Python----------字符串

1.转义字符 注&#xff1a;转义字符放在你所想效果字符前 2.原始字符串 print(r"D:\three\two\one\now") ->D:\three\two\one\now注&#xff1a; 在使用原始字符串时&#xff0c;转义字符不再有效&#xff0c;只能当作原始的字符&#xff0c;每个字符都没有特殊…

MySQL(一)基础使用

MySQL基础使用概念数据库相关概念关系型数据库SQL通用语法SQL分类DDL数据库操作表操作表操作-数据类型表操作-修改表操作-删除DML添加数据修改数据删除数据DQL基本查询&#xff08;不带任何条件&#xff09;查询多个字段&#xff1a;字段设置别名去除重复记录条件查询&#xff…

2月第3周榜单丨飞瓜数据B站UP主排行榜(哔哩哔哩平台)发布!

飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的B站UP主。飞瓜数据UP主充电榜排行榜&…

EasyExcel 几十万导入报错问题——java.lang.NoClassDefFoundError

EasyExcel 报错 NoClassDefFoundError org/ehcache/config/builders/CacheManagerBuilder 特此郑重声明&#xff01;该文章是原创作品&#xff0c;小编编写实属不易 &#xff0c;帮忙点赞关注一下~转载小伙伴请注明出处&#xff01; EasyExcel 导入几十万数据报错 今天在执行…

【java实现Word模板导出】Xdocreport和Freemaker

如果只是生成简单的word文件的话可以使用 Hutool 上手简单使用方便。 但如果需要导出内容比较复杂的word文件的话用那个就不合适了&#xff0c;这时候就需要Xdocreport这玩意了。 制作模板 新建一个word文档在需要插入变量的地方使用快捷键 Crtl F9 来生成一个域 然后右键单…

【软件工具】Source Insight 4.0编辑keil工程代码

0.前言 最近在学习过程中&#xff0c;发现诸多课程老师均使用Source Insight 4.0进行开发演示&#xff0c;为了方便课程的学习&#xff0c;也为了提高个人开发水平及效率&#xff0c;故学习Source Insight 4.0软件&#xff0c;此文章主要作为软件使用的流程总结&#xff0c;同…

第十一章 - 模糊匹配(like)、正则匹配(REGEXP)、文本处理函数、时间处理函数

第十一章 - 模糊匹配&#xff08;like&#xff09;、正则匹配&#xff08;REGEXP&#xff09;、文本处理函数、时间处理函数模糊匹配和正则匹配like%通配符_通配符REGEXP 正则匹配文本拼接concat&#xff08;&#xff09;substring()substring_index()一些文本处理函数时间处理…

Autosar MCAL-ADC配置PWM硬件触发采样

文章目录前言ADC配置AdcGroupRequestSourceAdcGroupTriggSrcAdcHwExtTrigSelectAdcHwGatePinAdcGeneral-AdcHwTriggerApiAdcHwGateSignalAdcHwTrigSignalAdcHwTrigTypeGtmGtmConnectionsPWM实际使用总结前言 在实际项目开发过程中&#xff0c;关于ADC采样&#xff0c;大部分使…

存储器与cpu的连接

1. 只读存储器 只读存储器中一般用于保存系统程序或者系统的配置信息&#xff1b; 早期的只读存储器&#xff0c; 在厂家就写好了内容改进&#xff11;&#xff0c; 用户可以自己写&#xff0c; 一次性改进2, 可以多次写-- 要能对信息进行擦除&#xff1b;改进3,  电可擦…

【Leetcode 剑指Offer】第3天 字符串(简单)

字符串剑指 Offer 05. 替换空格字符串构造函数和析构函数操作函数剑指 Offer 58 - II. 左旋转字符串剑指 Offer 05. 替换空格 题&#xff1a;实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。 class Solution { public:string replaceSpace(string s…

华为OD机试真题 用 C++ 实现 - 单词反转

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…