乐趣区

关于react.js:写一个简单的JSON-Schema为了应对后台接口细节的口口相传

什么是JSON Schema

怎么定义呢,说句实话我是写完了才发现,这不跟传说的 JSON Schema 差不多么,so,我就说 ta 是JSON Schema(我了解的,正不正经不晓得)。

正经的定义

Json Schema 定义了一套词汇和规定,这套词汇和规定用来定义 Json 元数据,且元数据也是通过 Json 数据模式表白的。Json 元数据定义了 Json 数据须要满足的标准,标准包含成员、构造、类型、束缚等。

我因为啥写

始终以来和后盾进行联调这件事都是一件很 ”困扰 “ 的事件,次要是 沟通老本太大 ,其次要集中在接口文档的沟通上,不是这个参数值不必传,就是那个值必须写的,或者某些参数必须是什么类型的。还都不落实到文档,这么说也不谨严,他有文档,但 不“清晰”,还是防止不了沟通,或者有改变他不更新,毕竟制订规矩并严格遵守可没间接口口相传来的“难受”,就是 图不便 ,但这就 很危险 ,大家一旦同步错乱,性能就写错,那节约的人力物力还有空口白牙,没处说理去所引起的心态崩了,这几乎 无奈承受 ,与其在困扰中忍耐,不是想个办法“ 变一变”,so,我就搞了这个。

那么开始上菜

设计思路

首先我冀望 ta 可能在我申请的时候,帮我办两件事:

  1. 符不合乎后盾的要求:在申请的时候,对入参(发送给后盾数据)进行相应的校验。
  2. 符不合乎前台的要求:在申请回数据的时候,对出参(后盾返回的数据)进行相应的校验。

而后,在补上一个特点

  1. 可配置

接下来开整吧☺。

配置办法

先贴出配置数据,而后咱们再剖析:

import BaseApiShape from './baseApiCheck'


