故事
首页
指南
  • Java
  • Python
  • Linux
  • 前端
  • Docker
  • 实践
  • 折腾
  • 分类
  • 标签
  • 归档
壁纸 (opens new window)
GitHub (opens new window)
首页
指南
  • Java
  • Python
  • Linux
  • 前端
  • Docker
  • 实践
  • 折腾
  • 分类
  • 标签
  • 归档
壁纸 (opens new window)
GitHub (opens new window)
  • Java基础

    • String类的深入学习
    • HashMap源码学习
    • Java8新特性回顾
  • Java框架

    • POI事件模式解析并读取Excel文件数据
    • SpringMVC的执行流程源码分析
    • netty+websocket实现即时通讯功能
    • 分布式定时任务解决方案xxl-job
    • spring-session实现集群session共享
    • springcloud优雅下线服务
    • Java日志发展历史
    • Mybatis Generator配置
    • JavaSPI机制和Springboot自动装配原理
    • Spring Cloud Config接入
    • 使用spring validation进行参数校验
      • 简单使用
      • 常用校验
      • 统一异常处理
      • 自定义校验规则
    • dubbo接口超时配置
    • ConfigurationProperties注解
    • EasyExcel导出excel
  • 中间件

    • 分布式锁解决方案
    • 关于消息中间件MQ
    • kafka学习记录
    • kakfa实践
    • kakfa重复消费问题处理
  • 数据库

    • MySql索引
    • SQL优化学习
    • Otter实现数据全量增量同步
  • 开发工具

    • Maven的生命周期
    • nexus无法下载依赖的问题记录
  • 其他

    • linux服务器安装OpenOffice踩坑
    • POI踩坑-zip file is closed
    • OpenJDK没有jstack等命令的解决办法
    • 微信小程序加密数据对称解密工具类
    • mybatis的classpath配置导致的jar包读取问题
    • feign请求导致的用户ip获取问题记录
    • ribbon刷新服务列表间隔和canal的坑
    • cpu占用率高排查思路
  • 简介
  • java
  • Java框架
storyxc
2022-04-19

使用spring validation进行参数校验

# 使用spring validation进行参数校验

后台的参数校验如果全写在业务代码里会导致代码很臃肿,此时可以引入Spring Validation来进行参数校验,还可以自定义校验规则,自定义参数校验异常等

