你们之中大概率早已练就了代码的拷贝、粘贴,无敌的码农神功,其实做久了业务功能开发,练就这两个无敌神功,那是迟早的事儿。今天先抛一个小问题,来打通你的任督二脉,就是很好奇的问一下:业务功能开发中,输入参数校验占了你多少时间呢?有没有考虑如何进行验证模块化、通用化?

咱们还是换个问法,看看元芳怎么看。“到底该如何保证业务 API 的健壮性”,元芳你怎么看类?

关注“一猿小讲”久了的元芳,多多少少都有点进步,所以不加思索的答到:首先要保证 API 输入参数的校验;然后……

听到“首先要保证 API 输入参数的校验”,就要给元芳打满分,因为他撬开了咱们今天的话题。

好了,请准备好小板凳,接下来看看如何玩转、生撸一个 API 参数校验的框架。

1.

常规写法。

备注:上面代码截图我仅用来举栗阐述,return 中的信息临时写死,应该动态设置返回码以及返回描述封装为 JSON 返回。

上图代码的栗子,也是众多新手最喜欢用的方式,毕竟在初入职场的程序猿眼中,错误的以为「代码量就是钱」,也可能是想在 SVN 或者 Git 上多留存更新记录,因为更新记录也是能体现代码量及劳动力的啊(捂嘴笑)。

但是往往一个 API 接口不简单只有两个输入参数,多则几十个参数,那岂不是大量代码的篇幅,都在进行参数校验,岂不是会耗费大量的时间精力,在参数校验上。

其实 API 接口入参校验步骤,我们大概可以分为:获取接口输入参数;校验必传参数是否传入;检验参数是否符合规则。

其实一旦找到规律,都可以交给机器去做,那咱们肯定可以打造一款高效的 API 参数校验的轮子。

2.

轮子应该有什么组成?

 

A. 接口入参配置;

B. 参数校验规则配置;

C. 封装参数校验失败时异常码以及异常信息;

结合上面的思考,咱们还是画个简单的流程图吧,上一图就秒懂。

3.

轮子应该咋实现?

 

A. 定义 API 接口入参配置文件 param.properties

########业务功能健壮性参数规则配置############################
#接口参数配置=Param group(按照|分割参数组,按照逗号分隔每组的参数)
##########################################################
#系统接口所需参数配置(PARAMS.apiURL=参数列表)
PARAMS./sys/login.do=username,password

  

B. 定义 API 接口参数正则校验 regex.properties

########业务功能健壮性参数规则配置############################
#接口参数正则表达式配置(PARAMS.接口URL.参数.regex=正则表达式)
PARAMS./sys/login.do.username.regex=\\w{1,32}
#全局参数正则表达式配置(参数.regex=正则表达式)
password.regex=\\w{1,32}

  

C. 开始生撸代码

第一步:定义如何根据请求的 api 接口获取对应的入参配置。

第二步:验证传入参数的值是否符合规则。由于每个 API 支持多组参数传入的情形,所以可以按照“|”分割多组参数。

第三步:验证传入参数的值是否符合规则,真正的校验逻辑。

第四步:按照咱们的流程图,把上面的方法串在一起,封装成一个 Service,想在哪儿用,在哪儿用,So Easy!!!其中 doService 方法的入参,apiUrl 就是接口的 url,json 是传入的参数。

