Spring Boot参数校验以及分组校验的使用

简介: 做web开发基本上每个接口都要对参数进行校验,如果参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句。虽然这种方式简单直接,但会大大降低开发效率和代码可读性。所以我们可以使用validator组件来代替我们进行不必要的coding操作。本文将基于validator的介绍资料,同时结合作者自己在项目中的实际使用经验进行了总结。

image.png

作者 | 江岩
来源 | 阿里技术公众号

一 前言

做web开发有一点很烦人就是要对前端输入参数进行校验,基本上每个接口都要对参数进行校验,比如一些非空校验、格式校验等。如果参数比较少的话还是容易处理的一但参数比较多了的话代码中就会出现大量的if-else语句。

使用这种方式虽然简单直接,但是也有不好的地方,一是降低了开发效率,因为我们需要校验的参数会存在很多地方,并且不同地方会有重复校验,其次降低了代码可读性,因为在业务代码中掺杂了太多额外工作的代码。

所以我们可以使用validator组件来代替我们进行不必要的coding操作。本文基于validator的介绍资料,也结合自己在项目中的实际使用经验进行了总结,希望能帮到大家。

1 什么是validator

Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本 。需要注意的是,JSR只是一项标准,它规定了一些校验注解的规范,但没有实现,比如@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。而hibernate validator是对这个规范的实现,并增加了一些其他校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。

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

image.png

二 注解介绍

1 validator内置注解

image.png

hibernate validator中扩展定义了如下注解:

image.png

三 使用

使用起来比较简单,都是使用注解方式使用。具体来说分为单参数校验、对象参数校验,单参数校验就是controller接口按照单参数接收前端传值,没有封装对象进行接收,如果有封装对象那就是对象参数校验。

1 单参数校验

单参数校验只需要在参数前添加注解即可,如下所示:

public Result deleteUser(@NotNull(message = "id不能为空") Long id) {// do something
}

但有一点需要注意,如果使用单参数校验,controller类上必须添加@Validated注解,如下所示:

@RestController
@RequestMapping("/user")
@Validated // 单参数校验需要加的注解
public class UserController {// do something
}

2 对象参数校验

对象参数校验使用时,需要先在对象的校验属性上添加注解,然后在Controller方法的对象参数前添加@Validated 注解,如下所示:

public Result addUser(@Validated UserAO userAo) {// do something
}public class UserAO {@NotBlankprivate String name;@NotNullprivate Integer age;……
}

注解分组

在对象参数校验场景下,有一种特殊场景,同一个参数对象在不同的场景下有不同的校验规则。比如,在创建对象时不需要传入id字段(id字段是主键,由系统生成,不由用户指定),但是在修改对象时就必须要传入id字段。在这样的场景下就需要对注解进行分组。

1)组件有个默认分组Default.class, 所以我们可以再创建一个分组UpdateAction.class,如下所示:

public interface UpdateAction {
}

2)在参数类中需要校验的属性上,在注解中添加groups属性:

public class UserAO {@NotNull(groups = UpdateAction.class, message = "id不能为空")private Long id;@NotBlankprivate String name;@NotNullprivate Integer age;……
}

如上所示,就表示只在UpdateAction分组下校验id字段,在默认情况下就会校验name字段和age字段。

然后在controller的方法中,在@Validated注解里指定哪种场景即可,没有指定就代表采用Default.class,采用其他分组就需要显示指定。如下代码便表示在addUser()接口中按照默认情况进行参数校验,在updateUser()接口中按照默认情况和UpdateAction分组对参数进行共同校验。

public Result addUser(@Validated UserAO userAo) {// do something
}
public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserAO userAo) {// do something
}

对象嵌套

如果需要校验的参数对象中还嵌套有一个对象属性,而该嵌套的对象属性也需要校验,那么就需要在该对象属性上增加@Valid注解。

public class UserAO {@NotNull(groups = UpdateAction.class, message = "id不能为空")private Long id;@NotBlankprivate String name;@NotNullprivate Integer age;@Validprivate Phone phone;……
}public class Phone {@NotBlankprivate String operatorType;@NotBlankprivate String phoneNum;
}

3 错误消息的捕获

参数校验失败后会抛出异常,我们只需要在全局异常处理类中捕获参数校验的失败异常,然后将错误消息添加到返回值中即可。捕获异常的方法如下所示,返回值Result是我们系统自定义的返回值类。

@RestControllerAdvice(basePackages= {"com.alibaba.dc.controller","com.alibaba.dc.service"})
public class GlobalExceptionHandler {@ExceptionHandler(value = {Throwable.class})Result handleException(Throwable e, HttpServletRequest request){// 异常处理}
}

需要注意的是,如果缺少参数抛出的异常是MissingServletRequestParameterException,单参数校验失败后抛出的异常是ConstraintViolationException,get请求的对象参数校验失败后抛出的异常是BindException,post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException,不同异常对象的结构不同,对异常消息的提取方式也就不同。如下图所示:

1)MissingServletRequestParameterException

if(e instanceof MissingServletRequestParameterException){Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);String msg = MessageFormat.format("缺少参数{0}", ((MissingServletRequestParameterException) e).getParameterName());result.setMessage(msg);return result;
}

2)ConstraintViolationException异常

if(e instanceof ConstraintViolationException){// 单个参数校验异常Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) e).getConstraintViolations();if(CollectionUtils.isNotEmpty(sets)){StringBuilder sb = new StringBuilder();sets.forEach(error -> {if (error instanceof FieldError) {sb.append(((FieldError)error).getField()).append(":");}sb.append(error.getMessage()).append(";");});String msg = sb.toString();msg = StringUtils.substring(msg, 0, msg.length() -1);result.setMessage(msg);}return result;
}

3)BindException异常

if (e instanceof BindException){// get请求的对象参数校验异常Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);List<ObjectError> errors = ((BindException) e).getBindingResult().getAllErrors();String msg = getValidExceptionMsg(errors);if (StringUtils.isNotBlank(msg)){result.setMessage(msg);}return result;
}
private String getValidExceptionMsg(List<ObjectError> errors) {if(CollectionUtils.isNotEmpty(errors)){StringBuilder sb = new StringBuilder();errors.forEach(error -> {if (error instanceof FieldError) {sb.append(((FieldError)error).getField()).append(":");}sb.append(error.getDefaultMessage()).append(";");});String msg = sb.toString();msg = StringUtils.substring(msg, 0, msg.length() -1);return msg;}return null;
}

4)MethodArgumentNotValidException异常

if (e instanceof MethodArgumentNotValidException){// post请求的对象参数校验异常Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);List<ObjectError> errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();String msg = getValidExceptionMsg(errors);if (StringUtils.isNotBlank(msg)){result.setMessage(msg);}return result;
}

原文链接
本文为阿里云原创内容,未经允许不得转载。

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

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

相关文章

长文解析:作为容器底层技术的半壁江山, cgroup如何突破并发创建瓶颈?

简介&#xff1a; io_uring 作为一种新型高性能异步编程框架&#xff0c;代表着 Linux 内核未来的方向&#xff0c;当前仍处于快速发展中。阿里云联合 InfoQ 发起《io_uring 介绍及应用实践》的技术公开课&#xff0c;围绕 OpenAnolis 龙蜥社区 Anolis OS 8 全方位解析高性能存…

Orion:谷歌的新一代SDN控制器

作者 | 魏煌松来源 | 鲜枣课堂时至今日&#xff0c;谷歌在2015年公布的成果&#xff0c;“利用SDN将广域网带宽利用率提升至接近100%”&#xff0c;仍然是SDN的一个标杆案列&#xff0c;也是难以逾越的巅峰。但事实上&#xff0c;当时使用的SDN控制器Onix&#xff0c;早已退出了…

移动云正式发布基于龙蜥 Anolis OS 的 BC-Linux V8.2 通用版操作系统

