大家好! 让我们谈谈数据验证。 用打字稿编写的项目中有什么复杂的,为什么根本不需要? Typescript可以很好地控制所有内容,它仍然需要检查最大的用户输入。 也就是说,将十几位常规人员投入该项目中,似乎所有内容都可以关闭该主题,但是......与以往相去甚远,对于网络而言,几乎从来没有,整个项目都在单个代码库中并且使用相同的类型。 在这样的代码库的交界处,会出现以下情况:等待与现实不符,而打字稿不再是助手。 一些例子:
- 应用程序的客户端部分从API接收数据并进行验证。 首先,API可能突然且有时不作通知更改;其次,“服务器人员”本身有时不知道其API可以做什么,例如,在某些领域,而不是有保证的数组,即使是空的,满月也可能被赋予
null
。 当在客户端上描述数据时,程序员似乎可以确定客户端知道如何使用,如果出现问题,那么在控制台中立即看到一条有关问题根源的消息会比从视图层中捡起已经无法理解的错误更令人愉悦。 (如果能立即注意到它,那就很好了)。 现在也已经有解决方案( 1、2 ),允许将类型从服务器传输到客户端。 我尚未尝试这样做,但是很有可能这就是未来。 - 相反的情况是服务器检查发送的参数,如果参数不符合预期,则立即停止处理请求。 我认为不需要任何细节说明为什么这样做很重要。
- 在将数据存储到数据库之前进行数据验证也不是多余的。 例如,您可以看到它在我的一辆自行车中的组织方式: MaraquiaORM#Validation 。
我认为这些示例非常有说服力,现在没有感觉可以使用简单的常规进行,因为这不仅与用户输入有关,而且还与复杂的验证有关,该验证通常嵌套在多个数据级别上。 这里已经需要一个特殊的库。 当然有! 碰巧的是,在过去的10年中,每次启动一个新项目时,我都会尝试在其中使用另一个类似的库,并根据自己的需要进行调整。 每次出现问题时,有时会导致在主动开发过程中更换测试对象。 我不会谈论我研究过的所有选项,而只会谈论在当前项目中测试过的那些选项。
类型检查
Github
小而相当方便的图书馆。 该电路被描述为一个字符串。 使用多行字符串,您可以描述相当复杂的结构:
`{ ID: String, creator: { fname: String | Null, mname: String | Null, lname: String | Null, email: [String] } | Undefined, sender: Maybe { name: String, email: String }, type: Number, subject: String, ... }`
有相当严重的缺点:
- IDE不能以任何方式帮助一组模式,这在切换到打字稿时特别麻烦。
- 几乎没有用的错误消息。 我使用该库已有一年多了,也许有些变化(根据代码判断,否)。 消息采用“期望的字符串,收到的值为空”的样式。 现在想象一下,您有一个由200个对象组成的数组,每个对象都有带字符串的字段,而在一个对象中,只有一个损坏。 如何找到这个领域? 查看所有200个条目? 我遭受了很多次折磨,这真的伤
了我的生命,毁了图书馆的印象。 通常,我不想知道在那里期望和收到的东西,但是我想打开数据模式,找到其中需要的字段,并在数据本身中找到相同的字段。 换句话说,在错误消息中,关键路径必须位于数据/架构中的正确位置,并且可以忽略那里的所有期望。 - 当然,这很麻烦,但是上面的示例中的缩进在压缩代码时不会消失。
i
Github
浏览器版本: joi-browser
可能是该主题上最著名的库,具有许多功能和无尽的API。 最初,我在服务器上使用了它,并完美展示了自己。 在某个时候,我决定将其替换为客户端的type-check
。 那时,我几乎没有控制捆的大小,这根本没有问题。 但是在过去的一年中,它发展迅速,并且在移动互联网上,第一次下载应用程序并不令人满意。 决定组织惰性加载组件。 webpack-bundle-analyzer报告显示了捆绑中的一堆巨人,他们都可以轻松地找到由webpack创建的块。 除Joi
之外的每个人。 许多组件与服务器通信,并且所有服务器响应都经过验证,也就是说,将Joi
放入某种块中是没有意义的,它只会始终在主块之后立即加载。 在某些时候,主包看起来像这样: tyts 。 当然,产生了对它做些什么的持久愿望。 我想要相同的便捷库,但是要少得多。
是的
Github
在自述文件中,他们承诺提供相同的Joi
,但其大小适合前端。 实际上,它仅小两倍左右,也就是说, Yup
仍然是主捆绑包中最大的库。 此外,还出现了其他缺点:
- 默认库跳过所有
undefined
。 不断地编写.required()
并不是.required()
令人愉快的事情,当最初一切皆有可能且允许的情况下,我更喜欢它。 Joi
有一个选项presence: 'required'
Joi
presence: 'required'
以配置此行为。 我创建了一个带有地狱号码666的请求 ,但到目前为止,作者都保持沉默。 - 不允许使用所有键来检查对象的值。
Joi
为此使用object.pattern ,第一个参数接受任何字符串。 可能仍然有可能摆脱它,作者可以校正第一个负号,但是鉴于大小,我不想自己等待或编辑某些内容。
嗷
Github
最终,下一个申请人很小,而且他没有让他不断写()
,没有它,您可以做些什么。 例如,您可以编写一个允许字符串或undefined
的验证器,如下所示:
let optionalStringValidator = ow.optional.string; ow(optionalStringValidator, '1');
太好了! 那null
呢? 将所有文档翻过来,我发现以下方法:
ow.any(ow.optional.string, ow.null);
太恐怖了! 当我尝试重写项目中的部分验证时,在键入此代码时我几乎断了手指。 我ow.nullable
添加ow.nullable
问题 ,该问题已发送到此处 。 简而言之,他们说根本不需要null
。 在自述文件的第一行中,给出的参数也足够了:
人类的函数参数验证
也就是说,该库用于验证作为函数参数的值。 显然,它们并没有真正依靠来自服务器的巨大嵌套结构。
进一步的研究和使用尝试揭示了更多功能,这些功能再次在自述文件的同一行中得到了很好的解释,但并不十分适合我。 这实际上是一个很好的库,仅用于其他目的。
在这里,我已经完全厌倦了放弃,并决定用二十一点和处女来写我的图书馆。 是的,是的,我会和您再下一辆自行车:)。 见面:
my
一些例子:
import om from 'omyumyum'; const isOptionalNumber = om.number.or.undefined; isOptionalNumber('1');
.or
可以通过增加可行的选项来多次使用:
om.number.or.string.or.null.or.undefined;
在这种情况下,会不断生成一个几乎普通的函数,该函数接受任何参数并返回boolean
。
如果您希望函数在测试失败的情况下引发错误:
om(om.number, '1');
或带有讽刺意味:
const isNumberOrThrow = om(om.number); isNumberOrThrow('1')
结果函数不是很普通,因为它具有其他方法。 .or
已经显示,部分方法将取决于所选的类型(请参阅API ),例如,可以使用正则表达式增强字符串:
const isNonEmptyString = om.string.pattern(/\S/);
对于对象,您可以指定其形状:
const isUserData = om.object.shape({ name: om.string, age: om.number.or.vacuum
到问题点的承诺密钥路径:
om(om.array.of(om.object.shape({ name: om.string })), [{ name: '' }, { name: null }]);
如果内置功能不够用,则可以始终使用.custom(validator: (value: any) => boolean)
:
const isEmailOrPhone = om.custom(require('is-email')).or.custom(require('is-phone')); isEmailOrPhone('test@test.test');
库存也是期望的, .and
用于组合和改进类型:
const isNonZeroString = om.string.and.custom(str => str.length > 0);
.and
优先于.or
,但由于.custom()
接受与库创建的形式完全相同的形式的验证器,因此可以避免这种情况:
您可以继续改进以前创建的验证器。 旧的一点也不会变质。 让我们尝试改善isUserData
创建的isUserData
:
const isImprovedUserData = isUserData.and.object.shape({ friends: om.array.of(isUserData).or.vacuum }); isImprovedUserData({ name: '', age: 20, friends: [{ name: '', age: 18 }] });
好吧,留下来。 .not
:
const isNotVacuum = om.not.null.and.not.undefined;
可以在库API中找到其他可用方法。
图书馆的优势:
- 具有
.or
, .and
.or
且带有最小括号的.or
语法。 结合自动完成打字稿,这套工具变得很有趣。 - 与
Ow
相比,重量甚至Ow
(几乎减少了10倍(最小化+ gzip)),与Joi
相比Joi
图书馆就像是一座山上的羽毛。 - 好名字:)
库的缺点:
- 类型及其修饰符较少。 而且不太可能会有更多。 本文开头给出的所有三种使用场景(有关结点和代码库的信息)均假定传输纯文本数据,在大多数情况下为JSON。 也就是说,我认为,这样的库应该支持JSON中可能的类型,以及
undefined
的类型和一些常用类型。 由于某种原因,同一个Ow
挤满了对所有类型的类型化数组和其他废话的支持。 我认为这是多余的。 - 无法像
Joi
那样转换数据。 我认为Joi
在这方面也很糟糕。 至少我没有足够的功能,如果有必要,我会使用完全不同的工具进行转换。 也许这是omyumyum
的进一步发展方向。
仅此而已! 如果您喜欢这篇文章,请订阅该频道,祝您好运))。