在这篇摘自 hapi.js in Action 的 文章中,我将讨论 Joi ,这是一个用于数据验证的 Node.js 模块,它可以验证从简单的标量数据类型(如字符串、数字或布尔值)到复杂值的任何类型的数据由多层嵌套对象和数组组成。
人类经常犯错,因此我们创建的系统需要为滥用做好准备。验证几乎是我们日常生活中使用的每个系统的重要组成部分。让我们用一个零食自动售货机来说明这样一个系统。
自动售货机有多个输入需要验证。如果任何输入不符合其预期,机器将停止正常运行,并向用户提供一些反馈,告诉用户哪里出了问题。例如,如果您将一枚外国硬币放入投币口,机器将拒收该硬币并将其吐出到退币盘中。
图 1 - 自动售货机中发生的日常验证示例
我们依靠从验证中获得的反馈来确保我们能够以正确的方式使用系统。没有它,我们将不知道我们做错了什么。
我们使用术语 验证 也适用于软件。在本文中,我将讨论 Joi,这是一个用于数据验证的 Node.js 模块。
介绍 Joi
Joi 是一个用于数据验证的 Node 模块。 Joi 可以验证任何类型的数据,从简单的标量数据类型(如字符串、数字或布尔值)到由多层嵌套对象和数组组成的复杂值。
如果您在应用程序中处理来自未知来源的某些数据,例如通过公共 API,Joi 可以帮助您确保数据采用所需的格式。检查这些类型的输入并在它们不正确时采取相应措施将有助于使您的应用程序更加稳定和可靠。
Joi 可以在任何 Node 应用程序中用作独立模块。 Joi 实际上在 hapiecosystem 的许多模块中都在内部使用,正是为了这个目的。如果您之前错误地使用过任何 hapi 的 API 并看到错误消息,则该消息可能来自 Joi 执行的验证。
hapi 的设计考虑到了 Joi。该框架的内置验证功能(例如 HTTP 标头和有效负载的验证)旨在与 Joi 无缝协作。
怎么运行的
使用 Joi 涉及四个步骤的过程。这些步骤如图 2 所示。第一步是创建模式。模式是一个描述您的期望的对象,也是您检查真实数据所依据的对象。您可以将模式视为对理想对象的描述。
有一种非常常见的婴儿益智游戏,他们需要将不同形状的积木放入相应形状的孔中。这个游戏很好地类比了 Joi 的工作方式。使用 Joi,架构就像一个洞,而您要测试的对象就像一个块。任何给定的对象要么适合模式,要么不适合。
图 2 - 使用 Joi 验证时采取的步骤
有了模式后,您将使用 Joi 针对某些对象测试模式。如果出现验证错误,Joi 会告诉您到底出了什么问题。然后,您可以使用该反馈做出决定并适当地响应用户。
一个简单的例子:验证标量类型
我们在应用程序中通常需要验证的一项数据是密码。在这个简单的示例中,我将检查 updatePassword 函数中密码参数的格式。如果格式与要求的格式不匹配,将抛出错误。密码必须是一个字符串,长度在 6 到 10 个字符之间。
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
要针对真实值测试架构,您可以使用
Joi.assert(value, schema)
。使用此功能时,Joi 会在遇到第一次验证失败时抛出错误。记录的错误消息将包含一些有关验证失败位置的有用信息。
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
如果您在终端中运行这个小示例,您将看到类似于以下内容的一些输出:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
前面的示例展示了如何验证一个简单的标量值,但 Joi 的功能要多得多。您希望验证的值通常是更复杂的复合值,例如对象和数组。
一个更复杂的例子:验证复合类型
假设我正在创建一个 API,用于从世界各地的自动气象测量站收集数据。然后,这些数据会被保存下来,API 的消费者可以检索这些数据,以获取他们所在地区的最新数据。
气象站发送的每份天气报告都必须遵循标准格式。报告由多个字段组成,可以表示为 JavaScript 对象。来自台北的天气预报,在 7 月的一天中午,可能如下所示:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
将数据发送到中央 API 的远程气象站不在我的控制范围内。因此,谨慎的做法是验证所有传入数据以确保其符合标准格式。接受来自故障站点的无效数据可能会给 API 的使用者带来未知问题。
图 3 - 验证传入请求有助于确保数据的完整性
了解您的数据
第一步是考虑我想对报告中的每个字段施加的限制。我在下表中概述了我对天气报告的要求。
字段名称 | 数据类型 | 必需的 | 其他限制 |
车站 | 细绳 | 是的 | 最多 100 个字符 |
约会时间 | 日期 | 是的 | |
温度 (f) | 数字 | 是的 | 在 -140 到 140 之间 |
湿度 | 数字 | 是的 | 介于 0 和 100 之间 |
沉淀 | 布尔值 | 不 | |
风向 | 细绳 | 不 | N、NE、E、SE、S、SW、W、NW 之一 |
创建架构
在上一小节中,您看到我们可以使用
Joi.string()
在模式中指定 JavaScript 字符串。一个对象的等价物是
Joi.object()
也就不足为奇了。就像
Joi.string()
一样,
Joi.Object()
也有可以链接到它的方法,以进一步扩展模式。其中一种方法是
keys()
,这使您可以为每个对象的属性添加子模式。下面是使用键为对象创建模式的示例:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
使用键为对象创建模式是如此普遍,以至于 Joi 还为上述内容提供了一个方便的简写。您可以将
Joi.object().keys({...})
替换为
{...}
:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
下面的列表是用于验证天气报告的完整架构。
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
我们现在可以使用这个模式来验证一些真实的天气报告。我们可以运行以下脚本来根据此架构检查之前的示例报告:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
在脚本完成之前不应有任何输出。这表明测试的值是有效的。现在让我们尝试修改原始报告,使其根据模式不再有效。
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
如果使用修改后的报告再次运行脚本,您应该会看到抛出的错误。错误消息将包含一些有用的输出,以准确查明导致验证失败的原因:
var Joi = require('joi');
var schema = Joi.string().min(6).max(10);//#A
//#A A string between 6 and 10 characters in length
您现在应该对使用 Joi 的基础知识有一个很好的了解,并且您应该认识到在您的应用程序中使用验证技术的有用性。有关使用 hapi 和 Joi 的更多信息,请查看我的书 hapi.js in Action ,来自 Manning Publications。