背景

最近端午好久没有和二胖聚一聚了,于是约了二胖到人民广场去宰他一顿,正好最近他跳槽加薪了。
:二胖听说你最近跳槽了,并且还是从传统软件公司跳到了互联网公司,工资是不是涨了一点啊,今天你请客哈。
二胖:别说了,工资是涨了点,但是性价比反而变低了,以前到点就下班,现在下班到家都快12点了。
:新公司怎么样还适应吗?除了上班时间久点。
二胖:哎,这个还真稍微有点不适应,这不是刚进去没啥事,leader就给我安排了一个简单的用户保存功能,原来以前公司个把小时就做好了的功能,在这新公司硬是折腾了两三天,真是苦不堪言。我改了好几个版本最终leader才满意的点了点头。

接口裸奔

  • 按照二胖在以前公司的写法再传统公司反正系统都是服务内部人员的,在后端写参数校验是不存在的事情,完全信赖前端传过来的内容。这不写完代码自测一把发现可以保存数据,就屁颠屁颠的发起代码review了(二胖在以前的公司代码review是不存在的,只要功能实现就好了)。正好leader今天有点时间,看到新同事提交的代码看看写的怎么样。看着这个裸奔的接口,leader把二胖叫了过去,语重心长的跟二胖说道:"你这个参数校验不写写吗?不怕人家攻击你的接口吗?这里不校验,直接用,不怕引入sql注入吗?这里不校验下邮箱是否符合格式吗?这个判空也不写,不怕大量的空指针,服务熔断吗?..."。面对leader的拼命十三问,二胖心想试用期怕是有点难过哦?只能低着头回到工位重新按照leader的教育整改起来,然后又重新提交了。

参数校验if判断

leader看了看说到:“这次代码比上次好多了,功能基本没啥问题了,但是这一块代码是不是可以在优化下,这样写不是很优雅”

   if(Objects.isNull(user)){throw new IllegalArgumentException("用户不能为空");}if(StringUtils.isEmpty(user.getUserName())){throw new IllegalArgumentException("用户名不能为空");}if(StringUtils.isEmpty(user.getUserName())){throw new IllegalArgumentException("用户名不能为空");}if(StringUtils.isEmpty(user.getSex())){throw new IllegalArgumentException("用户性别不能为空");}if(Objects.isNull(user.getUserDetail())){throw new IllegalArgumentException("用户详细信息不能为空");}if(Objects.isNull(user.getUserDetail().getAddress())){throw new IllegalArgumentException("用户地址不能为空");}if(!"M".equals(user.getSex()) && !"F".equals(user.getSex())){throw new IllegalArgumentException("用户性别不合法");}

二胖也是一阵郁闷,还是怀念以前的公司啊,功能实现就好,代码想怎么写就怎么写。互联网公司就是规矩多,写完代码还要写单测,还要监控一堆破事,活该这群人996.时间都花到这上面去了。抱怨该抱怨但是代码还得改啊。现在疫情期间好不容易找一个工作不能丢啊。二胖想到以前不是学过aop吗?再配合下自定义注解,这样代码就应该比较优雅了吧,说干就干。

