在 Node.js 中,Mongoose 是一个非常流行的 MongoDB 数据库对象建模工具。它提供了一种优雅的方式来定义和操作数据模式,使得我们可以更加轻松地进行查询、更新、删除等操作。不仅如此,Mongoose 还提供了强大的数据字段验证功能,以确保我们的数据在存储到数据库中之前是完整、可靠、有效的。在本文中,我们将深入学习 Mongoose 的数据字段验证机制,以及如何在实际项目中使用它。
1. 概述
在 Mongoose 中,数据字段验证由 Schema 和 Model 两个组件共同实现。Schema 是用来定义数据模型的结构和规则的,包括字段名称、类型、默认值、验证规则等;而 Model 则是用来创建数据的实例对象,同时会通过 Schema 来验证数据是否可用。我们可以通过 Schema 的 validate 方法或者钩子函数来自定义数据字段验证的规则,以保证数据的完整性和正确性。
2. 自定义验证规则
Mongoose 内置了很多常见的验证规则,例如必填字段、字符串长度、数字范围、日期格式等,可以直接在 Schema 中使用。但是,有时候我们需要定义一些更加复杂的验证规则,例如对数组里的每个元素进行验证、跟其他字段关联的验证等,这时候就需要自定义规则了。
2.1 使用 validate 方法
在 Mongoose 中,可以通过在字段定义中添加 validate 属性来自定义验证规则。validate 属性是一个数组,包含多个 Validator 函数。Validator 函数接收一个参数 value,表示待验证的字段值,返回一个布尔值或者抛出一个验证错误。
例如,我们要定义一个 user 模型,包含 name 字段和 age 字段,其中 name 为必填字段,且长度不能超过 20 个字符;age 为可选字段,但是如果提供了值,它必须大于等于 18 岁,才能保存到数据库中。此时,我们可以这样定义 Schema:
-- -------------------- ---- ------- ----- ---------- - --- ----------------- ----- - ----- ------- --------- ----- ---------- --- --------- - ---------- ------- -- --------- ----------------- -------- -------- ---- ------- - -- ---- - ----- ------- ---- ---- ---- -------- --------- - ------- -- ------ -- ----- -- --- ---- ------ - - ---
在上面的例子中,我们使用了 validate 方法来自定义了两个验证规则。对于 name 字段,我们使用了正则表达式来限制其只能包含字母和空格,如果不符合规则,则抛出 'Invalid name format' 错误。对于 age 字段,我们使用了较为简单的 validate 函数,对值进行大于等于 18 岁的判断,并抛出 'Too young' 错误。
使用 validate 方法有一个问题,就是当存在多个验证规则时,最后只会捕获一个错误,且顺序不固定。因此,更好的选择是使用 pre 和 post 钩子函数来自定义验证规则。
2.2 使用钩子函数
在 Mongoose 中,Schema 中的 save、validate 等方法执行前后,可以依次执行中间件函数,从而实现自定义验证逻辑。中间件函数有 pre 和 post 两种类型,前者是异步函数,可以在实例化或者保存数据之前进行验证或者数据加工;后者是同步函数,可以在成功保存或者失败添加等事件后进行数据处理。下面分别介绍两种钩子函数的用法。
2.2.1 使用 pre 钩子
pre 钩子是在前置钩子函数中执行的,可以在实例化或者保存数据之前进行验证或者数据加工操作。pre 钩子函数有以下六个参数:
- this:代表当前实例对象(this 指针);
- next:代表进入下一个 pre 中间件函数的函数,需要显式调用才能生效;如果抛出异常,则当前操作会被撤回;
- done:代表当前操作结束的回调函数,通常用于执行异步操作;
- error:代表 pre 中间件函数执行时抛出的错误,如果存在则不会执行当前及之后的中间件函数;
- result:代表上一个中间件函数的返回值(前提是该函数返回 Promise);
- params:代表传递给 pre 函数的参数数组,调用时是使用 apply/call 来传递的。
例如,我们要在保存 user 模型操作之前,对某些字段进行验证。可以通过 pre 中间件函数来实现:
-- -------------------- ---- ------- ---------------------- -------- ------ - -- --------- - -- -- --------- - ------ -------- ---------- -------- - -- ----------- --------------------- - ------ -------- -------------- ---- --------- - ------ --
在上述代码中,我们定义了一个 pre 中间件函数,监听了 save 事件。在函数中,我们首先对 age 字段进行了大于等于 18 岁的验证,如果不符合条件,则抛出一个错误。然后,我们对 name 字段进行了正则表达式验证,如果不符合条件,则同样抛出一个错误。最后,如果没有出现错误,则调用 next 函数进入下一个中间件函数,或者执行记录添加操作。
需要注意的是,pre 钩子函数只能自定义实例化或保存等方法的前置事件,在其他场景下可能不是我们想要的行为,例如查询、删除等。如果需要对这些操作进行自定义验证,我们可以使用 post 钩子。
2.2.2 使用 post 钩子
post 钩子是在后置钩子函数中执行的,可以在成功保存或者失败添加等事件后进行数据处理。与 pre 钩子不同,post 钩子函数是同步执行的,其参数列表也有所不同:
- doc:代表操作成功后的文档对象(即保存到数据库后的数据对象);
- result:代表操作成功的结果,其类型可以是任意值;
- next:如果有指定,则说明存在下一个中间件函数,可以调用 next() 函数进入下一个函数调用;如果没有指定,则不会执行后面的中间件函数。
- error:代表操作失败时的错误对象;
- session:如果开启了会话(session),则会传递会话对象;否则为 undefined。
例如,我们要在保存 user 模型操作成功后,清除一些敏感信息。可以通过 post 中间件函数来实现:
userSchema.post('save', function (doc, next) { delete doc.password next() })
在上述代码中,我们定义了一个 post 中间件函数,监听了 save 事件。在函数中,我们删除了文档对象的 password 属性,并调用了 next 函数,事实上此时可以省略 next 函数,因为 post 钩子的唯一作用就是在成功保存后进行操作。
需要注意的是,如果同时使用了 pre 和 post 钩子函数,则必须在执行前置操作时调用 next() 函数,否则后置操作将不会被执行。
3. 通过 assert 库进行验证
以上介绍了如何使用 Mongoose 自带的 validate 方法和中间件函数来自定义数据字段验证规则。这两种方式都是通过抛出错误的方式来让整个操作失败,然后通过 catch 语句捕捉错误进行处理。但是,有时候直接抛出错误可能不太方便,而且在某些情况下还需要对数据的某些属性进行详细的验证。这种情况下,我们可以使用 Node.js 内置的 Assert 库来进行验证。Assert 库提供了一些方法来判断变量是否符合某种条件,如果不符合,则会抛出异常,否则什么都不会发生。
例如,我们要对一个用户名进行验证,要求其长度在 4~8 个字符之间,且只能包含英文字母和数字。可以使用 assert 库来实现:
-- -------------------- ---- ------- ----- ------ - ------------------ -------- ---------------- ---------- - ------------------------------------------- -------- -------- ------ ----- -- --- ---------- ----- --- ---- ------- ------- --- --------- -- - --------------------------- -- -- ---------------------------- -- --------------
在上述代码中,我们使用 assert 库的正则表达式方法进行了用户名的验证。如果不符合规则,则抛出一个 AssertionError 异常,将影响到整个执行流程。如果需要自定义 Assertion 类型,则可以通过 define 方法来实现。
4. 总结
在本文中,我们学习了 Mongoose 的数据字段验证机制,介绍了如何在 Schema 中使用 validate 方法和钩子函数来自定义验证规则,同时还介绍了如何使用 Assert 库进行验证。这些方法可以提高我们在实际项目中对数据的安全性进行控制,保证其完整性、可靠性和有效性。需要注意的是,验证规则的复杂度越高,操作的性能就越低,因此在选择验证规则时需要权衡其中的利弊。如果您还有其他问题或者建议,欢迎在评论区留言,与我们分享您的看法。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6465e7f0968c7c53b0691893