let config = {
    fetchTestDataList: {
        reqParam: {type: {},// 不写默认是对象
            p1: {type: "string",// 要求是什么类型,传入类型名,否者间接取传入变量的类型},
            p2: {type: {},
            },
            p13: {type: "string",},
        },
        resParam: {
            p1: {type: [],
            },
            p2: {type: {},
            }
        }
    },
    fetchLightDetail: {
        resParam: {type: [],
            _item: {
                lightItems: {type: [],
                    _item: {
                        exclude: {type: "boolean",}
                    },
                },
                aaaaa: {type: {},
                }
            }
        }
    }
}

export default new BaseApiShape({config})

剖析:

下面的两个校验个体十分典型,残缺的把我所有的校验性能都体现进去了:

  • fetchTestDataList
  • fetchLightDetail

大体构造是这样的:

    {
        reqParam: {...},
        resParam: {...}
    }

看名猜意 reqParamresParam,别离是代表的入参(前 => 后)和出参(后 => 前)。

而后咱们再持续深刻看一下:

先介绍一下约定的规矩:

type:就是示意要求的参数类型,至于怎么传,(根本)都行,比方一个字符串,你是传“string”间接通知我什么类型还是轻易给我个字符串变量我外部取一下类型都行,就是很随便。

_item:示意一个数组中元素,只会在是数组类型下呈现。

案例一:

    {
        // 剖析点:A
        type: {},
        // 剖析点:B
        p1: {type: "string", // 剖析点:C},
        p2: {type: {},
        },
        p13: {type: "string",},
    }

ABC 说明确 ta:

  • A:type示意的是这整体是一个对象。
  • B:p1示意这个构造中要名字为 p1 的属性。
  • C:p1外面的 type, 就示意p1 应该是什么类型。

而后再说一下案例二

案例二:

    {
        // 剖析点:A
        type: [],
        // 剖析点:B
        _item: {
             // 剖析点:C
            lightItems: {type: [],
                _item: {
                    exclude: {type: "boolean",}
                },
            },
            aaaaa: {type: {},
            }
        }
    }

ABC 说明确 ta:

  • A:type示意的是这整体是一个数组,(留神奥,这是数组哦!)。
  • B:_item示意数组中的元素。
  • C:lightItems阐明这还是一个数组,这就形成了一个嵌套数组,超级简单,没关系,反对!(这就曾经很深了,还深?那就应该和后盾的哥们聊聊了。)

咱们实战一下

fetchLightDetail 为例

返回的参数如果是这样的:

[{
    lightName: "test_lightName_0",
    comment: "test_comment_0",
    lightItems: [
        { 
            lightItemId: 1, 
            lightType: 0, 
            baseSelect: 1,
            exclude: 0 
        }
    ]
}]

那么校验后果就是:

曾经很暖心的把问题说进去了,够用了,当前再优雅吧,反正想法根本落地了。

贴一下没怎么润色的基类外部实现,没测透,预计有 bug(应该是必定的)

let typeArr = [
    "bigInt",
    "boolean",
    "string",
    "symbol",
    "undefined",
    "object",
    "number",
    "function",
    "array"
]
export default class BaseApiShape {constructor(props) {const { config} = props;
        this.config = config;
        this.checkReport = []}
    process(config) {for (let key in config) {this[key] = _.cloneDeep(config[key])
        }
    }

    checkReqParams({apiName, params}) {if (apiName in this.config) {
            return this.check({required: this.config[apiName].reqParam,
                current: params
            })
        }
    }

    checkResParams({apiName, params}) {if (apiName in this.config) {
            return this.check({required: this.config[apiName].resParam,
                current: params
            })
        }
    }
    checkArr({required, current = [], logPrefix }) {current.forEach((item, index) => {
            this.checkObj({
                required: required,
                current: item,
                logPrefix: logPrefix + `[${index}]`
            })
        })
    }
    check({required = {}, current }) {if (required.type === "array" || Array.isArray(required.type)) {if (Array.isArray(current)) {if (required._item) {
                    this.checkArr({
                        required: required._item,
                        current: current,
                        logPrefix: "整体"
                    })
                }
            } else {this.checkReport.push({ property: "整体", message: ` 要求数组!`})
            }
        } else {this.checkObj({ required, current})
        }
        return this.checkReport;
    }
    // 如果上来是数组,实际上其实判断就是本人的外部的元素,那就是判断多个对象了
    checkObj({required = {}, current = {}, logPrefix}) {for (let key in required) {let checkOption = required[key];
            if (Object.keys(checkOption).length) {const { isRequired = true, type} = checkOption;
                if (type === "array" || Array.isArray(type)) {if (checkOption._item) {
                        this.checkArr({
                            required: checkOption._item,
                            current: current[key],
                            logPrefix: ` ${logPrefix + "->" + key}`
                        })
                    }
                    continue;
                }
                // 校验 - 是否存在
                if (isRequired && !(key in current)) {this.checkReport.push({ property: key, message: ` 参数中并没有指定要求的属性:${key}` })
                }
                // 校验 - 类型
                if ((typeArr.indexOf(type) != -1) || typeof type in typeArr || Array.isArray(type)) {
                    let typeTemp;
                    if (typeArr.indexOf(type) != -1) {typeTemp = type;} else {typeTemp = typeof type;}
                    if (typeTemp !== typeof current[key]) {this.checkReport.push({ property: key, message: `${logPrefix && ('数组' + logPrefix)}: 参数中指定要求的属性类型不对,yes-${typeTemp}:no-${typeof current[key]}` })
                    }
                }
            }
        }
    }
}

结语

西为中用,古为今用,“难”的,“形象”的常识,等理解之后就明确其实是很多“简略”组成的(我不置信有 coder 对此没感悟),你技术再强,你没有“心”,你顶多是个机器,你技术再弱,你有“心”,你也是集体,不过分自卑,不不可一世,缓缓学吧,定个方向往那致力,管 ta 成不成呢😁,无成有终。

集成了该性能的🌰

退出移动版