合作QA是大聪明?撸个接口校验工具保命(1)

181 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 自研方案

细细分析一波,你会发现校验接口参数的诉求并不复杂:

  1. 约定强校验的参数,通过上面的 json-schema 可以发现以 schema 的形式就很好;

  2. 类型要支持以下几种情况:

    • 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 处理特殊的参数间联动的场景;
  3. 支持设置公共参数抽离,这个很好理解,比如 token、openid 这种每个接口都需要的,所以就不用每个接口都写;

  4. 当遇到参数不满足 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 毕竟是小概率事件,工具需要满足以下的要求:

  1. 校验参数类型,目前设计的是 schema 形式,只要 schema 中声明的参数都认定为必须传;

  2. 我们设计了一种 schema 的形式来解决类型声明的问题,另外设计了 assert 函数来解决参数间有关联的场景;

下一篇会详述这个工具支持的类型以及 assert 函数的具体用法!