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

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

一 前言

做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项目,需要添加如下依赖。

二 注解介绍

1 validator内置注解

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

三 使用

使用起来比较简单,都是使用注解方式使用。具体来说分为单参数校验、对象参数校验,单参数校验就是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 {
@NotBlank
private String name; @NotNull
private 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; @NotBlank
private String name; @NotNull
private 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; @NotBlank
private String name; @NotNull
private Integer age; @Valid
private Phone phone; ……
} public class Phone {
@NotBlank
private String operatorType; @NotBlank
private 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;
}

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

Spring Boot参数校验以及分组校验的使用的更多相关文章

  1. spring boot 参数传递(spring boot 参数传数 arg0 每一个参数 arg0#{arg0},arg1 #{arg1})

    spring boot 参数传数 arg0 每一个参数 arg0#{arg0},arg1  #{arg1} @Select("select * from sys_user where nam ...

  2. Spring Boot 参数校验

    1.背景介绍 开发过程中,后台的参数校验是必不可少的,所以经常会看到类似下面这样的代码 这样写并没有什么错,还挺工整的,只是看起来不是很优雅而已. 接下来,用Validation来改写这段 2.Spr ...

  3. Validated 注解完成 Spring Boot 参数校验

    1.  @Valid 和 @Validated @Valid 注解,是 Bean Validation 所定义,可以添加在普通方法.构造方法.方法参数.方法返回.成员变量上,表示它们需要进行约束校验. ...

  4. Spring Boot参数校验

    1. 概述 作为接口服务提供方,非常有必要在项目中加入参数校验,比如字段非空,字段长度限制,邮箱格式验证等等,数据校验常用到概念:JSR303/JSR-349: JSR303是一项标准,只提供规范不提 ...

  5. spring boot+自定义 AOP 实现全局校验

    最近公司重构项目,重构为最热的微服务框架 spring boot, 重构的时候遇到几个可以统一处理的问题,也是项目中经常遇到,列如:统一校验参数,统一捕获异常... 仅凭代码 去控制参数的校验,有时候 ...

  6. spring boot参数验证

    必须要知道 简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系 JSR303 是一项标准,JSR-349 是其的升级版本,添加 ...

  7. Spring Boot 进行优雅的字段校验

    Controller层 VS Service层 一般推荐与业务无关的放在Controller层中进行校验,而与业务有关的放在Service层中进行校验. 那么如何将参数校验写的优雅美观呢,如果都是if ...

  8. 记一次spring boot参数初始化的问题

    背景:接手一个项目,看到一个配置参数的引用: @Value("${webSocket.id}") 再看看配置application.yml: ... webSocket: id: ...

  9. Spring boot 参数相关注解

    最近使用swagger的在线文档调试接口时发现老是报参数问题,最后发现是方法中参数上的注解有问题,今天把填的坑做一下总结. 1. RequestParam 该注解有两个属性: name/value:表 ...

  10. spring boot输入数据校验(validation)

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

随机推荐

  1. NA嵌入Flutter页面

    目录介绍 01.Android承载flutter容器 02.过时的NA跳转flutter方案 03.升级版本NA跳转Flutter处理 04.如何处理NA跳转flutter传参 05.思考遇到的几个问 ...

  2. C++ Qt开发:QUdpSocket实现组播通信

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QUd ...

  3. Linux快速入门(一)Linux基础知识

    我的环境:Ubuntu(后面涉及到的Linux均为Debian系列) Linux不同发行版的包管理方式 (1)Redhat系列:Redhat系列的包管理方式采用的是基于RPM包的YUM包管理方式 (2 ...

  4. 【IOT安全】ASA5520基本知识和配置

    本文主要介绍ASA5520防火墙的基本知识和配置 环境搭建 Linux eveng 5.17.8-eve-ng-uksm-wg+ #1 SMP PREEMPT Mon May 16 10:08:59 ...

  5. KingbaseES V8R3 集群运维案例 --操作系统‘soft lockup’引起的failover切换

    案例说明: 在国产中标麒麟系统生产环境中,监控发现KingbaseES V8R3集群发生了failover的主备切换,客户需要给出分析报告,说明此次集群发生failover切换的原因,本次文档通过分析 ...

  6. 正则表达式 (?<= 与 (?= 的区别

    (?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串.这是一个非获取匹配, 也就是说,该匹配不需要获取供以后使用.例如,"Windows(?=95|98 ...

  7. 网站优化之robots.txt

    本文于2015年底完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. 在查询favicon.ico相关的资料时,无间中看到了robots.tx ...

  8. 【中秋国庆不断更】OpenHarmony定义可动画属性:@AnimatableExtend装饰器

    [中秋国庆不断更]OpenHarmony定义可动画属性:@AnimatableExtend装饰器 @AnimatableExtend装饰器用于自定义可动画的属性方法,在这个属性方法中修改组件不可动画的 ...

  9. 10. Conclusion

    10.1 Matrix Factorizations A = LU = (Lower triangular L with 1's on the diagonal)(Upper triangular U ...

  10. MOGDB/openGauss索引推荐及虚拟索引

    MOGDB/openGauss 索引推荐及虚拟索引 索引推荐 在 ORACLE 的优化中,可能大家有接触过 SQL Tuning Advisor(SQL 调优顾问,STA),类似的 MOGDB/ope ...