1. 概述

作为接口服务提供方,非常有必要在项目中加入参数校验,比如字段非空,字段长度限制,邮箱格式验证等等,数据校验常用到概念:
JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。
hibernate validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中,能够通过BindingResult类在Controller层的处理方法中取得错误信息

如何校验

引入Maven依赖

在pom.xml中加入maven依赖, validation-api, hibernate-validator

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>

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

spring-boot-starter-web已经依赖了validation-api和hibernate-validator,所以引入spring-boot-starter-web之后就不需要引入这两个依赖了。

Model上加JSR-303注解类型

JSR-303定义的校验类型:

空检查: 
@Null 验证对象是否为null 
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串 
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. 
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.

Booelan检查: 
@AssertTrue 验证 Boolean 对象是否为 true 
@AssertFalse 验证 Boolean 对象是否为 false

长度检查: 
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 
@Length(min=, max=) Validates that the annotated string is between min and max included.

日期检查: 
@Past 验证 Date 和 Calendar 对象是否在当前时间之前 
@Future 验证 Date 和 Calendar 对象是否在当前时间之后

数值检查: 
建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null 
@Min 验证 Number 和 String 对象是否大等于指定的值 
@Max 验证 Number 和 String 对象是否小等于指定的值 
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度 
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度 
@Digits 验证 Number 和 String 的构成是否合法 
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。

@Range(min=, max=) Checks whether the annotated value lies between (inclusive) the specified minimum and maximum. 
@Range(min=10000,max=50000,message=”range.bean.wage”) 
private BigDecimal wage;

@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证) 
@CreditCardNumber信用卡验证 
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。 
@ScriptAssert(lang= ,script=, alias=) 
@URL(protocol=,host=, port=,regexp=, flags=)

@Pattern 验证 String 对象是否符合正则表达式的规则

hibernate validator校验demo

先来看一个简单的demo,添加了Validator的注解:

package com.winner.model;

import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.math.BigDecimal;
import java.util.List;

/**
 * @author winner_0715
 * @description:
 * @date 2018/12/5
 */
public class ParamRo {

    @NotBlank(message = "userName不允许为空")
    @Length(min = 2, max = 5, message = "userName长度必须在{min}-{max}之间")
    private String userName;

    @NotBlank(message="年龄不能为空")
    @Pattern(regexp="^[0-9]{1,2}$",message="年龄不正确")
    private String userAge;

    @NotNull(message = "price不允许为空")
    @DecimalMin(value = "15.5", message = "价格不能低于 {value}")
    private BigDecimal price;

    @AssertFalse(message = "必须为false")
    private Boolean isFalse;
    /**
     * 如果是空,则不校验,如果不为空,则校验
     */
    @Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正确")
    private String birthday;

    /**
     * 注意这里必须加@Valid注解
     */
    @Valid
    private List<ParamItem> list;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public String getUserAge() {
        return userAge;
    }

    public void setUserAge(String userAge) {
        this.userAge = userAge;
    }

    public Boolean getFalse() {
        return isFalse;
    }

    public void setFalse(Boolean aFalse) {
        isFalse = aFalse;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public List<ParamItem> getList() {
        return list;
    }

    public void setList(List<ParamItem> list) {
        this.list = list;
    }
}

POST接口验证,验证不通过的结果在全局处理器中处理,略

@RequestMapping(value = "/param/validate/post", method = RequestMethod.POST)
public ResultBean paramValidationPost(@Valid @RequestBody ParamRo ro) {
    return ResultBeanUtils.success(ro);
}

http://localhost:8080/param/validate/post,请求参数如下

{
    "userName": "zhangsan",
    "userAge":"1000",
    "price": 25.6,
    "isFalse":true,
    "birthday":"1990-09-09",
    "list": [
        {
            "address": "beijing"
        }
    ]
}

响应结果

{
    "success": false,
    "msg": "年龄不正确;address长度必须在2-5之间;userName长度必须在2-5之间;",
    "data": null,
    "errorCode": 0
}

参数验证非常方便,字段上注解+验证不通过提示信息即可代替手写一大堆的非空和字段限制验证代码。

hibernate的校验模式

上面例子中一次性返回了所有验证不通过的集合,通常按顺序验证到第一个字段不符合验证要求时,就可以直接拒绝请求了。

Hibernate Validator有以下两种验证模式:

普通模式

普通模式(会校验完所有的属性,然后返回所有的验证失败信息)

快速失败返回 

快速失败返回模式(只要有一个验证失败,则返回)

两种验证模式配置方式:

failFast:true  快速失败返回模式    false 普通模式

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .failFast( true )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

和 (hibernate.validator.fail_fast:true  快速失败返回模式    false 普通模式)

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .addProperty( "hibernate.validator.fail_fast", "true" )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

配置hibernate Validator为快速失败返回模式:

package com.winner.validate;

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }
}

此时相同的输入,输出变成

{
    "success": false,
    "msg": "年龄不正确;",
    "data": null,
    "errorCode": 0
}

即快速失败

GET参数校验(@RequestParam参数校验)

使用校验bean的方式,没有办法校验RequestParam的内容,一般在处理Get请求(或参数比较少)的时候,会使用下面这样的代码:

@RequestMapping(value = "/param/validate/get/2", method = RequestMethod.GET)
public ResultBean paramValidationGet2(@RequestParam String name,
                                      @RequestParam String address) {
    return ResultBeanUtils.success(name + address);
}

使用@Valid注解,对RequestParam对应的参数进行注解,是无效的,需要使用@Validated注解来使得验证生效。如下所示:

a.此时需要使用MethodValidationPostProcessor 的Bean:

