热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot参数校验的最佳实战教程_java

开发过程中,后台的参数校验是必不可少的,下面这篇文章主要给大家介绍了关于SpringBoot参数校验的最佳实战,文中通过示例代码介绍的非常详细,

前言

我们这里使用hibernate-validator作为对象参数验证器,所以在正式介绍SpringBoot参数验证之前,需要先简单了解一下hibernate-validator的使用。

hibernate-validator基本使用

引入依赖


    org.hibernate
    hibernate-validator
    6.0.17.Final


    javax.validation
    validation-api

编写需要验证对象

验证要求 person对象的用户名不能为空,年龄在1-150岁之间。

@Data
public class Person {

    @NotBlank(message = "username must not be null")
    private String username;

    @Min(value = 1, message = "age must be >= 1")
    @Max(value = 150, message = "age must be <150")
    private Integer age;
}

验证对象属性是否符合要求

    /**
     * 对象验证器
     */
    public Validator validator() {
        ValidatorFactory validatorFactory =
                Validation
                        .byProvider(HibernateValidator.class)
                        .configure()
                        // 验证属性时,如果有一个验证不通过就返回,不需要验证所有属性
                        .addProperty("hibernate.validator.fail_fast", "true")
                        .buildValidatorFactory();
        return validatorFactory.getValidator();
    }

    @Test
    public void test() throws Exception {
        Person person = new Person();
        Set> validate = validator().validate(person);
        validate.forEach(errorParam -> {
            System.out.println(errorParam.getMessage());
        });
    }
  1. 我们只需要验证的对象实例即可完成对象验证,如果验证成功,那么返回一个空的集合,如果验证失败,会返回具体的验证失败的属性信息。
  2. 我们输出验证失败的错误信息如下:
username must not be null

验证规则

validator提供了大量的验证注解供我们使用,主要以下几类:

空/非空验证

  1. @Null 元素必须为空
  2. @NotNull 元素不能为空,空字符串""是非空

以下所有验证规则都在元素非空的时候才会进行验证,如果传入的元素为空,验证都会通过。

bool

  1. @AssertTrue 元素必须为true
  2. @AssertFalse 元素必须为false

时间

  1. @Future 元素必须是未来的某个时间。
  2. @FutureOrPresent 元素必须是未来或者现在的某个时间。
  3. @Past 元素必须是过去的某个时间。
  4. @PastOrPresent 元素必须是过去或者现在的某个时间。

数学

数字类型可以是BigDecimal、BigInteger、CharSequence 、byte 、 short 、 int 、 long以及它们各自的包装器类型

  1. @Digits 元素必须是该数字类型下可以被接受的数值范围内。
  2. @Negative 元素必须是负数
  3. @NegativeOrZero  元素必须小于等于0
  4. @Positive 元素必须大于0
  5. @PositiveOrZero 元素必须大于等于0
  6. @Max,@Min 元素的大小必须符合指定大小

字符串

  1. @Email 邮箱格式验证
  2. @NotBlack 验证字符串非空,空字符串""也属于空
  3. @Pattern 字符串正则验证

模板正则

validator提供了字符串模板正则的注解,这里提供一份常用的正则表达式,大家可以直接作为常量工具类放到项目里使用

public interface ValidatorPattern {

    /**
     * 正则表达式:验证用户名
     * 1.长度在5-17
     * 2.由大写小写字母构成
     */
    String REGEX_USERNAME = "^[a-zA-Z]\w{5,17}$";

    /**
     * 正则表达式:验证密码
     * 密码只能为 6 - 12位数字,字母及常用符号组成。
     */
    String REGEX_PASSWORD = "^(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9._~!@#$^&*]{6,12}$";

    /**
     * 正则表达式:验证手机号
     */
    String REGEX_MOBILE = "^[1][34578]\d{9}$";

    /**
     * 正则表达式:验证邮箱
     */
    String REGEX_EMAIL = "^.+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-zA-Z]{2,}$";

    /**
     * 正则表达式:验证汉字
     */
    String REGEX_CHINESE = "^[\u4e00-\u9fa5],*$";

    /**
     * 正则表达式:验证身份证
     */
    String REGEX_ID_CARD = "(^\d{18}$)|(^\d{15}$)";

