百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

这么写参数校验(Validator)就不会被劝退了

toyiye 2024-06-21 12:18 7 浏览 0 评论

来自:掘金,作者:锦成同学

链接:https://juejin.im/post/5d3fbeb46fb9a06b317b3c48

  • 为什么要用validator
  • 实战演练
    • 1. @Validated 声明要检查的参数
    • 2. 对参数的字段进行注解标注
    • 3. 在全局校验中增加校验异常
    • 4. 测试
  • 自定义参数注解
    • 1. 比如我们来个 自定义身份证校验 注解
    • 2. 然后自定义Validator
    • 3. 使用自定义的注解
    • 4.使用groups的校验
    • 5.restful风格用法
  • 总结

  • 很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者 不断的返回异常时的校验信息,在代码中相当冗长, 充满了if-else这种校验代码,今天我们就来学习spring的javax.validation 注解式参数校验.


    为什么要用validator

    1.javax.validation的一系列注解可以帮我们完成参数校验,免去繁琐的串行校验

    不然我们的代码就像下面这样:

    这被大佬看见,一定说,都9102了还这么写,然后被劝退了.....

    1. 什么是javax.validation

    JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面(面向注解编程的时代),就可以在需要校验的时候进行校验了,在SpringBoot中已经包含在starter-web中,再其他项目中可以引用依赖,并自行调整版本:


    1.注解说明



    此处只列出Hibernate Validator提供的大部分验证约束注解,请参考hibernate validator官方文档了解其他验证约束注解和进行自定义的验证约束注解定义。


    实战演练

    话不多说,直接走实践路线,同样使用的是SpringBoot的快速框架,详细代码见:github.com/leaJone/myb…

    1. @Validated 声明要检查的参数

    这里我们在控制器层进行注解声明

    2、对参数的字段进行注解标注

    import lombok.Data;
    import org.hibernate.validator.constraints.Length;
    
    import javax.validation.constraints.*;
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author LiJing
     * @ClassName: UserDTO
     * @Description: 用户传输对象
     * @date 2019/7/30 13:55
     */
    @Data
    public class UserDTO implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        /*** 用户ID*/
        @NotNull(message = "用户id不能为空")
        private Long userId;
    
        /** 用户名*/
        @NotBlank(message = "用户名不能为空")
        @Length(max = 20, message = "用户名不能超过20个字符")
        @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*#34;, message = "用户昵称限制:最多20字符,包含文字、字母和数字")
        private String username;
    
        /** 手机号*/
        @NotBlank(message = "手机号不能为空")
        @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}#34;, message = "手机号格式有误")
        private String mobile;
    
        /**性别*/
        private String sex;
    
        /** 邮箱*/
        @NotBlank(message = "联系邮箱不能为空")
        @Email(message = "邮箱格式不对")
        private String email;
    
        /** 密码*/
        private String password;
    
        /*** 创建时间 */
        @Future(message = "时间必须是将来时间")
        private Date createTime;
    
    }
    

    3、在全局校验中增加校验异常

    MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要 处理ConstraintViolationException异常进行处理.

    • 为了优雅一点,我们将参数异常,业务异常,统一做了一个全局异常,将控制层的异常包装到我们自定义的异常中
    • 为了优雅一点,我们还做了一个统一的结构体,将请求的code,和msg,data一起统一封装到结构体中,增加了代码的复用性
    import com.boot.lea.mybot.dto.RspDTO;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.dao.DuplicateKeyException;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.servlet.NoHandlerFoundException;
    
    import javax.validation.ConstraintViolationException;
    import javax.validation.ValidationException;
    
    
    /**
     * @author LiJing
     * @ClassName: GlobalExceptionHandler
     * @Description: 全局异常处理器
     * @date 2019/7/30 13:57
     */
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        private static int DUPLICATE_KEY_CODE = 1001;
        private static int PARAM_FAIL_CODE = 1002;
        private static int VALIDATION_CODE = 1003;
    
        /**
         * 处理自定义异常
         */
        @ExceptionHandler(BizException.class)
        public RspDTO handleRRException(BizException e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(e.getCode(), e.getMessage());
        }
    
        /**
         * 方法参数校验
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public RspDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
        }
    
        /**
         * ValidationException
         */
        @ExceptionHandler(ValidationException.class)
        public RspDTO handleValidationException(ValidationException e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(VALIDATION_CODE, e.getCause().getMessage());
        }
    
        /**
         * ConstraintViolationException
         */
        @ExceptionHandler(ConstraintViolationException.class)
        public RspDTO handleConstraintViolationException(ConstraintViolationException e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(PARAM_FAIL_CODE, e.getMessage());
        }
    
        @ExceptionHandler(NoHandlerFoundException.class)
        public RspDTO handlerNoFoundException(Exception e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(404, "路径不存在,请检查路径是否正确");
        }
    
        @ExceptionHandler(DuplicateKeyException.class)
        public RspDTO handleDuplicateKeyException(DuplicateKeyException e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(DUPLICATE_KEY_CODE, "数据重复,请检查后提交");
        }
    
    
        @ExceptionHandler(Exception.class)
        public RspDTO handleException(Exception e) {
            logger.error(e.getMessage(), e);
            return new RspDTO(500, "系统繁忙,请稍后再试");
        }
    }

    4、测试

    如下文:确实做到了参数校验时返回异常信息和对应的code,方便了我们不再繁琐的处理参数校验

    在ValidationMessages.properties 就是校验的message,有着已经写好的默认的message,且是支持i18n的,大家可以阅读源码赏析


    自定义参数注注:

    1、比如我们来个 自定义身份证校验 注解

    这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。

    • message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
    • groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
    • payload 主要是针对bean的,使用不多。

    2、然后自定义Validator

    这个是真正进行验证的逻辑代码:

    IdCardValidatorUtils在项目源码中,可自行查看

    3、使用自定义的注解

    4、使用groups的校验

    有的宝宝说同一个对象要复用,比如UserDTO在更新时候要校验userId,在保存的时候不需要校验userId,在两种情况下都要校验username,那就用上groups了:

    先定义groups的分组接口Create和Update


    再在需要校验的地方@Validated声明校验组


    在DTO中的字段上定义好groups = {}的分组类型

    @Data
    public class UserDTO implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        /*** 用户ID*/
        @NotNull(message = "用户id不能为空", groups = Update.class)
        private Long userId;
    
        /**
         * 用户名
         */
        @NotBlank(message = "用户名不能为空")
        @Length(max = 20, message = "用户名不能超过20个字符", groups = {Create.class, Update.class})
        @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*#34;, message = "用户昵称限制:最多20字符,包含文字、字母和数字")
        private String username;
    
        /**
         * 手机号
         */
        @NotBlank(message = "手机号不能为空")
        @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}#34;, message = "手机号格式有误", groups = {Create.class, Update.class})
        private String mobile;
    
        /**
         * 性别
         */
        private String sex;
    
        /**
         * 邮箱
         */
        @NotBlank(message = "联系邮箱不能为空")
        @Email(message = "邮箱格式不对")
        private String email;
    
        /**
         * 密码
         */
        private String password;
    
        /*** 创建时间 */
        @Future(message = "时间必须是将来时间", groups = {Create.class})
        private Date createTime;
    
    }
    

    注意:在声明分组的时候尽量加上 extend javax.validation.groups.Default否则,在你声明@Validated(Update.class)的时候,就会出现你在默认没添加groups = {}的时候的校验组@Email(message = "邮箱格式不对"),会不去校验,因为默认的校验组是groups = {Default.class}.

    5、restful风格用法

    在多个参数校验,或者@RequestParam 形式时候,需要在controller上加注@Validated

    总结

    用起来很简单,soEasy,重点参与的统一结构体返回,统一参数校验,是减少我们代码大量的try catch 的法宝,我觉得在项目中,将异常处理好,并将异常做好日志管理,才是很好的升华,文章浅显,只是一个菜鸟的进阶笔记....

    这里只是个人见解,技术菜,欢迎大佬不吝赐教... 我是一个小白,技术在不断的更新迭代,我只有不断的填充自己的空白才能....跟上大佬们的步伐...


    对了,在这里说一下,我目前是在职Java开发,如果你现在正在学习Java,了解Java,渴望成为一名合格的Java开发工程师,在入门学习Java的过程当中缺乏基础入门的视频教程,可以关注并私信我:01。获取。我这里有最新的Java基础全套视频教程。

    相关推荐

    为何越来越多的编程语言使用JSON(为什么编程)

    JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

    何时在数据库中使用 JSON(数据库用json格式存储)

    在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

    MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

    前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

    JSON对象花样进阶(json格式对象)

    一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

    深入理解 JSON 和 Form-data(json和formdata提交区别)

    在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

    JSON 语法(json 语法 priority)

    JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

    JSON语法详解(json的语法规则)

    JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

    MySQL JSON数据类型操作(mysql的json)

    概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

    JSON的数据模式(json数据格式示例)

    像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

    前端学习——JSON格式详解(后端json格式)

    JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

    什么是 JSON:详解 JSON 及其优势(什么叫json)

    现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

    PostgreSQL JSON 类型:处理结构化数据

    PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

    JavaScript:JSON、三种包装类(javascript 包)

    JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

    Python数据分析 只要1分钟 教你玩转JSON 全程干货

    Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

    比较一下JSON与XML两种数据格式?(json和xml哪个好)

    JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

    取消回复欢迎 发表评论:

    请填写验证码