package com.winner.validate;

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Configuration
public class ValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }

    //@Bean
    //public MethodValidationPostProcessor methodValidationPostProcessor() {
    //  /**默认是普通模式,会返回所有的验证不通过信息集合*/
    //    return new MethodValidationPostProcessor();
    //}

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        /**设置validator模式为快速失败返回*/
        postProcessor.setValidator(validator());
        return postProcessor;
    }
}

b.方法所在的Controller上加注解@Validated

package com.winner.web;

import com.winner.model.ParamRo;
import com.winner.model.ResultBean;
import com.winner.util.ResultBeanUtils;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

/**
 * @author winner_0715
 * @description:
 * @date 2018/12/5
 */
@RestController
@Validated
public class HelloController {
    @RequestMapping(value = "/param/validate/get/2", method = RequestMethod.GET)
    public ResultBean paramValidationGet2(
                                        @NotBlank(message = "name不允许为空")
                                        @Length(min = 2, max = 5, message = "name长度必须在{min}-{max}之间")
                                        @RequestParam String name,
                                        @Min(value = 18, message = "年龄必须大于18")
                                        @RequestParam int age) {
        return ResultBeanUtils.success(name + age);
    }

}

c.返回验证信息提示

验证不通过时,抛出了ConstraintViolationException异常,使用同一捕获异常处理:略

d.验证

http://localhost:8080/param/validate/get/2?name=zhangsan&age=16

快速失败模式结果

{
    "success": false,
    "msg": "name长度必须在2-5之间;",
    "data": null,
    "errorCode": 0
}

默认模式结果

{
    "success": false,
    "msg": "name长度必须在2-5之间;年龄必须大于18;",
    "data": null,
    "errorCode": 0
}

对象级联校验

对象内部包含另一个对象作为属性,属性上加@Valid,可以验证作为属性的对象内部的验证:(验证Demo2示例时,可以验证Demo2的字段)

@Data
public class Demo2 {
    @Size(min = 3,max = 5,message = "list的Size在[3,5]")
    private List<String> list;

    @NotNull
    @Valid
    private Demo3 demo3;
}

@Data
public class Demo3 {
    @Length(min = 5, max = 17, message = "length长度在[5,17]之间")
    private String extField;
}

参考:

https://www.cnblogs.com/mr-yang-localhost/p/7812038.html

Spring Boot参数校验的更多相关文章

  1. Spring Boot 参数校验

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

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

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

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

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

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

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

  5. Spring Boot (一) 校验表单重复提交

    一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...

  6. 59. Spring Boot Validator校验【从零开始学Spring Boot】

    大纲: (1) 入门例子: (2) 国际化: (3) 在代码中添加错误信息: (1) 入门例子: Validator主要是校验用户提交的数据的合理性的,比如是否为空了,密码长度是否大于6位,是否是纯数 ...

  7. spring mvc参数校验

    一.在SringMVC中使用 使用注解 1.准备校验时使用的JAR validation-api-1.0.0.GA.jar:JDK的接口: hibernate-validator-4.2.0.Fina ...

  8. spring boot参数验证

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

  9. Spring boot validation校验

    使用 Hibernate validator 的步骤:1. 在 Pojo 类的字段上, 加上 Hibernate validator 注解2. 在Controller 函数的形参前加上 @Valid ...

随机推荐

  1. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-7项目缓冲方案( Redis)

    本文目录1. 摘要2. Redis配置3. RedisHelper4.使用实例 5. 总结 1.  摘要 由于內存存取速度远高于磁盘读取的特性,为了程序效率提高性能,通常会把常用的不常变动的数据存储在 ...

  2. memcached 配置

    Memcached是一款开源.高性能.分布式内存对象缓存系统,可应用各种需要缓存的场景,其主要目的是通过降低对Database的访问来加速web应用程序.它是一个基于内存的“键值对”存储,用于存储数据 ...

  3. Python学习(二十五)—— Python连接MySql数据库

    转载自http://www.cnblogs.com/liwenzhou/p/8032238.html 一.Python3连接MySQL PyMySQL 是在 Python3.x 版本中用于连接 MyS ...

  4. String类型的转型

    字符串类型的转型在java中常用的方法有标题中的三种. 简单介绍: 1.toString,需要保证调用这个方法的类.方法.变量不为null,否则会报空指针. 2.String.valueOf.这个方法 ...

  5. Python 2 和 3 的区别记录

    Python 2 和 3 的区别记录 print 2:关键字,可以 print a,也可以 print(a) 3:内置函数,必须带(),print(a) reload() 2:内置函数,可以直接使用 ...

  6. HDU-1009的解题报告

    Hdu-1009 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1009 题意:Fatmouse准备M磅的猫食,准备与猫守卫仓库有他最爱吃的食品贸易,J ...

  7. Pytorch安装(基于anaconda虚拟环境)

    Pytorch安装倒腾了一上午终于搞定,记录一下安装过程. 1. 首先尝试官网的安装方式,但是网速太慢了. 除去cudnn100, torchvision和pytorch三个文件,其余可以直接从清华镜 ...

  8. css布局方式总结

    ### 居中布局 ### 一.水平居中 * 要求:子元素于父元素水平居中且其(子元素与父元素)宽度均可变. ```` javacript <div class="parent" ...

  9. 上海市2019年公务员录用考试笔试合格人员笔试成绩(A类)

    考试类别:A类 注册编号 总成绩 注册编号 总成绩 注册编号 总成绩 注册编号 总成绩 4016574 127.4 5112479 145.9 5125732 124.3 5141074 159.9 ...

  10. 原生JavaScript支持6种方式获取元素

    一.原生JavaScript支持6种方式获取元素 document.getElementById('id'); document.getElementsByName('name'); document ...