简介&#xff1a; 2020年12月CentOS项目组宣布CentOS 8将于2021年12月31日结束支持&#xff0c;这意味着从2022年开始&#xff0c;使用CentOS 8的用户&#xff0c;将无法得到来自官方的新硬件支持、bug修复和安全补丁。针对这一情况&#xff0c;移动云大云操作系统团队基于国内…

干掉讨厌的 CPU 限流,让容器跑得更快

简介&#xff1a; 让人讨厌的 CPU 限流影响容器运行&#xff0c;有时人们不得不牺牲容器部署密度来避免 CPU 限流出现。本文介绍的 CPU Burst 技术可以帮助您既能保证容器运行服务质量&#xff0c;又不降低容器部署密度。文章分为上下两篇&#xff0c;该文为上篇&#xff0c;下…

微弱信号检测_机动车检测线常用传感器介绍

机动车检测线中经常会运用到各种传感器&#xff0c;这些传感器相当于车辆检测系统的“眼睛”、“鼻子”和“耳朵”&#xff0c;通过台体装置和装在台体中的传感器&#xff0c;能够把车辆的性能数据转换成计算机系统能够识别的信号&#xff0c;供计算机处理和计算&#xff0c;最…

赋能开发者,英特尔发布oneAPI 2022工具包

英特尔发布了oneAPI 2022工具包。此次发布的最新增强版工具包扩展了跨架构开发的特性&#xff0c;为开发者提供更强的实用性和更丰富的架构选择&#xff0c;用以加速计算。 英特尔公司首席技术官、高级副总裁、软件和先进技术事业部总经理 Greg Lavender表示&#xff1a;“我十…

Quick BI V4.0功能“炸弹”来袭,重磅推出即席分析、模板市场、企业微信免密登录等强势功能

简介&#xff1a; 2021年7月&#xff0c;Quick BI公共云版本迭代新功能&#xff1a;重磅推出即席分析、模板市场&#xff0c;分析门槛再降低&#xff1b;推出企业微信无缝对接&#xff0c;移动端类目个性配置及管理提升多端能力&#xff1b;数据建模配置交互升级至拖拽模式提升…

打印速度快点的打印机_瞒着领导偷偷给你们发两台打印机

前几次小粉笔组织的活动都被“投诉”&#xff01;说我们打印机太少~小粉笔心领神会&#xff0c;在这个月的活动预算费用上悄咪咪加了【两台打印机】~(看小粉笔多疼你们&#xff01;)希望知道的笔芯不要把这条推文转发给我领导(要不然你们以后就没有打印机了~哼&#xff01;)现在…

数据库误操作后悔药来了:AnalyticDB PostgreSQL教你实现分布式一致性备份恢复

简介&#xff1a; 本文将介绍AnalyticDB PostgreSQL版备份恢复的原理与使用方法。 一、背景 AnalyticDB PostgreSQL版&#xff08;简称ADB PG&#xff09;是阿里云数据库团队基于PostgreSQL内核&#xff08;简称PG&#xff09;打造的一款云原生数据仓库产品。在数据实时交互式…

与变异风险词赛跑 阿里探索AI治理网络风险

最近&#xff0c;阿里安全一线风控小二可粒发现&#xff0c;在禁售的风险防控库里&#xff0c;有人试图“上新”新品种&#xff0c;不法份子借助在社交媒体上走红的“魔法改运”等说辞&#xff0c;引人入玄学骗局。 尽量提前发现风险问题&#xff0c;提早布防是阿里安全风控部…

高效研发运维体系构建的流程和方法论

简介&#xff1a; 云计算产品大多都会与云原生发生关联&#xff0c;云原生正在重塑整个软件的生命周期。但到底什么是云原生&#xff1f;云原生带来的最大技术创新和未来机会是什么&#xff1f;围绕云原生&#xff0c;是否可以构建出一套云上的开发&运维体系&#xff0c;打…

Colima:MacOS 上的极简容器运行时和 Kubernetes

作者 | Addo Zhang来源 | 云原生指北Colima 是一个以最小化设置来在MacOS上运行容器运行时和 Kubernetes 的工具。支持 m1&#xff0c;同样也支持 Linux。Colima 的名字取自 Container on Lima。Lima 是一个虚拟机工具&#xff0c;可以实现自动的文件共享、端口转发以及 contai…

当容器应用越发广泛,我们又该如何监测容器?

简介&#xff1a; 随着容器技术蓬勃发展与落地推行&#xff0c;越来越多企业的业务运行于容器中。作为主流部署方式之一&#xff0c;容器将团队的任务和关注点分割开&#xff0c;开发团队只需关注应用程序逻辑和依赖项&#xff0c;而运维团队只需关注部署和管理&#xff0c;无需…

内含福利|CSDN携手字节跳动:云原生Meetup北京站报名热烈启动,1月8日见!

伴随云原生技术的成熟与落地&#xff0c;越来越多框架、中间件等开源项目相继涌现&#xff0c;帮助开发者和企业有效解决业务问题。2022年1月8日&#xff0c;CSDN携手字节跳动基础架构&#xff0c;将在北京举办第四场云原生线下Meetup。在这里&#xff0c;您可以与众多开源技术…

Flink CDC 2.0 正式发布,详解核心改进

简介&#xff1a; 本文由社区志愿者陈政羽整理&#xff0c;内容来源自阿里巴巴高级开发工程师徐榜江 (雪尽) 7 月 10 日在北京站 Flink Meetup 分享的《详解 Flink-CDC》。深入讲解了最新发布的 Flink CDC 2.0.0 版本带来的核心特性&#xff0c;包括&#xff1a;全量数据的并发…

unity三维地图的经纬度如何在二维地图上表示_接入C++版本recastnavigation寻路库到Unity/服务端中...

前言因为Unity版本的更新迭代&#xff0c;老版本的A*插件在新版本Unity已经无法正常使用&#xff0c;包括一些运行时代码也已经过时&#xff0c;重新接入要花费很多时间&#xff0c;干脆接入一个新的寻路方案吧。这里选择的是久负盛名的https://github.com/recastnavigation/re…

Dataphin功能:集成——如何将业务系统的数据抽取汇聚到数据中台

简介&#xff1a; 数据集成是简单高效的数据同步平台&#xff0c;致力于提供具有强大的数据预处理能力、丰富的异构数据源之间数据高速稳定的同步能力&#xff0c;为数据中台的建设打好坚实的数据基座。 数据中台是当下大数据领域最前沿的数据建设体系, 它并不是从零开始, 无中…

5G专网,路在何方?

作者 | 蜉蝣采采来源 | 无线深海话说你平常打电话、刷视频、玩游戏的4G和5G&#xff0c;一般也被叫做“公网”。这个“公”字的含义正是公开&#xff0c;公用的意思。也就是说&#xff0c;这个网络&#xff0c;不但你能用&#xff0c;你隔壁的张三也能用&#xff0c;张三的老乡…

如何开发 Node.js Native Add-on?

简介&#xff1a; 来一起为 Node.js 的 add-on 生态做贡献吧~ 作者 | 吴成忠(昭朗)这篇文章是由 Chengzhong Wu (legendecas)&#xff0c;Gabriel Schulhof (gabrielschulhof) &#xff0c;Jim Schlight (jimschlight)&#xff0c;Kevin Eady&#xff0c;Michael Dawson (mhdaw…

xxl子任务_XXL-JOB v2.1.2 发布,分布式任务调度平台

v2.1.2 Release Notes1、方法任务支持&#xff1a;由原来基于JobHandler类任务开发方式&#xff0c;优化为支持基于方法的任务开发方式&#xff1b;因此&#xff0c;可以支持单个类中开发多个任务方法&#xff0c;进行类复用XxlJob("demoJobHandler")public ReturnT …