还是要把主要代码分享给大家,以便你们更好的进步。

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; /**
* 请求参数校验服务
* desc:
* 1、通过传入的apiUrl找到需要校验的数据<b>(param.properties)</b>,
* 2、通过传入的参数key找到对应的正则表达式<b>(regex.properties)</b>,
* 3、对需要校验的数据进行正则表达式<b>
* 4、支持必填非必填字段校验
*/
public class RequestParamCheckService { public static final String REGKEY = "PARAMS.";
public static final String REGEX = ".regex"; public void doService(String apiUrl, JSONObject json) {
//1. 校验 API URL 是否传入
if (StringUtils.isEmpty(apiUrl)) {
System.out.println("Miss api url");
return;
}
//2. 根据 API URL 获取参数的配置
String paramKeyConf = StringUtils.trim(getKeyByApiUrl(apiUrl));
if (StringUtils.isEmpty(paramKeyConf)) {
System.out.println(String.format("%s found no check info", apiUrl));
return;
}
//3. 验证传入参数的值是否符合配置定义的规则
boolean[] validateAry = validate(apiUrl, paramKeyConf, json);
//4. 校验参数校验结果
if (!ArrayUtils.contains(validateAry, true)) {
System.out.println(String.format("%s param validate fail", apiUrl));
//TODO 此处需要根据字段获取对应的返回码以及返回信息,可以自行扩展实现一下,本次硬编码
throw new ValidateException("99999999", "参数校验失败");
}
} /**
* 验证传入参数的值是否符合配置定义的规则
*
* @param apiUrl 接口
* @param paramKeyConf 配置的参数key
* @param json 传入的json报文内容
* @return 验证结果
*/
private boolean[] validate(String apiUrl, String paramKeyConf, JSONObject json) {
// 将KEY分隔
String[] chkKeys = paramKeyConf.split("\\|");
//判断拆分后的参数是否为组合配置
//针对每组参数校验是否传入,最后得出参数传入是否正确
boolean[] validateAry = new boolean[chkKeys.length];
for (int i = 0; i < chkKeys.length; i++) { //针对每个参数组进行验证
String[] unitKeyAry = chkKeys[i].split(","); //按照逗号拆分每组的参数key配置
validateAry[i] = check(apiUrl, json, unitKeyAry); //校验参数是否符合规则
}
return validateAry;
} /**
* 校验传入的参数是否符合规则
*
* @param apiUrl 校验的apiUrl
* @param json 传入的json报文数据
* @param keyAry 参数数组
* @return 校验结果
*/
private boolean check(String apiUrl, JSONObject json, String[] keyAry) {
//验证是否通过,默认为true通过
boolean isCheckSucc = true;
for (String unitKey : keyAry) {
//按照括号拆分,区分是必填还是选填,参数传入就要进行验证正则
//获取"("首次出现的位置
boolean isOptional = isOptionalParam(unitKey);
//非必传
if (isOptional) {
unitKey = removBrackets(unitKey);
}
//针对每组参数进行必传校验、针对每组参数进行格式校验
if (!check(apiUrl, unitKey, json, !isOptional)) {
//验证失败
isCheckSucc = false;
break;
}
}
return isCheckSucc;
} /**
* 根据apiUrl从配置文件读取key
*
* @param apiUrl
* @return apiUrl 对应的入参配置
*/
public String getKeyByApiUrl(String apiUrl) {
//例如:请求的apiUrl为 /sys/login.do 则 key 为 PARAMS./sys/login.do
// 其中apiUrl 对应的参数值为 username,password
return ConfigUtils.getConfig(REGKEY + apiUrl);
} /**
* 根据apiUrl从配置文件读取入参对应的正则
*
* @param apiUrl
* @return
*/
public String getKeyRegex(String apiUrl, String paramKey) {
// 优先根据 PARAMS.接口URL.参数.regex 为key获取对应的正则校验规则
String key = REGKEY + apiUrl + "." + paramKey + REGEX;
//TODO 根据 key 读取 regex.properties 配置文件获取正则表达式
String keyRegex = StringUtils.trim(ConfigUtils.getConfig(key));
if (StringUtils.isEmpty(keyRegex)) {
// 如果根据 PARAMS.接口URL.参数.regex 为key没有对应的正则校验规则
// 则直接根据参数 参数.regex 为key 获取配置的正则校验规则
keyRegex = ConfigUtils.getConfig(paramKey + REGEX);
}
return keyRegex;
} /**
* 真正的校验逻辑,通过配置的正则表达式校验字段是否符合规范
*
* @param apiUrl 待校验的接口URL
* @param paramKey 参数key
* @param json 传入的json报文串
* @param must 是否必传参数
* @return 验证结果
*/
protected boolean check(String apiUrl, String paramKey, JSONObject json, boolean must) {
String value = StringUtils.trim(json.getString(paramKey));
if (StringUtils.isEmpty(value)) {
// 必传字段为空,验证fail
if (must) {
System.out.println(String.format("Request Parameter %s is Empty", paramKey));
return false;
}
} else {
String regex = getKeyRegex(apiUrl, paramKey);
if (StringUtils.isEmpty(regex)) {
System.out.println(String.format("Request Parameter %s found no regex info", paramKey));
return true;
}
// 去掉空格字符
value = value.replaceAll("\\s*", "");
// 不符合正则表达式
if (!value.matches(regex)) {
System.out.println(String.format("Request Parameter %s[%s] cannot match the regex[%s]", paramKey, value, regex));
return false;
}
}
return true;
} /**
* 去除中括号
*
* @param key 待校验的Key
* @return 去除中括号的字符串
*/
private String removBrackets(String key) {
return key.substring(1, key.length() - 1);
} /**
* 是否是必须的字段
*
* @param key 待校验的Key
* @return 必须字段返回<tt>true</tt>, 否则返回<tt>false</tt>
*/
private boolean isOptionalParam(String key) {
return key.startsWith("[") && key.endsWith("]");
}
}

  

好了,时间也不早了,生撸验证框架就分享到这儿吧,其实还有很多细节没有深入去说,只是讲了大体的思路,不过代码中注释已经写的很清晰了。如果你能搞明白,并在实际项目中应用,那肯定会大幅度提升开发效率,腾出更多时间去冲咖啡。

看完这篇硬核文章,懂与不懂,都请多点赞、收藏、分享,因为你每一个不经意的神操作,我都认真当成了喜欢。

