0%

乱炖fetch、json_Schema

背景

既有项目,前端以jQuery为主。和后端的接口做了调整,需要前端对接。

接口数据以json格式定义,非常复杂,手工检查起来非常耗费精力和时间。所以寻求解决方案。

json schema

先找到了json schema。简单来说它可以约束json数据格式,这里正好可以规范接口数据格式。声称的可以和前端UI发生关联看起来挺美好,没有进一步研究。

它只是一套规范,针对各种编程语言有不同的实现。另外它还有不同的版本,不同版本之间目测差距还很大,需要注意。

生成json schema数据

首先,json schema数据也是json格式。

当然可以手写json schema,但是针对复杂的数据格式,这不太现实。
好在虽然没有接口规范文档,但是有一份接口示例数据,这里提供了根据json数据生成json schema规范文件的功能。这么做之前,你需要确保示例json数据符合json语法。这个工具不提供json语法检查。另外一个在线工具可以提供json语法检查。两者生成的规范版本不同,高版本的比低版本的内容会多很多。

校验接口数据

这里有json schema的各种语言实现版本,javascript语言实现了校验功能的有ajvdjv以及vue-vuelidate-jsonschema 。因为项目不是纯vue,所以没有尝试后者,再因为既有框架给予requirejs,尝试发现avj可行,就用了它。

查看avj的文档,使用方法大致如下:

1
2
3
4
5
var Ajv = require('ajv');
var ajv = new Ajv(); // options can be passed, e.g. {allErrors: true}
var validate = ajv.compile(schema);
var valid = validate(data);
if (!valid) console.log(validate.errors);

其中,schema就是一个json内容,也就是前面生成的json schema内容。很明显,这里需要把生成的内容加载进来,才能够实现校验。

在发送json数据时加载对应的数据格式文件,通过ajv校验发送的数据格式。

在得到数据后,加载相应的数据格式文件,同样通过ajv校验得到的数据格式。

以上就是校验接口数据的思路。

fetch

fetch用的不多,先前用jQuery的ajax不觉得什么,但对比axios,在处理多个请求的时候真的很痛苦。这次碰到的问题很明显就是这样的,做一次请求,前后需要加载两次json文件。项目实际的情况不需要考虑老旧浏览器的兼容问题,同时也没有必要为了这一处改造单独引入axios,所以就用原生支持的fetch吧。

说起fetch,不得不提到Promise,个人觉得很难用三言两语把它说清楚,但尝试过后你会有很深刻的理解。在服务器端,它的用途更广。在这里姑且简单的理解它是一个存放一步操作结果的容器吧。fetch操作后,得到的就是这样一个容器。ES6语法层面有进一步操作这些单独容器的支持,这里不做进一步的说明了。详情可以移步ECMAScript 6 入门仔细研究。下面是具体实现的代码:

1
2
3
4
5
6
let doSomeThing = fetch(BASE_PATH + '/dosomething', fetchInit)
.then(resp => resp.json());
let getSendConfig = fetch('../pathA/config.json?r=' + Math.random() * 100)
.then(resp => resp.json());
let getGetConfig = fetch('../pathB/config.json?r=' + Math.random() * 100)
.then(resp => resp.json());

代码中的fetchInit是一个对象,通过它对发送数据请求做了设置,比如请求的数据、格式等。

前面说过fetch返回的是一个promise对象,.then语句操作的是这个promise对象,同时它返回的仍然是一个promise对象。我们可以一直执行需要的后续步骤。

以上三条语句,执行了网络请求,后续操作都进一步把结果处理成了json格式。需要说明,上面三条语句是异步操作,发起请求的顺序和得到反馈的顺序无法做到一致。下面要从三个Promise对象中获取结果了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let param=...// 发送的json数据
...
let allPromise = Promise.all([doSomeThingResult, sendConfig, getConfig])
.then(function ([resultJson, sendConfigJson, getConfigJson]) {
let ajv = new Ajv();
let validate = ajv.compile(sendConfigJson);//指定校验配置
let valid = validate(param);// 校验发送数据
if (valid) {
console.log('发送数据校验没有异常!');
}
else {
console.error('发送数据校验发现异常: ' + ajv.errorsText(validate.errors));
}

let validate2 = ajv.compile(getConfigJson);//指定校验配置
let valid2 = validate2(resultJson);// 校验返回数据
if (valid2){
console.log('返回数据校验没有异常!');
}
else{
console.error('返回数据校验发现异常: ' + ajv.errorsText(validate2.errors));
}

...// 执行正常的业务逻辑
})
.catch((resp) => {
// 提示出错
});

这里,Promise.all等待前面三个请求都有了结果后才执行后续步骤,任何的请求错误都会在末尾的catch中执行,then中的方法实现了上述的校验思路,这里不再重复了。

以上写法的执行一次只能查到一处数据格式的问题,所以校验完整的数据格式,可能需要多次尝试。没有进一步研究ajv是否支持一次检查出所有格式问题。

参考

使用 Fetch