javax.validation 参数校验

本贴最后更新于 972 天前,其中的信息可能已经时移俗易

参数校验往往让人心塞,各种判断匹配。基本的参数校验例如实体校验可以通过如下方法解决,但是因为我们的请求响应结果都是加密字符,需要解密后才能校验,所以在此的基础上增加第二段的方式,通过封装工具类的方法完成。

————————————————
版权声明:本文为 CSDN 博主「LailaiMonkey」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/h273979586/article/details/105572872
如果是 Spring 项目那么这个架构依赖会自动引用,如果是非 Spring 项目得手动引用一下依赖。本文以 Spring 项目为主:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
    <scope>compile</scope>
</dependency>

这是注解包里面元素:

@AssertFalse 被注释的元素必须为 false
@AssertTrue 被注释的元素必须为 true
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Email 被注释的元素必须是电子邮箱地址
@Future 被注释的元素必须是一个将来的日期
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@NotBlank 验证注解的元素值不为空(不为 null 并去除首尾位空格)
@NotEmpty 验证注解的元素值不为 null 且不为空(字符串长度不为 0、集合大小不为 0)
@NotNull 不为 null
@Null 为 null
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) `B 限制字符长度必须在 min 到 max 之间
验证规则
在 Spring 项目中定义一个需要验证的 model 内容如下:
@Data
public class ValidatorModel {

@NotBlank(message = "姓名不能为空")
    private String name;
    
    @Size(min = 3, max = 6)
    private List<String> friendNames;
    
    @Min(value = 18, message = "年龄未满18岁")
    @Max(value = 60, message = "年龄必须在60岁以下")
    private Integer age;
    
    private String tel;
    
    @DecimalMin(value = "30", message = "金钱不能小于30")
    private BigDecimal money;

service 接口内容如下:
因为是 javax 的验证构架所以在没有 Spring 时候也可以进行验证,注解得写在接口上,如果写在实现类将无效!!!

//开启验证注解
@Validated
public interface ValidatorService {
    void validator(@Valid ValidatorModel model, @NotNull String param);
}

service 实现类就是正常逻辑处理这里不在演示了。

controller 内容如下:
controller 接收参数传递给 service,在 service 接口进行参数验证,如果失败将抛出异常。

@RestController
public class ValidatorController {

    @Autowired
    private ValidatorService validatorService;
    
    @GetMapping
    public void validator() {
        ValidatorModel model = new ValidatorModel();
        model.setName("张三");
        model.setFriendNames(Arrays.asList("李四", "王五"));
        model.setAge(20);
        model.setTel("188888888881");
        model.setMoney(new BigDecimal(100));
        validatorService.validator(model,"也可以在参数上做验证");
    }
}

异常截图:
以上是 Javax Validation 校验常用方法,可以满足多数情况,但是项目中遇到的都是少数情况,例如:
添加时候需要对所有参数进行验证,更新时候只需要对某个几参数或有值的参数进行验证(分组验证)。
可不可以自定义验证规则,我要验证性别,只能是男或女(自定义验证规则)。
分组验证
添加时候需要验证 friendNames 字段,更新时候需要验证 name 字段,这时得引用分组验证,修改 ValidatorModel 如下:

@Data
public class ValidatorModel {

    /**
     * 添加分组
     */
    public interface Create {
    }
    
    /**
     * 更新分组
     */
    public interface Update {
    }
    
    /**
     * 分组可以指定多个,用{}表示
     * 分组为:默认分组、更新分组
     */
    @NotBlank(message = "姓名不能为空", groups = {Default.class, Update.class})
    private String name;
    
    /**
     * 分组为添加分组
     */
    @Size(min = 3, max = 6, groups = {Create.class})
    private List<String> friendNames;
    
    @Min(value = 18, message = "年龄未满18岁")
    @Max(value = 60, message = "年龄必须在60岁以下")
    private Integer age;
    
    private String tel;
    
    @DecimalMin(value = "30", message = "金钱不能小于30")
    private Integer money;
}

把校验参数注解写在 Controller 上,别忘了在 Controller 上加 @Validated 注解哈!!!

@Validated
@RestController
public class ValidatorController {

    @Autowired
    private ValidatorService validatorService;
    
    @GetMapping
    public void validator() {
        ValidatorModel model = new ValidatorModel();
        model.setName("张三");
        model.setFriendNames(Arrays.asList("李四", "王五"));
        model.setAge(2);
        model.setTel("188888888881");
        model.setMoney(100);
        model.setEmail("a");
        validatorService.validator(model, "也可以在参数上做验证");
    }
    
    @GetMapping("/group")
    public void validatorGroup(@Validated({ValidatorModel.Create.class}) ValidatorModel model) {
        validatorService.validatorGroup(model, "也可以在参数上做验证");
    }
}

这样就可以实现分组验证了。
注意:model 里有一个默认分组,如果指定分组就按分组来验证参数,而没有指定的分组属于默认分组,上图的 Controller 不会对默认分组进行验证,写成 @Validated({ValidatorModel.Create.class, Default.class})把默认分组加进去就可以验证了。如下:

@GetMapping("/group")
	public void validatorGroup(@Validated({ValidatorModel.Create.class, Default.class}) ValidatorModel model) {
	    validatorService.validatorGroup(model, "也可以在参数上做验证");
	}

自定义验证规则
那如果我想验证手机怎么办呢?它里面没有提供,不要慌小朋友,使用自定义注解验证就可以了,它提供了接口我们只需要实现一下就好了。

首先需要我们自定义一个验证手机号的注解(MonkeyTel),并指定其实现类,在实现写验证的逻辑就可以了。注解如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 指定真正实现校验规则的类
@Constraint(validatedBy = MonkeyJavaTelImpl.class)
public @interface MonkeyJavaTel {
    String message() default "手机号必须是11位";

    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
}

实现类如下:

/**
 * @Author: LailaiMonkey
 * @Description:
 * String:参数类型
 * @Date:Created in 2020-04-17 15:19
 * @Modified By:
 */
 public class MonkeyJavaTelImpl implements ConstraintValidator<MonkeyJavaTel, String> {

    /**
     * 手机号是十一位为ture
     * @param value 值
     * @param context
     * @return
     */
     @Override
     public boolean isValid(String value, ConstraintValidatorContext context) {
        return  value.length() == 11;
     }
  }

项目结构如下:
忽略 monkeyValidator 包,自定义注解校验规则,有兴趣的小伙伴可以看下一篇博客:自定义校验注解框架自定义校验注解框架

自定义验证规则
缺点:
此注解只能写有接口或 Controller 上才管用,如果写在实现类上是无效的。而且逻辑都写在实现类上,不知道哪些参数进行验证,还得返回到接口或 Controller 上查询才可以。
不支持 List 集合验证,实现起来困难,只支持简单 model 验证。
分组验证只能写在 Controller 上,写在接口不起作用(坑)。
如果一个接口有多个实现类这个注解就不能再用了。

优点:
自定义注解实现简单,上手快。

使用工具类校验
校验工具类:

package com.alibaba.banff.web.util;
 
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.groups.Default;
 
/**
 * 校验工具类
 * 
 * @author lizhilong
 */
public class ValidationUtils {
 
    private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
 
    public static <T> ValidationResult validateEntity(T obj) {
        ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class);
        // if( CollectionUtils.isNotEmpty(set) ){
        if (set != null && set.size() != 0) {
            result.setHasErrors(true);
            Map<String, String> errorMsg = new HashMap<String, String>();
            for (ConstraintViolation<T> cv : set) {
                errorMsg.put(cv.getPropertyPath().toString(), cv.getMessage());
            }
            result.setErrorMsg(errorMsg);
        }
        return result;
    }
 
    public static <T> ValidationResult validateProperty(T obj, String propertyName) {
        ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<T>> set = validator.validateProperty(obj, propertyName, Default.class);
        if (set != null && set.size() != 0) {
            result.setHasErrors(true);
            Map<String, String> errorMsg = new HashMap<String, String>();
            for (ConstraintViolation<T> cv : set) {
                errorMsg.put(propertyName, cv.getMessage());
            }
            result.setErrorMsg(errorMsg);
        }
        return result;
    }
}

校验工具类返回的数据 ValidationResult(省略 getset):

package com.aliyun.prophet.facade.partner.flaw;
 
import java.util.Map;
 
/**
 * 校验结果
 * 
 * @author lizhilong
 */
public class ValidationResult {
 
    // 校验结果是否有错
    private boolean             hasErrors;
 
    // 校验错误信息
    private Map<String, String> errorMsg;
}

Person 类(省略 getset):

package com.aliyun.prophet.facade.partner.flaw;
 
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
 
public class Person {
    @Length(max=20,message="姓名长度不能大于20")
    @NotEmpty(message="姓名不能为空")
    private String name;
    @Range(min = 0, max = 1, message = "性别只能输入只能输入0或1")
    private Integer gender;
    private Integer age;
}

使用方法:

/**
 * 
 * @author: lizhilong
 */
public class Test {
    @org.junit.Test
    public void testValidation(){
        Person person = new Person();
        person.setAge(12);
        person.setGender(2);
//       person.setName("李智龙");
        ValidationResult result = ValidationUtils.validateEntity(person);
        Map<String, String> map = result.getErrorMsg();
        boolean isError = result.isHasErrors();
        System.out.println("isError: " +isError);
        System.out.println(map);
    }
}

打印结果:

isError: true
{gender=性别只能输入只能输入0或1, name=姓名不能为空}

是不是很简单呀,几步搞定,根本不用 spring 便可封装一个好用的工具类
————————————————
版权声明:本文为 CSDN 博主「熬夜是小狗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/icannotdebug/article/details/78708202

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8207 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 150 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
zhaozhizheng
没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或者鄙夷 北京

推荐标签 标签

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18898 引用 • 70797 回帖
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    397 引用 • 3418 回帖
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 31 关注
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    371 引用 • 1217 回帖 • 582 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    60 引用 • 287 回帖
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖 • 2 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 567 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 394 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 4 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 383 回帖 • 2 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 4 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 421 回帖 • 1 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 640 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 179 关注
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    169 引用 • 799 回帖 • 1 关注
  • Sillot

    Sillot (汐洛)孵化自思源笔记,致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点
    Github 地址:https://github.com/Hi-Windom/Sillot

    20 引用 • 6 回帖 • 29 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 5 关注
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    122 引用 • 73 回帖 • 1 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    311 引用 • 546 回帖 • 32 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 690 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    324 引用 • 1395 回帖
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 244 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 74 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖 • 1 关注