背景知识
在Java语言开发的许多地方遇到了必须验证值的情况之后,很明显,有必要以某种方式解决此问题。 为此,设置了以下任务:
开发一个可启用以下功能的库:
- 验证数据类型 ;
- 设置默认值,而不是无效的字段或元素;
- 删除对象或数组的无效部分 ;
- 收到错误讯息
其基础将是:
为了实现这些目标,已经开发了一个四方验证库。
基本验证模块
被设计为适用于各种任务的大多数系统的核心是最简单的元素 :动作,数据和算法。 以及它们的构成方法-为了从更简单的最简单的元素中组装一些东西,以解决更复杂的问题。
验证者
四方库基于验证器的概念。 该库中的验证器具有以下形式的功能
function validator( value: any, { key: string|int, parent: any }, { key: string|int, parent: any }, ... ): boolean
此定义中有几件事应该更详细地描述:
function(...): boolean
-表示验证器-计算验证结果,验证结果为布尔值-true或false ,分别有效或无效
value: any
any-表示验证器-计算验证值的结果,该值可以是任何javascript值。 验证器将验证后的值分配给有效或无效。
{ key: string|int, parent: any }, ...
表示验证值可以在不同的上下文中,具体取决于值嵌套的级别。 让我们用例子展示一下
没有上下文的示例值
const value = 4;
数组上下文中的示例值
对象上下文中的示例值
const obj = { a: 1, b: 2, c: value, d: 8 }
由于对象中的结构可以具有更大的嵌套,因此谈论各种上下文是有意义的
const arrOfObj = [{ a: 1, b: 2, c: value, d: 8 },
依此类推。
关于数组方法的相似性验证器的此定义应使您想起作为参数传递给数组方法的函数的定义,例如: map,filter,some,every等。
- 这些函数的第一个参数是数组元素。
- 第二个参数是元素的索引。
- 第三个参数是数组本身。
在这种情况下,验证器是一个更通用的函数-它不仅获取数组和数组中元素的索引,而且还获取其父级和父级中数组的索引,依此类推。
我们应该建什么房子?
上述砖头在javascript拐杖“海滩”上的其他“石头解决方案”中并不突出。 因此,让我们从它们中构建一些更连贯和有趣的东西。 为此,我们有一个构图 。
如何建立一个对象验证的摩天大楼?
同意,以使验证描述与对象描述匹配的方式来验证对象将很方便。 为此,我们将使用验证器的对象组成 。 看起来像这样:
如您所见,从为特定字段定义的不同验证器块中,我们可以组装一个对象验证器-一些仍然很小的“小建筑物”,但是比没有它更好。 为此,我们使用验证器v
的作曲者。 每次在验证程序的位置遇到对象文字v
时,他都会将其视为对象组成,并根据其字段将其转换为对象验证程序。
有时我们无法描述所有领域 。 例如,当一个对象是数据字典时:
const quartet = require('quartet') const v = quartet() const isStringValidator = name => typeof name === 'string' const keyValueValidator = (value, { key }) => value.length === 1 && key.length === 1 const dictionarySchema= { dictionaryName: isStringValidator, ...v.rest(keyValueValidator) } const compositeObjValidator = v(dictionarySchema) const obj = { dictionaryName: 'next letter', b: 'c', c: 'd' } const isObjValid = compositeObjValidator(obj) console.log(isObjValid)
如何重用施工解决方案?
如上所述,有必要重用简单的验证器。 在这些示例中,我们已经不得不使用“字符串类型验证器”两次。
为了缩短记录并提高其可读性,四方库使用验证程序的字符串同义词。 每当验证者的作曲者在验证者应有的位置遇到字符串时,它都会在字典中搜索其验证者并使用它 。
默认情况下,最常见的验证器已在库中定义。
请考虑以下示例:
v('number')(1)
以及文档中描述的许多其他问题。
每个拱门都有自己的砖头类型?
验证程序的编写者(函数v
)也是验证程序的工厂。 从某种意义上说,它包含许多返回的有用方法
例如,让我们看一下数组验证:通常,它包括检查数组的类型和检查其所有元素。 我们将v.arrayOf(elementValidator)
使用v.arrayOf(elementValidator)
方法。 例如,采用带有名称的点数组。
const a = [ {x: 1, y: 1, name: 'A'}, {x: 2, y: 1, name: 'B'}, {x: -1, y: 2, name: 'C'}, {x: 1, y: 3, name: 'D'}, ]
由于点数组是对象数组,因此使用对象组合来验证数组元素是有意义的。
const namedPointSchema = { x: 'number',
现在,使用工厂方法v.arrayOf
,为整个数组创建一个验证器。
const isArrayValid = v.arrayOf({ x: 'number', y: 'number', name: 'string' })
让我们看看这个验证器是如何工作的:
isArrayValid(0)
这只是工厂方法之一,每种方法在文档中都有描述。
如上所述, v.rest
也是一种工厂方法,该方法返回一个对象组合,该对象组合检查未在对象组合中指定的所有字段。 这意味着可以使用spread-operator
将其嵌入到另一个对象合成中。
让我们以其中的几个示例为例:
是还是不是?
有效数据经常采用各种形式,例如:
id
可以是数字,也可以是字符串。- 根据尺寸,
point
对象可能包含也可能不包含某些坐标。 - 还有许多其他情况。
为了组织变体的验证,提供了一种单独的组成类型-变体组成。 它由可能选项的验证器数组表示。 当至少一个验证者报告对象的有效性时,该对象被视为有效。
考虑一个带有标识符验证的例子:
const isValidId = v([ v.and('not-empty', 'string'),
点验证示例:
const isPointValid = v([ {
因此, 每当作曲家看到一个数组时,他都会认为它是此数组的验证器元素的组成,这样当其中一个认为该值有效时,验证的计算将停止并且该值将被视为有效。
如我们所见,作曲家不仅将验证器功能视为验证器,而且还将所有可能导致验证器功能的事物视为。
验证者类型 | 例子 | 作曲家认为 |
---|
验证功能 | x => typeof x === 'bigint' | 只是要求了必要的值 |
对象组成 | { a: 'number' } | 根据指定的字段验证器为对象创建验证器函数 |
变体成分 | ['number', 'string'] | 创建一个验证器函数以使用至少一个选项来验证值 |
工厂方法调用结果 | v.enum('male', 'female') | 大多数工厂方法都返回验证函数( v.rest ,该函数返回对象组成),因此它们被视为常规验证函数 |
所有这些验证器选项都是有效的,并且可以在验证器应在模式中的任何位置使用。
结果,工作方案始终是这样的: v(schema)
返回验证函数。 接下来,在特定值上调用此验证函数:
v(schema)(value[, ...parents])
您在施工现场有任何事故吗?
-还没有
-他们会的!
碰巧数据无效,我们需要能够确定无效原因。
为此,四方库提供了一种解释机制。 它包含以下事实:在验证器(无论是内部验证器还是外部验证器)检测到验证数据的有效性时,必须发送说明性注释 。
为此, v
了验证者v
的作曲者的第二个参数。 它增加了在数据无效的情况下向v.explanation
数组发送解释性注释的v.explanation
。
例如,让我们验证一个数组,并想找出所有无效元素的数量及其值:
如您所见,解释的选择取决于任务。 有时甚至没有必要。
有时我们需要对无效字段进行处理。 在这种情况下,可以使用无效字段的名称作为解释 :
const objSchema = { a: v('number', 'a'), b: v('number', 'b'), c: v('string', 'c') } const isObjValid = v(objSchema) let invalidObj = { a: 1, b: '1', c: 3 } isObjValid(invalidObj)
有了这种解释机制,您可以实现与验证结果相关的任何行为。
解释可以是任何东西:
- 包含必要信息的对象;
- 纠正错误的功能。 (
getExplanation => function(invalid): valid
); - 无效字段的名称或无效元素的索引;
- 错误代码
- 所有这些足以让您发挥想象力。
没有构建东西时该怎么办?
纠正验证错误并非难事。 为此,该库使用带有副作用的验证器,该副作用会记住错误的位置以及如何修复错误。
v.default(validator, value)
-返回一个记住无效值的验证程序,并在调用v.fix
设置默认值v.filter(validator)
-返回一个记住无效值的验证器,并在调用v.fix
-从父级中删除该值v.addFix(validator, fixFunc)
-返回一个记住无效值的验证程序,并且在调用v.fix
-调用带有参数(值,{key,parent},...)的fixFunc。 fixFunc
必须变异合作伙伴之一-更改值
const toPositive = (negativeValue, { key, parent }) => { parent[key] = -negativeValue } const objSchema = { a: v.default('number', 1), b: v.filter('string', ''), c: v.default('array', []), d: v.default('number', invalidValue => Number(invalidValue)),
杂务仍然派上用场
该库中还有一些验证方法的实用程序方法:
结果
通过以下方式解决了任务:
挑战赛 | 解决方案 |
---|
数据类型验证 | 默认命名验证器。 |
预设值 | v.default |
移除无效零件 | v.filter , v.omitInvalidItems 和v.omitInvalidProps 。 |
易学 | 简单的验证器,将它们组合为复杂的验证器的简单方法。 |
代码可读性 | 该库的目标之一是将验证方案本身比作 |
验证对象。 |
易于修改 | 掌握了合成的元素并使用了自己的验证功能-更改代码非常简单。 |
错误讯息 | 以错误消息的形式进行说明。 或根据说明计算错误代码。 |
后记
该解决方案旨在快速方便地创建验证器功能,并具有嵌入自定义验证功能的能力。 因此,欢迎阅读本文的人进行任何更正,批评和改进。 谢谢您的关注。