【Karman.js】严格把关参数的验证引擎! - 03

先回过头来複习一下,在第一章有提到的 defineAPI 里,会有一个 payloadDef 物件,专门用来定义 FinalAPI 的第一个参数 payload 需要哪些属性,其中 {} 物件型别的 payloadDef,除了能够以 key 定义 payload 需求参数名以外,value 还能够是另一个物件来描述该参数的必填非必填(required)、验证规则(rules)、预设值(defaultValue)以及使用位置(position),而当中的 requiredrules 将会与验证引擎息息相关。

参数验证引擎

参数验证引擎是 Karman 内建的一个模组,负责解析 payloadDefrulesrequired,并在 FinalAPI 发起请求时验证 payload 是否符合规则。

启动验证引擎,需要透过 validation 属性来设定,取决于你要批次或单独启动 FinalAPI 的参数验证,可以被设置在包括 defineKarmandefineAPI 与 FinalAPI 的第二个参数 config 里,且设置的位置同样会遵循卡门树的继承原则。

可以设定 validation 来开启或关闭验证引擎的地方:

const karman = defineKarman({    // ...    validation: true, // 开启此节点以下的所有参数验证    api: {        someAction: defineAPI({            // ...            validation: false // 关闭这支 FinalAPI 的参数验证        })    }})const [resPromise] = karman.someAction(null, {    validation: true // FinalAPI 使用时再一次启用参数验整})// ...

设定验证规则

在 Karman 中,有许多内建的规则可以使用,其中大部分都会自动生成错误讯息,并点明验证失败的参数,除了特别複杂或特殊的规则,不然通常情况下不太会需要使用到客製化的验证函式。

payloadDef 中,若想设定参数的验证规则需要透过 rules 属性,但若是想设定参数为必填需要通过 required,且所有参数预设情况下都会是非必填参数,若是想让参数为必填,即不为 null 或是 undefined,需要将 required 属性设置为 true

const requiredTest = defineAPI({    // ...    validation: true,    payloadDef: {        param01: {            required: true        }    }})requiredTest() // Uncaught ValidationError: Parameter 'param01' is required, but received 'undefined'.

rules 会决定要用甚么规则去验证接收到的参数,以下是 Karman 当中可以使用的验证规则:

字串规则

以字串描述参数的型别,除了基本型别("string""number"...等)外,也会有一些特殊型别:

"char":字符,长度为 1 的字串"int":整数"object":广义上的物件"object-literal":以 {} 构成的物件"array":阵列

其余内建型别可以参考官方文件。

除了内建的型别外,后续还会介绍 Karman 的一个核心叫「Schema API」,透过这功能订定出来的 schema 也能作为字串规则使用,之后会再详细介绍。

另外,字串规则还支援一种阵列语法,类似其他强型别语言宣告阵列时的语法,如 C# 可以用 int[] 宣告一个整数阵列,使用这个语法作为验证规则时,参数的型别必定是一个阵列,只是可以额外去规範阵列内每个元素的型别,并且还能透过以下的语法,去限制该阵列的长度:

<type>[]               # <type> 阵列<type>[<equal>]        # 长度 <equal> 的 <type> 阵列<type>[<min>:]         # 最小长度 <min> 的 <type> 阵列<type>[:<max>]         # 最大长度 <max> 的 <type> 阵列<type>[<min>:<max>]    # 最大长度 <max>、最小长度 <min> 的 <type> 阵列

现在,尝试看看以字串规则验证参数,并记得将 validation 设置为 true 来开启验证引擎,另外,这边多配置了个 onError hooks,并在里头返回了非 undefinednull 的值,使下面 FinalAPI 在调用时可以不被错误给中断执行:

关于 hooks 的机制会在后续章节详细说明。

const test = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: "char"        },        param02: {            rules: "int"        },        param03: {            rules: "string[]"        },        param04: {            rules: "char[6]"        },    },    onError(err) {        console.error(err)        return 1    }})test({ param01: "K" })                                 // validtest({ param01: "Karman" })                            // ValidationError: Parameter 'param01' should be 'char' type, but received 'Karman'.test({ param02: 10 })                                  // validtest({ param02: 0.1 })                                 // ValidationError: Parameter 'param02' should be 'int' type, but received '0.1'.test({ param03: ["Karman", "Victor"] })                // validtest({ param03: ["Karman", "Victor", 6] })             // ValidationError: Parameter 'param03[2]' should be 'string' type, but received '6'.test({ param04: ["K", "a", "r", "m", "a", "n"] })      // validtest({ param04: ["K", "a", "r", "m", "a", "n", "!"] }) // ValidationError: Parameter 'param04.length' should be equal to '6', but received '7'.test({ param04: "Karman" })                            // ValidationError: Parameter 'param04' should be 'array' type, but received 'Karman'.

类别规则

在类别规则中,你可以用一个 class 或建构函式做为验证规则,验证引擎会以 instanceof 对参数进行验证:

