Mongoose 是一款优秀的 Node.js ORM 框架,可以在 Node.js 应用程序中方便地操作 MongoDB 数据库。Mongoose 还提供了各种钩子函数,可以在执行特定操作时自动执行一些逻辑操作,如数据验证、中间件等。本文将介绍 Mongoose 的钩子函数及使用技巧,并包含实用示例代码。
什么是 Mongoose 钩子函数
Mongoose 钩子函数是一些回调函数,可以在特定的操作(如保存、更新、删除)期间自动执行。这些钩子函数可以用于实现数据验证、执行操作前后的逻辑、目标文档处理等。
Mongoose 钩子函数类型
Mongoose 提供了四种钩子函数类型:前置钩子、后置钩子、虚拟属性钩子和错误钩子。
前置钩子
前置钩子在执行操作前执行。它们可以修改数据、添加默认值、进行数据验证、执行异步操作等。前置钩子的函数签名应为 (next: Function) => void,其中 next 是一个可选的回调函数。如果前置钩子未调用 next 回调,则不会继续执行操作。
下面是一个前置钩子示例,它在保存文档前添加创建日期和更新日期:
// www.javascriptcn.com code example
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: String,
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: Date,
});
postSchema.pre('save', function (next) {
const now = new Date();
this.updatedAt = now;
if (!this.createdAt) {
this.createdAt = now;
}
next();
});后置钩子
后置钩子在执行操作后执行。它们可以以异步方式进行其他操作,如发送电子邮件、更新缓存等。后置钩子的函数签名应为 () => void,它们没有 next 回调。
下面是一个后置钩子示例,它在保存文档后发送一封电子邮件:
// www.javascriptcn.com code example
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: String,
});
postSchema.post('save', function (doc) {
const email = new Email();
email
.to('johndoe@example.com')
.subject('New post published')
.body(`Title: ${doc.title}\nContent: ${doc.content}`)
.send();
});虚拟属性钩子
虚拟属性钩子在访问虚拟属性时执行。它们不能修改文档,只能返回新的值。虚拟属性钩子的函数签名应为 () => any,它们没有 next 回调。
下面是一个虚拟属性钩子示例,它返回文章的 URL:
// www.javascriptcn.com code example
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: String,
});
postSchema.virtual('url').get(function () {
return `/posts/${this._id}`;
});错误钩子
错误钩子在执行操作时发生错误时执行。它们可以用于记录错误、发送错误报告等。错误钩子的函数签名应为 (error: Error, next: Function) => void,其中 error 是错误对象,next 是一个回调函数,如果未调用 next 回调,则不会继续执行操作。
下面是一个错误钩子示例,它记录保存文章时发生的错误:
// www.javascriptcn.com code example
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: String,
});
postSchema.post('save', function (error, doc, next) {
if (error) {
const logger = new Logger();
logger.error(`Failed to save post: ${error.message}`);
}
next();
});Mongoose 钩子函数使用技巧
钩子函数的执行顺序
Mongoose 钩子函数的执行顺序非常重要。例如,保存文档时,如果存在多个前置钩子和后置钩子,它们将以以下顺序执行:
- 执行所有顺序值为 1 的前置钩子。
- 执行其他顺序值的所有前置钩子,按顺序执行。
- 执行操作。
- 执行所有顺序值的后置钩子,按倒序执行。
在添加前置钩子和后置钩子时,可以通过设置 order 选项来指定执行顺序:
// www.javascriptcn.com code example
// 执行顺序为 1 的前置钩子
postSchema.pre('save', { order: 1 }, function (next) {
// ...
next();
});
// 执行顺序为 2 的前置钩子
postSchema.pre('save', { order: 2 }, function (next) {
// ...
next();
});
// 执行顺序为 1 的后置钩子
postSchema.post('save', { order: 1 }, function (doc) {
// ...
});处理异步操作
在钩子函数中执行异步操作时,需要注意 next 回调的使用。例如,在保存文档时,如果前置钩子需要执行异步操作,可以将 next 回调作为异步操作的回调函数:
postSchema.pre('save', function (next) {
const someAsyncFunction = () => {
// ...
next();
};
someAsyncFunction();
});如果前置钩子同时使用了异步和同步操作,需要在异步操作执行完成后调用 next 回调。例如,下面的前置钩子需要验证唯一性并保存文档:
// www.javascriptcn.com code example
postSchema.pre('save', async function (next) {
const isUnique = await Post.exists({ title: this.title });
if (isUnique) {
const error = new Error('Title must be unique');
next(error);
} else {
next();
}
});处理错误
当钩子函数执行出错时,可以在错误钩子中处理错误。例如,在保存文档时,如果前置钩子失败,则可以在错误钩子中记录错误并返回错误对象:
// www.javascriptcn.com code example
postSchema.pre('save', async function (next) {
const someAsyncFunction = () => {
throw new Error('Failed to execute some async function');
};
someAsyncFunction();
next();
});
postSchema.post('save', function (error, doc, next) {
if (error) {
const logger = new Logger();
logger.error(`Failed to save post: ${error.message}`);
return next(error);
}
next();
});注意,在错误钩子中必须调用 next 回调以终止错误处理过程。
结语
本文介绍了 Mongoose 钩子函数的四种类型、使用技巧以及示例代码。希望本文能够帮助您更好地了解和使用 Mongoose 钩子函数。如果您想进一步深入了解 Mongoose,请参考官方文档。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/67820a8e935627c900f4a9c9