自定义注解实现

  • 首先自定义了一个注解因为要校验参数

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface ParameterValidator {}
  • 配置一个切面,解析有ParameterValidator注解的方法。然后通过切面获取所有请求的参数,获取参数之后就解析参数上面的注解。配置切面啥的都比较简单,稍微复杂的就是反射解析参数了,因为要涉及到请求参数的嵌套结构。二胖习惯性的面向百度编程能copy别人的代码坚决不去自己写。百度出来的基本上都是单层结构,简单基本类型的对象,没有涉及到是嵌套、级联的类型的参数。最后在github全球最大的同性交友网站)找了一圈也没有找到合适的。既然拿来主义没有结果那就只能哼次哼次的自己写了,幸好自己以前学过点反射的知识。花了一个小时通过递归调用写了个粗糙的版本,比较粗糙还有很多场景没有考虑进去。不过基本可以满足条件了部分代码如下:

 public static void checkField(Object object, Class<?> aClass) throws IllegalAccessException {boolean primitive = isPrimitive(aClass);if (primitive) {return;}Field[] declaredFields = filterField(aClass.getDeclaredFields());for (Field field : declaredFields) {makeAccessible(field);// 校验我们自定义注解MyNotBlank fieldAnnotation = field.getAnnotation(MyNotBlank.class);Object currentObject = field.get(object);if (Objects.nonNull(fieldAnnotation)) {if (StringUtils.isEmpty(currentObject)) {throw  new IllegalArgumentException(field.getName()+":"+fieldAnnotation.message());}}if (!isJavaClass(field.getType())) {// 递归调用,有级联参数时候checkField(currentObject);} else if (field.getType().isPrimitive()) {} else if (field.getType().isAssignableFrom(List.class)) {// 递归调用,解析list类型getList(field, currentObject);}}}

然后赶紧测试一波,还不错基本功能实现了,能够实现判空检验了,也可以实现级联校验了。效果如下:不过这个现在支持类型为基本类型和StringList的 后续如果参数类型是数组、或者Map等等还得去解析。这时候同事二狗从旁边走过,看到二胖这么认真的在敲代码。
二狗:二胖你又在写什么bug啊。
二胖:在自己造个轮子,写个通用的参数校验。
二狗:这个现在市面上不是已经有现成的方案了吗?jsr(Java Specification Requests)可以去了解下哦。
二胖:好的我马上去查下资料。

jsr(Java Specification Requests) Java 规范提案

  • 说到jsr我们就得先了解下什么是JCP(Java Community Process)

JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新。

  • JSR又是个什么东东列?

它是指向JCP提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,(如果你觉得自己牛逼你也可以提交一个) 以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

Bean Validation

Bean Validation 顾名思义是对 java Bean 的校验,目前为止,Java 对 Bean 的校验有3个规范。

  • JSR-303 : Bean Validation

  • JSR 349 : Bean Validation 1.1

  • JSR 380 : Bean Validation 2.0

Hibernate-Validator

Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint

代码实现

  • 如果项目的框架是 spring boot 的话,在 spring-boot-starter-web 中已经包含了 Hibernate-validator 的依赖(版本必须是2.3之前)。2.3以后的版本 spring-boot-starter-web已经去除了这个依赖,需要手动引入 Hibernate-validator依赖,详细内容见官网描述

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  • springboot项目的话直接引入

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

代码演示:方法前面这个注解@Valid是必须的,否则不生效哦。

    @PostMapping(value = "/save2")@ResponseBodypublic ResultViewModel save2(@Valid @RequestBody User user){boolean saveUser = saveUser(user);if (saveUser) {return ResultViewModelUtil.success();}return ResultViewModelUtil.error();}

实体类上标上需要校验的规则注解就好了。

//被注释的元素,值必须是一个字符串,不能为null,且调用trim()后,长度必须大于0
@NotBlank(message = "")//被注释的元素,值不能为null,但可以为"空",用于基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 等对字段数值进行大小的控制
@NotNull(message = "")//被注释的的元素,值不能为null,且长度必须大于0,一般用在集合类上面
@NotEmpty(message = "")//被注释的元素必须符合指定的正则表达式。
@Pattern(regexp = "", message = "")//被注释的元素的大小必须在指定的范围内。
@Size(min =, max =)//被注释的元素,值必须是一个数字,且值必须大于等于指定的最小值
@Min(value = long以内的值, message = "")//被注释的元素,值必须是一个数字,且值必须小于等于指定的最大值
@Max(value = long以内的值, message = "")//被注释的元素,值必须是一个数字,其值必须大于等于指定的最小值
@DecimalMin(value = 可以是小数, message = "")//被注释的元素,值必须是一个数字,其值必须小于等于指定的最大值
@DecimalMax(value = 可以是小数, message = "")//被注释的元素,值必须为null
@Null(message = "")//被注释的元素必须是一个数字,其值必须在可接受的范围内
@Digits(integer =, fraction =)//被注释的元素,值必须为true
@AssertTrue(message = "")//被注释的元素,值必须为false
@AssertFalse(message = "")//被注释的元素必须是一个过去的日期
@Past(message = "")//被注释的元素必须是一个将来的日期
@Future(message = "")//被注释的元素必须是电子邮件地址
@Email(regexp = "", message = "")
//被注释的元素必须在合适的范围内
@Range(min =, max =, message = "")//被注释的字符串的大小必须在指定的范围内
@Length(min =, max =, message = "")

唯一需要注意的点就是如果是级联校验的话需要在最外层加上@Valid 为什么需要在校验的上一次标上@Valid这个注解,里面的校验才会生效列?有知道的或者感兴趣的可以去看看源码给我留言哦。然后在配置一个全局的异常捕获器就好了,由于篇幅原因代码就不贴了,代码上传到了github上。校验结果:

总结

  • Hibernate-Validator还可以自定义注解实现。

  • 还可以分组校验(有这样一种场景,新增用户信息的时候,不需要验证userId(因为系统生成);修改的时候需要验证userId,这时候可用用户到validator的分组验证功能)

  • 如果项目不是springboot的、比如使用的是Jfinal框架(这个是个国产框架大多数人能都不知道)、或者soa调用参数校验的时候,这时候可以怎么使用列?

  • 更多使用姿势大家感兴趣的可以去官网解锁哦,不过日常开发的以上介绍基本就可以满足需求了。

  • 二胖看到这丰富的api,以及炒鸡简单的用法,赶紧把自己写的轮子给删除了,立马换上了这个Hibernate-Validator框架。重新修改提交后,leader的脸上终于露出了满意的笑容。

  • 项目地址:https://github.com/worit1/validator

参考:

http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-gettingstarted(官网中文版本贴心吧) https://docs.jboss.org/hibernate/validator/6.1/reference/en-US/html_single/#validator-gettingstarted https://juejin.im/post/5dd8d44c518825734e4cda22 https://www.cnblogs.com/mr-yang-localhost/p/7812038.html

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

二胖写参数校验的坎坷之路相关推荐

  1. postmapping注解参数说明_这么写参数校验(validator)就不会被劝退了~

    作者: 锦成同学http://juejin.im/post/5d3fbeb46fb9a06b317b3c48 整理:后端技术精选 很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者不断的返回异常时 ...

  2. 补习系列(4)-springboot 参数校验详解

    目录 目标 一.PathVariable 校验 二.方法参数校验 三.表单对象校验 四.RequestBody 校验 五.自定义校验规则 六.异常拦截器 参考文档 目标 对于几种常见的入参方式,了解如 ...

  3. Springboot 参数校验@Valid @Validated(最新最全)

    Springboot 参数校验@Valid @Validated(最新最全) 提示:统一参数校验,异常处理 文章目录 Springboot 参数校验@Valid @Validated(最新最全) 前言 ...

  4. java校验参数防止攻击_程序员写接口参数校验,总是太多if else?一招让你避免体力活...

    对于写Java的程序员来说,不管是写单纯的接口.还是页面后台一把梭,后端参数校验的功能都是整个代码不可或缺的一部分,它可以从系统入口过滤掉一些不合法的数据,以确保我们的系统稳定. 还记得我刚入行Jav ...

  5. Spring Mvc Controller返回值、参数绑定、参数校验 (高级二)

    1,Controller 返回值 返回moduleAndView 返回 String 返回void 2,Controller 参数绑定 客户端请求携带的key/value 键值对 绑定到 Contro ...

  6. Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:六点半起床 juejin.im/post/685654110 ...

  7. Spring Validation 最佳实践及其实现原理,参数校验没那么简单!

    之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂Spring Validation.本文会详细介绍Spring Validation各种场景下 ...

  8. struts2教程(5)--请求参数校验

    一.请求参数校验介绍 校验的分类 :客户端数据校验 和服务器端数据校验 客户端数据校验 ,通过JavaScript完成校验 (改善用户体验,使用户减少出错 ) 服务器数据校验 ,使用框架内置校验功能( ...

  9. springboot 优雅的参数校验_SpringBoot 2.x 开发案例之优雅的校验参数

    前言 参数如何校验?撸主很久很久之前的项目都是在前端页面一个个 if else 的,后来就用了一系列的前端校验框架,比如 layui iview 等等,几个样式属性就可以轻松搞定,的确是美滋滋. 后端 ...

最新文章

  1. win运行pycharm找不到要加载的model.pkl文件是workingspace设置路径与文件路径位置不对应
  2. Java Socket例子
  3. 从Java类到对象的创建过程都做了些啥?内存中的对象是啥样的?
  4. 马斯克:大部分人没必要活那么长!活太久会让社会“陷入窒息”
  5. C#- Chart Controls (转)
  6. 指定init启动Linux内核,初看linux内核启动过程
  7. 算数计算机在线应用,数学计算器
  8. 代码管理学:常量类应该单独一个目录,还是在模块目录下?
  9. VC MFC 换肤 SkinSharp
  10. 【财务篇】如何群发工资条?
  11. 14_美年_图形报表,POI报表_运营数据统计_运营数据统计报表导出
  12. 【论文随笔2】COALA: Co-Aligned Autoencoders for Learning Semantically Enriched Audio Representations
  13. Elasticsearch安全加固
  14. [P1726]上白泽慧音
  15. 为了研究而玩:游戏分析的方法
  16. IPFS系列 - 分布式哈希表(DHT)
  17. Microsoft Access 操作必须使用一个可更新的查询
  18. 阿里云生活物联网平台中Android Demo运行
  19. 2017年职称计算机考试教程,【2017年职称计算机考试wps教程资料2】- 环球网校
  20. 利用 Logarithmic Binning (Log-Binning)方法绘制幂律分布(Power-law Distributions)曲线

热门文章

  1. 回溯法排序树怎么画_kd tree(k-dimensional树的简称)
  2. Himmelblau函数优化实战
  3. 保留两位小数除法算式_北师大|五年级上册|第一周周测·小数除法(1)
  4. Codeforces Round #700 (Div. 2) D2 Painting the Array II(最通俗易懂的贪心策略讲解)看不懂来打我 ~
  5. 排序算法—sort(),stable_sort(),merge()
  6. 提取某一个镇的行政边界_池店镇消防安全违法典型案例通报(一)
  7. 从零开始撸音乐播放器(源码可下载)
  8. P1080 国王游戏(贪心+大数乘除)
  9. P1603 斯诺登的密码
  10. angularjs2 html转义,详解Angular.js数据绑定时自动转义html标签及内容