如何使用Bean Validation 解决业务中参数校验

前言

在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有点麻烦:

验证代码繁琐,重复劳动

方法内代码显得冗长

每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码

叙述

Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator。

Bean Validation API是Java定义的一个验证参数的规范。

Hibernate Validator是Bean Validation API的一个实现。

@Valid和Validated的比较

Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。

@Valid : 没有分组功能,可以用在方法、构造函数、方法参数和成员属性(field)上,如果一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@valid,才能验证待验证对象中的成员属性

@Validated :提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,用在类型、方法和方法参数上。但不能用于成员属性(field)。

两者都可以用在方法入参上,但都无法单独提供嵌套验证功能,都能配合嵌套验证注解@Valid进行嵌套验证。

嵌套验证示例:

public class ClassRoom{
    @NotNull
    String name;

    @Valid  // 嵌套校验,校验参数内部的属性
    @NotNull
    Student student;
}
  @GetMapping("/room")   // 此处可使用 @Valid 或 @Validated, 将会进行嵌套校验
    public String validator(@Validated ClassRoom classRoom, BindingResult result) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }

BindingResult 的使用

BindingResult必须跟在被校验参数之后,若被校验参数之后没有BindingResult对象,将会抛出BindException。

@GetMapping("/room")
    public String validator(@Validated ClassRoom classRoom, BindingResult result) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }

不要使用 BindingResult 接收String等简单对象的错误信息。简单对象校验失败,会抛出 ConstraintViolationException。主要就是接不着,你要写也算是没关系…

  // ❌ 错误用法,也没有特别的错,只是 result 是接不到值。
    @GetMapping("/room")
    @Validated  // 启用校验
    public String validator(@NotNull String name, BindingResult result) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }

修改校验失败的提示信息

可以通过各个校验注解的message属性设置更友好的提示信息。

public class ClassRoom{
    @NotNull(message = "Classroom name must not be null")
    String name;

    @Valid
    @NotNull
    Student student;
}
 @GetMapping("/room")
    @Validated
    public String validator(ClassRoom classRoom, BindingResult result, @NotNull(message = "姓名不能为空") String name) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }

message属性配置国际化的消息也可以的,message中填写国际化消息的code,在抛出异常时根据code处理一下就好了。

@GetMapping("/room")
    @Validated
    public String validator(@NotNull(message = "demo.message.notnull") String name) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }
// message_zh_CN.properties
demo.message.notnull=xxx消息不能为空

// message_en_US.properties
demo.message.notnull=xxx message must no be null

hibernate-validator 的使用

1.引入pom

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.3.1.Final</version>
</dependency>

2.dto入参对象属性加入注解

@Data
public class UserModel implements Serializable {
    private String id;
    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotNull(message = "性别不能不填")
    private Byte gender;

    @NotNull(message = "年龄不能不填")
    @Min(value = 0,message = "年龄必须大于0岁")
    @Max(value = 150,message = "年龄必须小于150岁")
    private Integer age;

    @NotBlank(message = "手机号不能不填")
    private String telphone;

    private String registerMode;
    private String thirdPartyId;
    private String encrptPassward;
}

方法一:3.controller方法入参加入校验(@Validated )

@GetMapping("/getUser")
    public String validator(@Validated UserModel userModel , BindingResult result) {
        if (result.hasErrors()) {
            return result.getFieldError().getDefaultMessage();
        }
        return "ok";
    }

方法二:3.自定义封装ValidatorImpl类

@Component
public class ValidatorImpl implements InitializingBean{

    private Validator validator;

    //实现校验方法并返回校验结果
    public ValidationResult validate(Object bean){
        final   ValidationResult result=new ValidationResult();
        Set<ConstraintViolation<Object>> validate = validator.validate(bean);
        if (validate.size()>0) {
            result.setHasError(true);
            validate.forEach(constraintViolation->{
                String errMsg=constraintViolation.getMessage();
                String propertyName=constraintViolation.getPropertyPath().toString();
                result.getErrorMsgMap().put(propertyName,errMsg);
            });
        }
        return result;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        this.validator= Validation.buildDefaultValidatorFactory().getValidator();
    }
}

方法二:4.自定义封装ValidationResult 类

public class ValidationResult {
    public boolean hasError=false;
    private Map<String,String> errorMsgMap=new HashMap<>();

    //实现通用的通过格式化字符串信息获取错误结果的msg方法
    public String getErrMsg(){
        return StringUtils.join(errorMsgMap.values().toArray(),",");
    }

    public boolean isHasError() {
        return hasError;
    }

    public void setHasError(boolean hasError) {
        this.hasError = hasError;
    }

    public Map<String, String> getErrorMsgMap() {
        return errorMsgMap;
    }

    public void setErrorMsgMap(Map<String, String> errorMsgMap) {
        this.errorMsgMap = errorMsgMap;
    }
}

5.controller方法入参加入校验

   @Autowired
   private ValidatorImpl validator;
    @Override
       @Transactional(rollbackFor = Exception.class)
    public void register(UserModel userModel) throws BusinessException {
        UserDo userDo=new UserDo();
        if (userModel == null) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }

  //validate进行入参校验
       ValidationResult validate = validator.validate(userModel);
        if (validate.isHasError()){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,validate.getErrMsg());
        }
    }

Bean Validation 的约束

  • @Null 被注释的元素必须为 null
  • @NotNull 被注释的元素必须不为 null
  • @AssertTrue 被注释的元素必须为 true
  • @AssertFalse 被注释的元素必须为 false
  • @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @Size(max, min) 被注释的元素的大小必须在指定的范围内
  • @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
  • @Past 被注释的元素必须是一个过去的日期
  • @Future 被注释的元素必须是一个将来的日期
  • @Pattern(value) 被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的约束