    /**
     * 正则表达式:验证URL
     */
    String REGEX_URL = "http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";

    /**
     * 正则表达式:验证IP地址
     */
    String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)";

    /**
     * 车牌号正则
     */
    String LICENSE_NO = "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z][A-Z][A-Z0-9]{4,5}[A-Z0-9挂学警港澳]$";

    /**
     * 姓名校验
     * 1~15位
     * 姓名支持空格和中文的点
     */
    String NAME = "[\u4e00-\u9fa5\u00b7\sA-Za-z]{1,15}$";

    /**
     * 表情正则
     */
    String EMOJI = "[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]";

    /**
     * 数字正则
     */
    String NUMBER = "^[0-9]*$";

    /**
     * n位的数字
     */
    String N_NUMS = "^\d{n}$";

}

SpringBoot整合hibernate-validator

引入依赖

这个不再赘述,直接拷贝上文的依赖信息

配置hibernate-validator验证器对象

在配置类中加入hibernate-validator验证器对象

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

借助SpringMVC统一异常处理处理参数校验结果

配置好后,Spring会自动帮助我们进行参数验证,如果参数验证不通过,会抛出BindException异常,我们刚刚手动验证时的Set>通过该异常获取。

我们这可以通过借助SpringMVC统一异常处理的能力处理这个异常

@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {
    /**
     * spring validation 自动校验的参数异常
     *
     * @param e BindException
     * @return R
     */
    @ResponseStatus(org.springframework.http.HttpStatus.PAYMENT_REQUIRED)
    @ExceptionHandler(BindException.class)
    public R handler(BindException e) {
        String defaultMsg = e.getBindingResult().getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining(":"));
        log.warn(defaultMsg);
        return R.of(IRespCode.PARAMETERS_ANOMALIES.getCode(), e.getMessage());
    }
}

使用参数校验

我们只需要在校验参数的方法传参上标注@Valid或者@Validated都行

    @PostMapping("register")
    public R register(@Valid @RequestBody Person person) {
        // todo 
        return R.ok();
    }

分组校验

那么@Valid和@Validated有什么区别呢?

Validated比Valid多了一个属性,这个属性用于分组校验使用

public @interface Valid {
}
public @interface Validated {
    Class[] value() default {};
}

啥叫分组校验?

就是一个实体类中的属性,在不同的方法传参中,方法的对属性的要求不同。

比如说,Person类中有三个属性,一个是用户名称,一个是邮箱,一个是年龄。

在注册用户接口中,用户名称,邮箱和年龄都不能为空,但是在更改用户的信息接口中,用户的年龄和邮箱都可以为空,但是用户名称不能为空。

这时候,我们就可以按照对属性校验的要求进行分组。

新建一个RegisterGroup分组,该分组只是一个空的接口,仅仅用于标记该校验要求

public interface RegisterGroup {
}

对校验要求进行分组

@Data
public class Person {

    @NotBlank(message = "username must not be null")
    private String username;

    @Min(value = 1, message = "age must be >= 1")
    @Max(value = 150, message = "age must be <150")
    @NotNull(message = "age must not be null", groups = RegisterGroup.class)
    private Integer age;

    @Email(message = "email format error")
    @NotBlank(message = "email must not be null",groups = RegisterGroup.class)
    private String email;
}

方法调用时,加入分组要求

    @PostMapping("register")
    public R register(@Validated(value = RegisterGroup.class) @RequestBody Person person) {
        // todo
        return R.ok();
    }

这种方式其实不推荐使用,我在标题的时候,也已经标记为“过时”,因为,我们完全可以为这两个不同的接口创建两个不同的实体类,而不是使用分组对校验要求进行隔离,因为实际生产环境中,分组可能有非常多个,这会为我们的程序的可读性埋下隐患,后期开发人员难以维护,而且对于自动生成API文档也不友好。大家对于分组只需要了解即可,不建议在项目开发中使用。

总结


推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文介绍了在使用Laravel和sqlsrv连接到SQL Server 2016时,如何在插入查询中使用输出子句,并返回所需的值。同时讨论了使用CreatedOn字段返回最近创建的行的解决方法以及使用Eloquent模型创建后,值正确插入数据库但没有返回uniqueidentifier字段的问题。最后给出了一个示例代码。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
author-avatar
诚实的坏男2502892343
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有