Typescript 声明校验

Typescript 声明校验

最近线上发生了几次这样的问题:由于后端接口响应的 JSON 数据中某些字段缺失,前端无法正常获取数据,导致前端报错,用户无法正常交互。这些问题通过后端紧急修复也恢复正常了,但想想这类问题是否能在前端捕获到这种异常情况呢?这些问题都是通过用户反馈才发现的,如果在前端获取请求时捕获到后端接口返回数据格式与接口文档不一致的情况,就能在第一时间及时处理。

怎么校验后端接口返回数据格式与接口文档不一致呢?想到了 JSON Schema,具体的使用语法可以去官网里查阅。

下面通过这个接口进行校验

接口声明

根据上面接口返回内容声明接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type REQUEST_METHODS = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONAL';

/**
* Response.data 结构
*/
interface Response_Data<T> {
code: number;
type: string;
message: T;
}


/**
* Response 结构
*/
export interface Response<T> {
api: string;
data: Response_Data<T>;
method: REQUEST_METHODS;
status: boolean;
track_id: string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Response } from './basical';

interface BannerImg {
id: number;
enabled: number;
created_at: string;
image_url_mobile: string;
image_url_pc: string;
redirect_blank: number;
redirect_url: string;
scenario: string;
updated_at: string;
}

export interface BannerImage extends Response<BannerImg> { }

声明转 JSON Schema

通过 Typescript-json-schema 将上诉接口声明转换成 JSON Schema。

1
typescript-json-schema ./src/types/bannerImage.ts '*' -o ./src/types/bannerImage.json --id=bannerImage --required   --strictNullChecks

校验

生成 Schema 后,最重要的一步就是对后端接口返回内容进行校验,校验需要借助一些库来进行校验,如:jsonschemaajv。下面以 jsonschema 库进行校验。

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
const queryBannerImage = (): void => {
const data = {
"data": {
"type": "",
"code": 0,
"message": {
"id": 38,
"scenario": "login-page",
"image_url_pc": "http://pingpp-module-batch-test.oss-cn-hangzhou.aliyuncs.com/nova/images/xsTbtu16ibQhXvHo5c4AP1Ds7DZTwDxCXIyZyluF.png?OSSAccessKeyId=LTAIc8oA2lfdOEzv&Expires=1592124248&Signature=5KXyfRype65K3Wdqttz3Qn3iCLQ%3D",
"image_url_mobile": "http://pingpp-module-batch-test.oss-cn-hangzhou.aliyuncs.com/nova/images/LY2GGlMUguPVMT1F4Glh00zZ4gHSKgZi60ugqdvv.png?OSSAccessKeyId=LTAIc8oA2lfdOEzv&Expires=1592124248&Signature=dKNGNMrhFaPIUqCbPCbERuRnfMw%3D",
"enabled": '1',
"redirect_url": "www.baidu.com",
"redirect_blank": 1,
"created_at": "2019-12-17 16:44:08",
"updated_at": "2019-12-17 16:44:08"
}
},
"status": true,
"api": "https://dashboard.pinpula.com/api/callback/banner/image?scenario=login-page",
"method": "HEAD",
"track_id": "log_5i90u9KaDCCSGm1ub5nv1CiD"
};

validateResponse(data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const validateResponse = (data: any) => {
const validator: Validator = new Validator();
validator.addSchema(schema, '/bannerImage');

// bannerImage#/definitions/
const result: any = validator.validate(data, { $ref: 'bannerImage#/definitions/BannerImage' });

console.group('校验结果:');
console.info(`请求 URL:${data.api}`);
console.warn(`校验结果:${result.valid}`);
if (!result.valid) {
// 异常上报

result.errors.map((item: any) => {
console.error(item);
console.error(item.toString());
});
}
console.groupEnd();

return data;
}

按校验提示对 data.message.enabledmethod 字段进行调整。

总结

  • 新增接口需要按照接口文档进行接口声明并转换为 Schema;

  • 为了避免接口变更导致未转换成相应的 Schema,在编译流程中增加转换 Schema 的步骤

  • 接口返回数据校验失败时,将接口信息进行异常上报