持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的28天,点击查看活动详情
一、 背景 & 前情回顾
在上一篇小作文详细讨论了这个接口校验工具支持的类型:
- 基本类型;
- 基本联合类型;
- 正则;
- 固定值;
- 接口;
- 数组,包含精确数组和任意数组,精确数组项直至以上所有类型;
- assert 函数,搭配
provide指定联动参数,可以解决参数互相联动的问题;
本篇,我们开始讨论具体的实现;
二、代码组织设计
我们先根据用法和初步的功能进行以下几个方法的设定:
2.1 构造函数
写可复用代码么,外加装逼需要,肯定得封装一个类,我们这个类叫做 RuleSet,是的和 webpack 用于处理 loader 的类同名;
2.2 执行匹配
此外这个类还有个 exec 方法,用于处理接口参数的校验,当然了这和 RuleSet 的实例方法 exec 作用是一样的;
2.3 错误处理
另外,还记得这个需求最初在介绍的时候有说过如果接到参数错误需要提示。那这个提示用 console.error 还是 throw new Error?这个咋考虑?这个不用这么复杂,为了确保通用,我们设计一个添加错误处理的方法,我们把错误传递给错误处理 handler 不就好了,至于用户喜欢 console 还是喜欢 throw 那就见仁见智吧。这个方法我们叫做 addErrHandler 方法;
2.4 向网络库添加拦截器
说完构造函数和 exec、addErrHandler 方法,接着我们设计一下向网络库添加拦截器;前面有说过,我们的核心就是使用网络库的拦截器机制,所以这里我们设计一个向受网络库添加拦截器的方法 doIntercept;
经过起那么的设计我们的 RuleSet 方法大致如下:
class RuleSet {
constructor (xfetch, schema) {}
exec (cfg) {},
addErrHandler (handler) {},
doInterceptor (xfetch) {}
}
// 这个方法用于返回一个 RuleSet 实例
// 之所以会这样,是因为 ESLint 禁用了 new 操作符
export default function createApiParamCheckInstance (xfetch, apiSchema) {
return new RuleSet(xfetch, apiSchema)
}
三、用法
前面我们已经设计了代码组织,现在看看怎么用这个东东:
- 第一步通过调用 createApiParamCheckInstance 方法得到 RuleSet 实例;
- 添加错误处理函数,即调用 addErrHandler 方法添加错误处理函数,当然不添加肯定也是可以的,我们在代码内部设置了默认的 console.error 的方式再控制台输出错误信息;
// 创建 RuleSet 实例
let rules = createApiParamCheckInstance(axios, apiSchema);
// 添加错误处理函数
rules.addErrHandler(err => {
someUI.showModal({
content: err.map(itm => itm.msg).join('/n/r')
})
})
四、异常类型设计
在详细展开 RuleSet 设计之前,我们先设计一波类型对应的错误:
4.1 错误类型
根据前面我们设计的类型,我们设置了如下错误类型,公有五种:
-
方法错误,所谓方法是 HTTP-METHOD,这个作为扩展项我们并没有过多讨论,这个类型标识本次请求方法错误,例如这个接口强制 POST 请求,如果使用 GET 请求就应该被识别为错误。不过我们这个工具主要是校验参数的,所以这部分作为扩展再次不过多展开;
-
值类型错误,用于校验当类型被指定具体的某个值的失败时抛出的错误;
-
断言函数失败,自定义 assert 函数执行未返回 true 的情况对应的报错提示信息;
-
正则校验失败,用于校验当类型为指定正则时未通过的错误;
-
类型错误,这个就是指定 type 字段并且没有通过校验的时的错误;
const ME = 'MethodError'
const VE = 'ValueTypeError'
const AF = 'AssertFailed'
const RF = 'RegExpTestFailed'
const TE = 'TypeError'
4.2 错误生成函数
针对上一步设定的错误类型,我们设计了对应的类型错误对象生成函数,关于为什么设计这玩意儿主要有以下几点:
-
错误提示也许尽可能的详细,当用户看到报错信息时可以迅速的找到定位到问题,友好的报错信息也是一个工具成熟的表现;基于这个预期,各位看官老爷可以想一想,作为一个用于校验接口请求参数的工具,你觉得哪些信息可以更友好、更迅速的让你定位到有问题的参数?我能想到的几点如下:
- 1.1 类型错误;
- 1.2 接口名称;
- 1.3 http method;
- 1.4 报错提详细信息,详细信息也有关类型,如果是类型错误,需要输出字段名、当前的值、期待类型、当前类型,如果是 assert 函数,则把 assert 返回的非 true 的值都作为错误信息输出;
-
另外这么设计这个东东是为了更好的把辅助逻辑重校验逻辑中解耦出来,另外利于项目的后期维护;
import { AF, ME, RF, TE, VE } from './constants'
import { type } from './utils'
// 生成 http method 类型错误
const genMethodErr = (u, s, m) => ({
type: ME,
api: u,
method: m,
msg: `Unexpected ${ME}: schema requires "${s.method}" instead!`
})
// 生成值类型错误
const genValueErr = (u, s, p) => ({
type: VE,
api: u,
paramKey: p,
msg: `Unexpected ${VE}: value is not equal with "${s.value}"!`
})
// 生成 assert 函数校验失败
const genAssertFailed = (u, s, p, e) => ({
type: AF,
api: u,
paramKey: p,
msg: `Unexpected ${AF}: schema.assert failed! ${e && type(e) !== 'String' ? JSON.stringify(e) : e}`
})
// 生成正则校验失败类型错误
const genRegExpTestFailed = (u, s, key, value) => ({
type: RF,
api: u,
paramKey: key,
paramVal: value,
msg: `Unexpected ${RF}: ${s.type.toString()} tests parma failed!`
})
// 生成类型校验失败错误
const genTypeError = (u, s, pn, pv) => ({
type: TE,
api: u,
paramKey: pn,
paramVal: pv,
msg: `Unexpected ${TE}: cannot assign to the "${s.type}" type!`
})
export {
genTypeError,
genMethodErr,
genValueErr,
genRegExpTestFailed,
genAssertFailed
}
五、总结
本文开始着手实现我们前面的诉求——一款校验接口参数类型的工具。本文目前算是详述了一下这个工具的架构设计,目前包含以下几方面:
-
RuleSet 类的设计以及类型上的 exec/addErrHandler/exec 方法;
-
工具设想已经完成的请情况下,设计如何使用这个工具;
-
设计工具可能用到的类型校验失败时对应的错误以及生成错误对象的方法及错误提示信息设计;