# 简单使用

  1. 项目引入spring-boot-starter-web依赖
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
1
2
3
4
5
6
7
8
  1. 在需要校验的对象前,加上@Validated注解,在对象字段上使用具体校验规则的注解即可。如果是嵌套对象,则在嵌套的对象上使用@Valid注解表明需要嵌套校验

    public String receive(@RequestBody @Validated StatementDto dto, BindingResult result) {
    		if (result.hasErrors()) {
    			throw new ParameterNotValidException(result);
    		}
    		return "test";
    	}
    
    1
    2
    3
    4
    5
    6
    public class QchjcCustomerStatementDto implements Serializable {
    
    	@NotEmpty(message = "不能为空")
    	@Valid
    	private List<OrderDto> order;
    
    	@DecimalMin(value = "0",message = "金额需大于等于0.00")
    	private BigDecimal amount;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    提示

    @Validated注解标记的参数会被spring进行校验,校验的信息会存放到其后的BindingResult中,如果有多个参数需要校验可以采用如下形式:(@Validated Person person, BindingResult fooBindingResult ,@Validated Bar bar, BindingResult barBindingResult);即一个校验类对应一个校验结果。

# 常用校验

  1. JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。

    1. @Null 被注释的元素必须为null

    2. @NotNull 被注释的元素必须不为null

    3. @AssertTrue 被注释的元素必须为true

    4. @AssertFalse 被注释的元素必须为false

    5. @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

    6. @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

    7. @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定

      最小值

    8. @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定

      最大值

    9. @Size(max, min) 被注释的元素的大小必须在指定的范围内

    10. @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内

    11. @Past 被注释的元素必须是一个过去的日期

    12. @Future 被注释的元素必须是一个将来的日期

    13. @Pattern(value) 被注释的元素必须符合指定的正则表达式

  2. hibernate validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等

    1. @Email 被注释的元素必须是电子邮箱地址
    2. @Length 被注释的字符串的大小必须在指定的范围内
    3. @NotEmpty 被注释的字符串的必须非空
    4. @Range 被注释的元素必须在合适的范围内
  3. spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中

# 统一异常处理

如果方法参数中不声明BindingResult,那么spring校验不通过后会直接抛出BindException,体验很不好。因此我们可以进行自定义异常处理。

  • 自定义异常
public class ParameterNotValidException extends RuntimeException{
    private static final long serialVersionUID = 1L;
    private BindingResult bindingResult;

    public ParameterNotValidException(BindingResult bindingResult) {
        this.bindingResult = bindingResult;
    }

    public BindingResult getBindingResult() {
        return bindingResult;
    }

    @Override
    public String getMessage() {
        return "参数校验不通过";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 统一异常处理
@ControllerAdvice
public class ExceptionHandler {
    /**
     * 方法参数校验异常处理
     */
    @ExceptionHandler(ParameterNotValidException.class)
    @ResponseBody
    public O handleMethodArgumentNotValidException(ParameterNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        String field = fieldError.getField();
        String defaultMessage = fieldError.getDefaultMessage();
        O o = new O();
        o.setResultCode(500);
        o.setSuccess(false);
        o.setResultMessage(field + ":" + defaultMessage);
        return o;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 自定义校验规则

例如,我们需要新增一个校验规则,数字类型必须大于0

  • 新建校验注解

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = GreaterThanZeroValidator.class)//标注校验由哪些类执行,可以是多个
    public @interface GreaterThanZero {
        String message();
    
        Class<?>[] groups() default {};//在不同接口中参数可能校验的规则不同,可以创建不同的组,并在校验规则标记组,在controller层也标记组,这样就可以不同接口实现不同校验规则
    
        Class<?>[] payload() default {};
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    一个标注(annotation) 是通过@interface关键字来定义的. 这个标注中的属性是声明成类似方法的样式的. 根据Bean Validation API 规范的要求

    • message属性, 这个属性被用来定义默认得消息模版, 当这个约束条件被验证失败的时候,通过此属性来输出错误信息.

    groups 属性, 用于指定这个约束条件属于哪(些)个校验组.这个的默认值必须是Class<?>类型到空到数组.

    • payload 属性, Bean Validation API 的使用者可以通过此属性来给约束条件指定严重级别. 这个属性并不被API自身所使用.

      public class Severity {
          public static class Info extends Payload {};
          public static class Error extends Payload {};
      }
      
      public class ContactDetails {
          @NotNull(message="Name is mandatory", payload=Severity.Error.class)
          private String name;
      
          @NotNull(message="Phone number not specified, but not mandatory", payload=Severity.Info.class)
          private String phoneNumber;
      
          // ...
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      这样, 在校验完一个ContactDetails 的示例之后, 你就可以通过调用ConstraintViolation.getConstraintDescriptor().getPayload()来得到之前指定到错误级别了,并且可以根据这个信息来决定接下来到行为.

  • 校验规则类

    public class GreaterThanZeroValidator implements ConstraintValidator<GreaterThanZero, Object> {
        @Override
        public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
            if (o == null) {
                return false;
            }
            if (o instanceof Number) {
                return ((Number) o).intValue() > 0;
            }
    
            return false;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    • ConstraintValidator定义了两个泛型参数, 第一个是这个校验器所服务到标注类型, 第二个这个校验器所支持到被校验元素到类型.如果一个约束标注支持多种类型到被校验元素的话, 那么需要为每个所支持的类型定义一个ConstraintValidator,并且注册到约束标注中.这个验证器的实现就很平常了
    • initialize() 方法传进来一个所要验证的标注类型的实例
    • isValid()是实现真正的校验逻辑的地方
编辑 (opens new window)
#spring
上次更新: 2022/04/21, 18:12:24
Spring Cloud Config接入
dubbo接口超时配置

← Spring Cloud Config接入 dubbo接口超时配置→

Theme by Vdoing | Copyright © 2019-2023 story | 豫ICP备19046036号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式