const instanceTest = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: Date        }    }})instanceTest({ param01: new Date(1995, 5, 27) }) // validinstanceTest({ param01: "1995-06-27" })          // Uncaught ValidationError: Parameter 'param01' should be instance of 'Date', but received '1995-06-27'.

自定义验证函式

若是要使用客製化的验证函式,需要使用到 defineCustomValidator 这支函式,它只接受一个参数,即为你要使用的自定义验证逻辑的函式,这函式会接收两个参数,第一个是参数名称,第二个是参数的值,你可以对第二个参数进行验证,不论验证通过与否,皆不须返回任何值,只需在验证未通过时直接抛出错误就好,并且 Karman 有提供了一个 ValidationError 类别,是验证引擎预设抛出的错误类型,你可以藉由抛出此错误来统一所有验证失败时的错误讯息,以便后续的程式判读:

import { defineAPI, defineCustomValidator, ValidationError } from "@vic0627/karman"const sortValidator = defineCustomValidator((param, value) => {    if (value !== "asc" || value !== "desc")        throw new ValidationError(`Parameter '${param}' must be 'asc' or 'desc' but received '${value}'.`)})const validatorTest = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: sortValidator        }    }})validatorTest({ param01: "aesc" }) // Uncaught ValidationError: Parameter 'param01' must be 'asc' or 'desc' but received 'aesc'.

正则表达式

以正则表达式做为验证规则时,接受两种规格的参数传入,一种是一般的正则表达式,另一种是一个物件,包含了正则表达式与错误讯息:

const emailRegexp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;const regexpTest = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: emailRegexp        },        param02: {            rules: { regexp: emailRegexp, errorMessage: "wrong email format" }        }    },    onError(err) {        console.error(err)        return 1    }})    const wrongEmail = "karman~"regexpTest({ param01: wrongEmail }) // ValidationError: Parameter 'param01' validation failed, but received 'karman~'.regexpTest({ param02: wrongEmail }) // ValidationError: wrong email format

参数描述符

此种规则主要以物件构成,可以规範包括最大值、最小值、相等值与测量属性等,以下是可用的属性与说明:

min:最小值,可与 max 一同设定。max:最大值,可与 min 一同设定。equality:相等值,优先权最高,若是有设定这属性,minmax 将被忽略。measurement:要以什么属性作为量测单位,预设是 "self",也就是测量参数值本身,而其他常见属性有 "length""size"...等等。

验证引擎会先在参数上寻找你所设定的属性,若是找不到,例如 "length" 属性不存在于 number 类型中,这时 Karman 将会丢出警告讯息,并以 undefined 作为后续验证的值,因此,此验证规则通常不会单独使用,而是会配合规则集合设定类型的前置条件,当通过前置规则的验证时,才会进行範围的验证。

const paramDescriptorTest = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: { min: 0, max: 1 }        },        param02: {            rules: { equality: 5, measurement: "length" }        }    },    onError(err) {        console.error(err)        return 1    }})paramDescriptorTest({ param01: 2 })// ValidationError: Parameter 'param01' should be within the range of '0' and '1', but received '2'.paramDescriptorTest({ param02: 2 })// [karman warn] Cannot find property "length" on "2".// ValidationError: Parameter 'param02.length' should be equal to '5', but received 'undefined'.paramDescriptorTest({ param02: "karman!" })// ValidationError: Parameter 'param02.length' should be equal to '5', but received '7'.

规则集合

规则集合,或称複合规则,可一次设定多组规则,依照不同的集合类型,会有不同的通过验证条件,在 Karman 中规则集合会分成下列两项:

联集规则 UnionRules:当参数满足联集规则中的任一项规则,即通过验证。交集规则 IntersectionRules:当参数满足交集规则中的所有规则,即通过验证。

而要定义规则集合的方式有两种,第一种是直接在 rules 传入规则的阵列,这会在初始化时自动转换成交集规则,第二种是使用 Karman 提供的 API defineUnionRulesdefineIntersectionRules

import { defineAPI, defineUnionRules, defineIntersectionRules } from "@vic0627/karman"const ruleSetTest = defineAPI({    validation: true,    payloadDef: {        param01: {            rules: ["int", { min: 1 }]        },        // param01 与 param02 这两种方法等效        param02: {            rules: defineIntersectionRules("int", { min: 1 })        },        param03: {            rules: defineUnionRules("string", "number")        }    },    onError(err) {        console.error(err)        return 1    }})ruleSetTest({ param01: -1 })// ValidationError: IntersectionRules// [1] Parameter 'param01' should be greater than or equal to '1', but received '-1'ruleSetTest({ param03: false })// ValidationError: UnionRules// [0] Parameter 'param03' should be 'string' type, but received 'false'.// [1] Parameter 'param03' should be 'number' type, but received 'false'.

以上就是参数验证引擎的介绍了,至于下一章节要介绍什么我还需要再思考一下,另外,在撰写这些文章的同时,我也会一边验证套件的实际行为是否符合文章所写内容(应该写测试的),所以更新进度可能会稍微缓慢一些。


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章