徒手生撸一个验证框架,API 参数校验不再怕!的更多相关文章

  1. 纯手工撸一个vue框架

    前言 vue create 真的很方便,但是很多人欠缺的是手动撸一遍.有些人离开脚手架都不会开发了. Vue最简单的结构 步骤 搭建最基本的结构 打开空文件夹,通过 npm init 命令生成pack ...

  2. 老司机教你用原生JDK 撸一个 MVC 框架!!!

    其实 Spring MVC 是一个基于请求驱动的 Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则分发给相应的页面控制器进行处理,具体工作原理见下图. 在这里,就不详细谈相关的原 ...

  3. API参数如何验证?别纠结,拿去用就是

    今天我们主要分享项目实战中,另一种常用的参数校验框架 Hibernate Validator,请准备好小板凳,我们的分享开始. 1. 是啥? 先抛一张图,锻炼一下你的观察力. 通过上图有没有发现,数据 ...

  4. @Validated和@Valid区别:Spring validation验证框架对入参实体进行嵌套验证必须在相应属性(字段)加上@Valid而不是@Validated

    Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR- ...

  5. strus2验证框架

    为什么要用验证框架? 当验证规划比较复杂时,Action类的代码江边的非常繁琐,假如我们要对电话号码进行验证,是非常麻烦的. 验证框架的优点 Struts2中内置了一个验证框架,将常用的验证规则进行了 ...

  6. SpringBoot Validation参数校验 详解自定义注解规则和分组校验

    前言 Hibernate Validator 是 Bean Validation 的参考实现 .Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的 ...

  7. Spring Boot 2.x基础教程:JSR-303实现请求参数校验

    请求参数的校验是很多新手开发非常容易犯错,或存在较多改进点的常见场景.比较常见的问题主要表现在以下几个方面: 仅依靠前端框架解决参数校验,缺失服务端的校验.这种情况常见于需要同时开发前后端的时候,虽然 ...

  8. 测试开发专题:如何在spring-boot中进行参数校验

    上文我们讨论了spring-boot如何去获取前端传递过来的参数,那传递过来总不能直接使用,需要对这些参数进行校验,符合程序的要求才会进行下一步的处理,所以本篇文章我们主要讨论spring-boot中 ...

  9. SpringBoot 如何进行参数校验,老鸟们都这么玩的!

    大家好,我是飘渺. 前几天写了一篇 SpringBoot如何统一后端返回格式?老鸟们都是这样玩的! 阅读效果还不错,而且被很多号主都转载过,今天我们继续第二篇,来聊聊在SprinBoot中如何集成参数 ...

随机推荐

  1. Windows SMBv3 CVE-2020-0796漏洞

    今天,Microsoft不小心泄露了有关新产品的信息 蠕虫的 Microsoft服务器消息块(SMB)协议中的漏洞(CVE-2020-0796). 今天,Microsoft不小心泄露了有关安全更新的信 ...

  2. 05 Linux目录速查表

    /:根目录,一般根目录下只存放目录,在 linux 下有且只有一个根目录,所有的东西都是从这里开始 当在终端里输入 /home,其实是在告诉电脑,先从 /(根目录)开始,再进入到 home 目录 /b ...

  3. Java多线程并发01——线程的创建与终止,你会几种方式

    本文开始将开始介绍 Java 多线程与并发相关的知识,多谢各位一直以来的关注与支持.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程的创建方式 在 Java 中,用户常用的主动创建 ...

  4. 学习ConcurrentHashMap1.7分段锁原理

    1. 概述 接上一篇 学习 ConcurrentHashMap1.8 并发写机制, 本文主要学习 Segment分段锁 的实现原理. 虽然 JDK1.7 在生产环境已逐渐被 JDK1.8 替代,然而一 ...

  5. vue基础响应式数据

    1.vue 采用 v……vm……m,模式,v---->el,vm---->new Vue(实例),m---->data 数据,让前端从操作大量的dom元素中解放出来. 2.vue响应 ...

  6. IPv6 时代如何防御 DDoS 攻击?

    在互联网世界,每台联网的设备都被分配了一个用于标识和位置定义的 IP 地址.20 世纪 90 年代以来互联网的快速发展,联网设备所需的地址远远多于可用 IPv4 地址的数量,导致了 IPv4 地址耗尽 ...

  7. H5新特性之语义化标签

    一.为什么要增加新的语义化标签 在HTML 5出来之前,我们用div来表示章节,但是这些div都没有实际意义,这样的布局方式使我们的结构不够清晰,于是语义化标签应运而生. 二.何为语义化标签 顾名思义 ...

  8. jvm 性能调优工具之 jps 命令详解

    JPS名称:jps - Java Virtual Machine Process Status Tool命令用法:jps [options] [hostid] options:命令选项,用来对输出格式 ...

  9. 9. selenium+request方式的cookie绕过

    1. 首先确认POST请求的content-type类型 2. 查看cookies数据 3. 找到对应的headers数据 4. 如果是application/json,传入的json数据需要时jso ...

  10. 超实用的Flask入门基础教程,新手必备!

    Flask入门基础教程 Flask简介 Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活.轻便.安全且容易上手.它可以很好地结合MVC模式进行开发,开发人员分工合 ...