尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Sails 模型校验:掌握 `.validate()` 同步校验方法与实战细节

Sails 模型校验:掌握 `.validate()` 同步校验方法与实战细节 Sails 模型校验掌握.validate()同步校验方法与实战细节【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails导读在 Sails基于 Node.js 的实时 MVC 框架应用中模型属性的校验规则通常在.create()或.update()时自动执行。.validate()是 Waterline ORM 暴露给模型的一个便捷方法它允许你在不触碰数据库的前提下针对单个属性预先验证某个值是否符合模型定义的校验规则并返回宽松归一化loosely coerced后的结果。本文将以 docs/reference/waterline/models/validate.md 为骨架结合仓库中的校验规则文档与错误处理文档完整讲解.validate()的用法、错误协商、适用场景与边界限制帮助你用它写出更 DRYDont Repeat Yourself的代码。一、方法签名与参数说明.validate()是一个模型方法model method调用方式如下Something.validate(attrName, value);其中Something是某个已定义的数据模型例如User、BankAccount它接受两个参数#参数可接受的数据类型是否必填说明1attrName((string))是要针对其进行校验的属性名attribute name2value((ref))是待校验/归一化的值需要特别注意的是这里的attrName必须是模型attributes中真实存在的属性名例如emailAddress、password、balance。value则可以是任意 JavaScript 值因为它会被当作该属性的新值来走一遍完整的校验与类型归一化流程。方法签名中的Something.validate(attrName, value)与仓库中其他模型方法如.create()、.update()处于同一文档体系均属于 docs/reference/waterline/models/ 目录下的方法参考。二、核心语义它是.update()的一次预演dry run.validate()的本质是把传入的数据当作将要传给.update()的valuesToSet中的某个值进行相同的校验与可能发生的归一化。你可以把它理解为不真正写库不真正执行查询只运行内存中的 JavaScript 校验逻辑。因此文档明确提示.validate()不会与数据库通信它只能发现逻辑层的失败——比如类型安全错误type safety errors和高层校验规则违规而无法检测物理层的约束例如唯一性unique冲突因为唯一性约束由底层数据库负责检查而不是由 Sails 或 Waterline 检查。这一边界在 docs/concepts/ORM/Validations.md 中也有呼应除了unique是数据库级约束外其余所有校验规则均在 Node.js 服务器进程中以 JavaScript 实现并运行。这意味着.validate()能复用的正是这批纯 JavaScript 逻辑层的校验规则。三、基础示例校验并归一化单个属性假设User模型上定义了emailAddress与password两个属性我们可以在控制器中直接校验来自请求参数的值User.validate(emailAddress, req.param(email)); User.validate(password, req.param(password));流程如下读取req.param(email)/req.param(password)拿到的原始值针对User模型中emailAddress/password属性定义的类型例如type: string与校验规则例如isEmail、minLength进行校验如果校验通过返回值是宽松归一化后的结果——例如字符串类型的属性会把传入值规整为符合该类型的 JavaScript 值。注意如果归一化不可行即校验失败.validate()会抛出一个同步异常。文档特别强调在异步回调内部你必须手动处理任何被抛出的错误否则可能导致未捕获异常uncaught exception。3.1 关于宽松归一化的底层依据宽松校验 归一化这一行为与 Waterline 的整体设计一致。在 docs/concepts/ORM/Validations.md 中明确写道Waterline 及其适配器会对 criteria 字典以及传给.create()/.update()的值执行宽松校验loose validation以确保其符合预期的数据类型。.validate()复用的正是这套逻辑层机制。仓库根目录的 package.json 显示当前项目为 Sails 1.5.18其依赖中包含sails-hook-ormdevDependencies 中的sails-hook-orm: ^4.0.2ORM 的实际实现由 Waterline 生态提供Sails 侧的模型方法文档统一收纳在 docs/reference/waterline/models/ 目录下。四、错误协商像.update()一样处理使用错误由于.validate()与.update()共享同一套校验逻辑因此它可能抛出你在调用.update()时见到的任何使用错误usage errors。典型场景如下try { var normalizedBalance BankAccount.validate(balance, $349.86); } catch (err) { switch (err.code) { case E_VALIDATION: // [Error: Invalid bankAccount] _.each(err.all, function(woe){ sails.log(woe.attrName : woe.message); }); break; default: throw err; } }关键信息解读err.code E_VALIDATION表示校验规则被违反在 Sails 中通常对应name: UsageError这一类err.all包含所有违规明细的数组每一项包含attrName违规的属性名与message人类可读的错误描述遍历err.all即可向用户逐条呈现哪个字段、为什么失败。4.1 错误分类体系在 docs/concepts/ORM/errors.md 中Sails/Waterline 将错误实例归一化为一致的属性属性类型说明name((string))错误的宽泛分类例如UsageErrormessage((string))错误描述信息stack((string))堆栈信息code((string?))有时存在的更细分类例如E_UNIQUE其中使用错误usage errors即name: UsageError表示某个 Waterline 方法被错误使用或以无效选项执行——例如试图创建一条违反模型高层校验规则的新记录。.validate()抛出的正是这类错误code: E_VALIDATION。而E_UNIQUE属于AdapterError大类只能来自.create()、.update()、.addToCollection()、.replaceCollection()永远不会由.validate()产生——这与本文第二部分.validate()无法检测唯一性的结论完全一致。提示在异步代码中更推荐使用.intercept()与.tolerate()这类查询装饰器来协商错误但由于.validate()是同步方法直接使用try...catch即可。五、同步特性无需await与回调.validate()是同步方法这意味着不需要await不需要 promise 链式调用不需要传统 Node 回调.exec()。你可以把它当作一个普通的、立即返回结果的函数来用。这也解释了为什么它的返回值和错误都以直接返回 / 直接抛出的方式呈现而不是像.create()、.update()那样返回可等待的 deferred 对象。六、与.create()/.update()的关系.validate()只是为方便而单独暴露的方法。你完全可以只调用.create()或.update()而不必先调用.validate()因为这两个模型方法会自动执行完全相同的检查。那么为什么要单独提供它文档给出了明确理由在以下场景中复用模型校验能让代码更 DRY、更易读调用第三方 API 之前校验不可信数据例如在把用户数据发给 Mailgun、Stripe 等第三方服务之前先用模型的校验规则把关避免把脏数据发出去分阶段校验以简化推理在业务逻辑中先跑一遍特定校验让后续代码的前提假设更清晰、更容易推理在无需落库的中间流程中复用规则比如表单的多步校验前几步只想验证、不想写库。6.1 需要手动校验的场景需要注意的是并不是所有场景都适合用模型校验。在 docs/concepts/ORM/Validations.md 的When to use validations一节中明确提醒模型校验会在每一次.create()/.update()时运行如果某个校验只应在特定分支生效例如两个邮箱二选一必填取决于用户通过邮箱还是 LinkedIn 注册就不应把required: true写在模型属性上而应在控制器内联校验或在 services / 模型类方法中自行检查不要害怕为了可维护性而放弃内置校验改为在控制器或 helper 中手工检查。这恰好凸显了.validate()的价值当你想临时复用某条模型校验规则、但又不希望它永久生效于所有写入时它就是最干净的工具。七、校验规则的完整视图要真正用好.validate()需要理解它背后实际运行的规则集。下面按 docs/concepts/ORM/Validations.md 整理规则全表.validate()的E_VALIDATION错误正是这些规则被触发时产生的规则名检查内容用法示例兼容属性类型custom传入自定义函数作为第一个参数时返回truecustom: function(value){ … }任意isAfter解析为日期后晚于配置的Date实例isAfter: new Date(Sat Nov 05 1605 00:00:00 GMT-0000)((string)), ((number))isBefore解析为日期后早于配置的Date实例isBefore: new Date(Sat Nov 05 1605 00:00:00 GMT-0000)((string)), ((number))isBoolean值为true或falseisBoolean: true((json)), ((ref))isCreditCard值为信用卡号注意 PCI 合规问题isCreditCard: true((string))isEmail值看起来像邮箱地址isEmail: true((string))isHexColor值为十六进制颜色字符串isHexColor: true((string))isIn值在指定字符串数组中isIn: [paid, delinquent]((string))isInteger值为整数isInteger: true((number))isIP值为合法 IP 地址v4 或 v6isIP: true((string))isNotEmptyString值不是空字符串isNotEmptyString: true((json)), ((ref))isNotIn值不在配置数组中isNotIn: [profanity1, profanity2]((string))isNumber值为 JavaScript 数字isNumber: true((json)), ((ref))isString值为字符串typeof(value) stringisString: true((json)), ((ref))isURL值看起来像 URLisURL: true((string))isUUID值看起来像 UUIDv3、v4 或 v5isUUID: true((string))max数值小于等于配置值max: 10000((number))min数值大于等于配置值min: 0((number))maxLength字符串长度不超过配置值maxLength: 144((string))minLength字符串长度至少为配置值minLength: 8((string))regex字符串匹配配置的正则regex: /^[a-z0-9]$/i((string))要点补充若某规则兼容 ((string))、((number)) 或 ((boolean))则该规则同时也兼容 ((json)) 与 ((ref))除unique外所有规则都在内存中运行这正是.validate()可以完整复用它们的前提大部分规则不额外限制空字符串但isNotEmptyString、isBoolean、isNumber、max、min等属于例外string、number、boolean类型默认不接受null如需允许null需开启allowNull: true该标志仅对上述类型有效对json、ref、关联属性和主键无效required: true意味着.create()时必须提供值且创建/更新时不允许置为null或空字符串。7.1 自定义校验规则custom规则允许你定义任意复杂的校验逻辑。自定义函数接收待校验值作为第一个参数返回true表示合法false表示非法。这类规则同样会被.validate()复用// api/models/User.js module.exports { attributes: { location: { type: json, custom: function(value) { return _.isObject(value) _.isNumber(value.x) _.isNumber(value.y) value.x ! Infinity value.x ! -Infinity value.y ! Infinity value.y ! -Infinity; } }, password: { type: string, custom: function(value) { // 必须为字符串、至少 6 位、包含至少一个字母和一个数字 return _.isString(value) value.length 6 value.match(/[a-z]/i) value.match(/[0-9]/); } } } };7.2 内置数据类型类型安全的前提属性必须始终声明一种内置数据类型这是所有校验与归一化的大前提数据类型用法说明((string))type: string任意字符串((number))type: number任意数字((boolean))type: booleantrue或false((json))type: json任意可 JSON 序列化的值数字、布尔、字符串、数组、字典、null((ref))type: ref除undefined外的任意 JavaScript 值仅在需要利用适配器特定行为时使用例如一个可选邮箱属性可以这样定义使得.validate(workEmail, value)在值合法时返回归一化字符串workEmail: { type: string, isEmail: true, }这里workEmail可接受合法邮箱或空字符串但不能接受null违反type: string的类型安全限制。若希望接受null可改为type: json并视需要追加isString: true。八、注意事项与边界总结最后把.validate()的关键注意事项汇总如下均来自 docs/reference/waterline/models/validate.md 及仓库内关联文档同步执行不要使用await、promise 链或 Node 回调返回值直接得到失败直接抛出。便捷而非必需create()/update()内部自动执行同样检查.validate()只是让你先验一下。典型价值在 DRY在与第三方 API如 Mailgun、Stripe交互前校验不可信数据或让部分代码先完成校验以便于推理。只查逻辑层不查物理层类型安全与高层校验规则可被检测unique之类的数据库约束无法被检测这类问题只会由真正的写入操作抛出E_UNIQUE。错误形态与.update()一致以E_VALIDATIONUsageError形式抛出可通过err.all获取逐属性违规明细在异步回调中必须手动try...catch。与req.validate()无关仓库测试 test/hooks/request/initialize.test.js第 39 行附近验证的是请求 hook 暴露的req.validate()函数且断言调用它应当总是失败而本文讨论的.validate()是模型方法二者不可混淆。九、进一步阅读模型方法参考.update()——.validate()校验语义的对照对象模型方法参考.create()—— 同样自动执行校验的写入方法概念Validations 校验规则详解 —— 规则全表、类型系统、allowNull/required、自定义规则概念Errors 错误协商 ——UsageError/AdapterError/E_UNIQUE的分类体系概念模型与 ORM —— 模型定义与模型方法总览查询装饰器.intercept()与.tolerate()—— 异步场景下的错误协商利器【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表