Hibernate Validator 附加的 constraint:

  • @Email 被注释的元素必须是电子邮箱地址
  • @Length 被注释的字符串的大小必须在指定的范围内
  • @NotEmpty 被注释的字符串的必须非空
  • @Range 被注释的元素必须在合适的范围内

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot使用validation做参数校验的实现步骤

    1.添加依赖 直接添加 hibernate-validator <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.2.Final</version> </dependency> 添加spring-boot-starter-validat

  • 如何使用Spring Validation优雅地校验参数

    引言 不知道大家平时的业务开发过程中 controller 层的参数校验都是怎么写的?是否也存在下面这样的直接判断? public String add(UserVO userVO) { if(userVO.getAge() == null){ return "年龄不能为空"; } if(userVO.getAge() > 120){ return "年龄不能超过120"; } if(userVO.getName().isEmpty()){ return &q

  • SpringBoot + validation 接口参数校验的思路详解

    有参数传递的地方都少不了参数校验.在web开发中,前端的参数校验是为了用户体验,后端的参数校验是为了安全.试想一下,如果在controller层中没有经过任何校验的参数通过service层.dao层一路来到了数据库就可能导致严重的后果,最好的结果是查不出数据,严重一点就是报错,如果这些没有被校验的参数中包含了恶意代码,那就可能导致更严重的后果. 实践 一.引入依赖 <!--引入spring-boot-starter-validation--> <dependency> <gr

  • java validation 后台参数验证的使用详解

    一.前言 在后台开发过程中,对参数的校验成为开发环境不可缺少的一个环节.比如参数不能为null,email那么必须符合email的格式,如果手动进行if判断或者写正则表达式判断无意开发效率太慢,在时间.成本.质量的博弈中必然会落后.所以把校验层抽象出来是必然的结果,下面说下几种解决方案. 二.几种解决方案 1.struts2的valid可以通过配置xml,xml中描述规则和返回的信息,这种方式比较麻烦.开发效率低,不推荐 2.validation bean 是基于JSR-303标准开发出来的,使

  • 如何使用Bean Validation 解决业务中参数校验

    前言 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,邮箱格式验证等等,写这些与业务逻辑关系不大的代码个人感觉有点麻烦: 验证代码繁琐,重复劳动 方法内代码显得冗长 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码 叙述 Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator. Bean Validation API是Java定义的一个验证参数的规范. Hibernate

  • SpringMVC中常用参数校验类注解使用示例教程

    目录 一.环境准备 二.常用的校验注解及示例 三.校验类方法中的普通参数 四.校验类方法中的自定义对象 五.关于@Valid和@Validated的区别联系 六.分组校验 七.自定义校验注解 一.环境准备 在项目中添加以下依赖 gradle org.hibernate:hibernate-validator:5.3.5.Final maven <dependency> <groupId>org.hibernate</groupId> <artifactId>

  • SpringBoot 如何自定义请求参数校验

    目录 一.Bean Validation基本概念 二.基本用法 三.自定义校验 3.1 自定义注解 3.2 自定义Validator 3.3 以编程的方式校验(手动) 3.4 定义分组校验 3.5 定制返回码和消息 3.6 更加细致的返回码和消息 四.小结 最近在工作中遇到写一些API,这些API的请求参数非常多,嵌套也非常复杂,如果参数的校验代码全部都手动去实现,写起来真的非常痛苦. 正好Spring轮子里面有一个Validation,这里记录一下怎么使用,以及怎么自定义它的返回结果. 一.B

  • 解决Spring配置文件中bean的property属性中的name出错问题

    Spring配置文件中bean的property属性中的name有错,红色 原因: 在实现类中没有写set方法 解决: 理解Spring配置文件中的property标签中的属性 1.ref引用一个已经存在的对象,value创建一个新的对象 2.value可以赋一些简单类型的值,ref可以引用其他的bean对象. Spring配置文件中配置property标签的name和ref的区别 <bean id="person" class="service.Person"

  • 关于backbone url请求中参数带有中文存入数据库是乱码的快速解决办法

    最近项目用到了backbone 做前后端的分离方案,遇见了中文乱码问题,解决方案总结如下: 假设需要存一条课程记录到后台 model定义如下: var AddCourse= Backbone.Model.extend({ url:path+"/course/add", parse : function(response){ return response.data; } }); encodeURIComponent 函数 将中文的内容进行编码 $('#addCourseBtn' ).c

  • 完美解决js传递参数中加号和&号自动改变的方法

    在action中用get方法获得参数,如果参数里有"+",要做处理,否则到后台会变成空格. 解决方案: 1 .改用post方法: 2 .在 js 里用 url = encodeURI(encodeURI(XXX)) ,后台再解码一次: 3 .传递参数的时候直接替换转义,或者直接写转义后的代码 data =  "a + b": data = data.replace(/\+/g, "+"); data = data.replace(/\&/

  • 解决form中action属性后面?传递参数 获取不到的问题

    如下所示: $p_id = $_REQUEST['p_id']; echo "<h1>您将更新商品编号为<span>$p_id</span>的商品信息 <a href='listproduct.php'>查看所有</a></h1> <form action='updateproduct.do.php?p_id=$p_id' method='get'> 商品名称:<input type='text' name

  • 解决MyBatis中Enum字段参数解析问题

    目录 基础Class和TypeHandler 请求参数解析问题 问题解决 基础Class和TypeHandler MyBatis操作的基本User对象结构如下: @Data @Alias(value = "user") public class User implements Serializable { private static final long serialVersionUID = -4947062488310146862L; private Long id; @NotNu

  • springboot+dubbo+validation 进行rpc参数校验的实现方法

    注意:本文dubbo 版本 2.8.4 springboot 版本 2.0.4.RELEASE 项目结构 test-rest (前端消费着,controller 层,springboot+maven项目) test-api (dubbo服务 的 api ,只记录 service 接口和 model ,maven 项目) test-provider(dubbo 服务提供者,实际的数据库操作及业务层, springboot+maven项目 ) 背景: 使用springmvc做restful,使用du

  • 如何在spring boot中进行参数校验示例详解

    上文我们讨论了spring-boot如何去获取前端传递过来的参数,那传递过来总不能直接使用,需要对这些参数进行校验,符合程序的要求才会进行下一步的处理,所以本篇文章我们主要讨论spring-boot中如何进行参数校验. lombok使用介绍 在介绍参数校验之前,先来了解一下lombok的使用,因为在接下来的实例中或有不少的对象创建,但是又不想写那么多的getter和setter,所以先介绍一下这个很强大的工具的使用. Lombok 是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很

随机推荐