持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的26天,点击查看活动详情
一、背景
事情是这样样子的,在个人信息隐私保护的大背景下,我司对项目中的涉及隐私的参数进行了一次治理,其中对各个接口中的参数就是其中的重要一趴。
在众多的参数中有一个参数就是大家熟悉的 openid,对应的治理方案是把他改个名字,叫做xxfp(这看起来好像治理了,又好像没有治理😂😂)。
接着有趣的事情来了,最后因为各种原因,openid 确实是没有了,但是 xxfp 也没有了,就这么上线了。。。(别问我们的 qa 在干什么,我司外包qa,我当他们都已经狗带了,干啥啥不行,甩锅第一名)。
然后某个周六晚上11点,刚刚哄儿子睡着,正准备交给我老婆的时候,老板来电话了!我赶紧和老婆说,”出事儿了,这个点打电话应该是大事!”
我老板:”线上现在无法支付的 case 激增,触发收银台报警,经查 openid 参数没有了,这个参数为啥没有了?“
我:”个保法整改时全部换成了,xxfp 参数了!“
我老板:”但是 api(我司后端人员)说没有这回事儿!“
我:”当时有个在线文档,是经后端人员确认过的,这文档上 openid 确实被标记不必要,且改名 xxfp“
我老板:”这看起来是改了后 api 没有透传给收银台!“
我:”有看过 xxfp 在吗?“
QA:”在啊,我刚还看到,是吧 xs(我领导名字缩写)“
我:”我看下吧!“
......切分支、拉代码、装依赖、打包... 2000 Years Later ....
我:“没有啊,新表单没有 xxfp 啊?我 master 最新代码...”
.... 集体沉默了(我已经用在用 C 和 B 语言访问 QA 的祖先了)....
细细想来,这是第二次被接口参数坑了,搞着搞着就丢了,瞎眼 QA 指不上,最后一上线发现出事后再复盘。你肯定会好奇,为啥你会发现不了?是因为下单接口有上百个参数。。。
所以问题就来了,当复杂的 C 端应用参数越来越多,各参数类型也是五花八门,那么有没有很好的方案来约束参数个数、类型呢?
二、方案调研
经过初步的调研,我们有了以下方案:
2.1 TypeScrip
TS 做类型校验,正可谓行家里手了,但是 TS 生效是在编译时完成的,对于接口中的参数很多都是在运行时获取的,这种情况下不是 TS 所长;
2.2 json-schema
json-schema,这个东西大家可能有点陌生,但是大家可能经常用,有一个常见的场景就是 webpack.config.js 的配置文件校验就是通过类似的方式做的。 json-schema 是通过预定规则,校验 js 对象。
json-schema 看起来就是不错的解决方案了,但是有个场景,如果某些参数间是有关联,比如:
下单接口参数 a,当 a 为 1 的时候,另一个下单接口参数 multi_gf 应该是一个数组,否则 multi_gf 就必须为 null。这个时候 json-schema 就处理起来就不够灵活了;
2.3 自研方案
细细分析一波,你会发现校验接口参数的诉求并不复杂:
-
约定强校验的参数,通过上面的 json-schema 可以发现以 schema 的形式就很好;
-
类型要支持以下几种情况:
- 2.1 指定的值,比如 1,"username";
- 2.2 符合某个规则的字符串,通过正则约束;
- 2.3 基本类型的值:string number null undefined boolean;
- 2.4 枚举值:enum;
- 2.5 interface:校验复杂的比如 { id: 13, hash: 'badk23u##ddd', pid: 'dak;flekk;$' };
- 2.6 数组:array[string/number/null/undifined/boolean/interface] / array[any];
- 2.7 处理特殊的参数间联动的场景;
-
支持设置公共参数抽离,这个很好理解,比如 token、openid 这种每个接口都需要的,所以就不用每个接口都写;
-
当遇到参数不满足 schema 约定的时候能够提示开发者;
经过上面一对比发现肯定是得自研方案更适宜了,不然这个小系列到此就结束了😂
三、方案落地
3.1 这个工具的形式
为了确保能够拿到最终的参数(这里还有个小彩蛋,看官要注意啊,你先思考这),最好的形式就是以网络库的请求拦截器的形式存在了,例如 axios 的请求拦截器;
axios.interceptors.request.use(function validate () {
// 这里进行校验
})
3.2 设计 schema
举个例子:校验 some/api/name 下面的 paramName 参数,paramName 参数值是一个枚举类型,枚举值为 28 或者 29;
一个 schema 是导出一个 js 对象,接口参数校验维度就是以接口展开的,对象的 key 就是要校验的接口名,例如下面的 some/api/name 这个接口。
接口对应的值称为参数对象
,该对象中所有的 key 是需要校验的参数名,例如下面的 paramName。参数名对应的值是称为类型对象
,其中 type 表示 paramName 参数名的类型,例如下面的 enum 标识枚举类型,当然了,既然是枚举,肯定需要告知枚举范围,这也就有了类型细则信息 enumRange 字段,类型细则和类型对应,下面详述;
除了使用 type 和 value 外,类型对象还支持一种 assert 函数的形式,assert 函数需要用户自行决定是否通过校验,当 assert 返回 true 的时候认定通过校验,返回的其他类型的值均视为没通过校验,且返回值自身作为报错信息抛出;
注意,assert 函数的优先级最高,如果设定了 assert 函数,type 和 value 会被忽略掉;
// schema 主体
export default {
// 接口名
'some/api/name': {
// 参数名
'paramName': {
type: 'enum', // 参数 paramName 的类型
enumRange: [28, 29] // 这个对象中除了 type 以外的其他 key 都是类型需要的细节部分
}
}
}
四、总结
本篇小作文给大家介绍了一下这个工具诞生的背景,从网上搜了一下似乎没有啥好的方案,估计像我司这种上百个参数、还碰上这种优秀的 QA 毕竟是小概率事件,工具需要满足以下的要求:
-
校验参数类型,目前设计的是 schema 形式,只要 schema 中声明的参数都认定为必须传;
-
我们设计了一种 schema 的形式来解决类型声明的问题,另外设计了 assert 函数来解决参数间有关联的场景;
下一篇会详述这个工具支持的类型以及 assert
函数的具体用法!