猫鼬/NextJS - 模型未定义/编译后无法覆盖模型



TL;DR EDIT:如果你来自谷歌,这是解决方案:

module.exports = mongoose.models.User || mongoose.model("User", UserSchema);

对于非TL;DR 答案,检查接受的答案。

~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 我正在开发一个NextJS网站,在后端我正在使用Mongoose和Express。每当我使用注册功能时,我都会在后端收到此错误:

{"name":"Test","hostname":"DESKTOP-K75DG72","pid":7072,"level":40,"route":"/api/v1/signup","method":"POST","errorMessage":"UserModel is not defined","msg":"","time":"2020-06-17T23:51:34.566Z","v":0}

我怀疑此错误是因为我在其他控制器中使用了UserModel。直到我制作了一个新的控制器,这个错误才发生。所以我的问题是,如何解决此问题/如何在不同的控制器/中间件中使用相同的模型?

我认为问题与节点有关.js - 编译后无法覆盖模型 猫鼬这篇文章,我之前收到此错误,但不知何故设法修复了它。

编辑:错误在于模型/用户.js,在预保存中间件中,UserModel 未在级别定义,如何验证该用户名的用户是否已存在,如果是,请拒绝新文档?

控制器/注册登录.js [错误发生的地方]

const UserModel = require("../models/User");
// More packages...
async function signUp(req, res) {
try {
const value = await signUpSchema.validateAsync(req.body);
const response = await axios({
method: "POST",
url: "https://hcaptcha.com/siteverify",
data: qs.stringify({
response: value.token,
secret: process.env.HCAPTCHA,
}),
headers: {
"content-type": "application/x-www-form-urlencoded;charset=utf-8",
},
});
if (!response.data.success) {
throw new Error(errorHandler.errors.HCAPTCHA_EXPIRED);
}
const hashPassword = await new Promise((res, rej) => {
bcrypt.hash(
value.password,
parseInt(process.env.SALTNUMBER, 10),
function (err, hash) {
if (err) rej(err);
res(hash);
}
);
});
await UserModel.create({
userName: value.username,
userPassword: hashPassword,
userBanned: false,
userType: "regular",
registeredIP: req.ip || "N/A",
lastLoginIP: req.ip || "N/A",
});
return res.status(200).json({
success: true,
details:
"Your user has been created successfully! Redirecting in 6 seconds",
});
} catch (err) {
const { message } = err;
if (errorHandler.isUnknownError(message)) {
logger.warn({
route: "/api/v1/signup",
method: "POST",
errorMessage: message,
});
}
return res.status(200).json({
success: false,
details: errorHandler.parseError(message),
});
}
}
module.exports = { signUp };

控制器/配置文件中.js[如果我在这里使用UserModel,它会破坏一切]

const UserModel = require("../models/User");
//plus other packages...
async function changePassword(req, res) {
try {
const value = await passwordChangeSchema.validateAsync(req.body);
const username = await new Promise((res, rej) => {
jwt.verify(value.token, process.env.PRIVATE_JWT, function (err, decoded) {
if (err) rej(err);
res(decoded.username);
});
});
const userLookup = await UserModel.find({ userName: username });
if (userLookup == null || userLookup.length == 0) {
throw new Error(errorHandler.errors.BAD_TOKEN_PROFILE);
}
const userLookupHash = userLookup[0].userPassword;
try {
// We wrap this inside a try/catch because the rej() doesnt reach block-level
await new Promise((res, rej) => {
bcrypt.compare(value.currentPassword, userLookupHash, function (
err,
result
) {
if (err) {
rej(errorHandler.errors.BAD_CURRENT_PASSWORD);
}
if (result == true) {
res();
} else {
rej(errorHandler.errors.BAD_CURRENT_PASSWORD);
}
});
});
} catch (err) {
throw new Error(err);
}
const hashPassword = await new Promise((res, rej) => {
bcrypt.hash(
value.newPassword,
parseInt(process.env.SALTNUMBER, 10),
function (err, hash) {
if (err) rej(err);
res(hash);
}
);
});
await UserModel.findOneAndUpdate(
{ userName: username },
{ userPassword: hashPassword }
);
return res.status(200).json({
success: true,
details: "Your password has been updated successfully",
});
} catch (err) {
const { message } = err;
if (errorHandler.isUnknownError(message)) {
logger.warn({
route: "/api/v1/changepassword",
method: "POST",
errorMessage: message,
});
}
return res.status(200).json({
success: false,
details: errorHandler.parseError(message),
});
}
}

模型/用户.js

const mongoose = require("mongoose");
const errorHandler = require("../helpers/errorHandler");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
userName: String,
userPassword: String,
userBanned: Boolean,
userType: String,
registeredDate: { type: Date, default: Date.now },
registeredIP: String,
lastLoginDate: { type: Date, default: Date.now },
lastLoginIP: String,
});
UserSchema.pre("save", async function () {
const userExists = await UserModel.find({
userName: this.get("userName"),
})
.lean()
.exec();
if (userExists.length > 0) {
throw new Error(errorHandler.errors.REGISTER_USERNAME_EXISTS);
}
});
module.exports = mongoose.model("User", UserSchema);

我已经设法修复了它。这里有两个问题。

1( "用户模型"变量在预中间件中不存在。通过实例化这个.constructor来解决,它显然解决了这个问题(需要进一步测试(

2(NextJS构建所有内容显然存在问题,每当我使用UserModel中的任何函数时,它似乎都在尝试创建一个新模型。这是固定的,导出已创建的模型

const mongoose = require("mongoose");
const errorHandler = require("../helpers/errorHandler");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
userName: String,
userPassword: String,
userBanned: Boolean,
userType: String,
registeredDate: { type: Date, default: Date.now },
registeredIP: String,
lastLoginDate: { type: Date, default: Date.now },
lastLoginIP: String,
});
UserSchema.pre("save", async function () {
try {
const User = this.constructor;
const userExists = await User.find({
userName: this.get("userName"),
})
.lean()
.exec();
if (userExists.length > 0) {
throw new Error(errorHandler.errors.REGISTER_USERNAME_EXISTS);
}
} catch (err) {
throw new Error(errorHandler.errors.REGISTER_USERNAME_EXISTS);
}
});
module.exports = mongoose.models.User || mongoose.model("User", UserSchema);

对我来说,它只是添加了Stan Loona答案的最后一行:

module.exports = mongoose.models.User || mongoose.model("User", UserSchema);

最简单的答案 (2023(

正在发生的事情是 Next.js 喜欢继续重建您的代码,导致 Mongoose 中的缓存出现一些问题 (mongoose.models(。

基本上,您要做的不是每次重建时都创建一个新模型,而是检查模型是否已加载到 Mongoose 缓存中,如果是,只需使用它而不是重新制作它。

所以在你的代码中,你会替换

mongoose.model('Profile', profileSchema);

mongoose.models.Profile ?? mongoose.model('Profile', profileSchema);

在更简单的代码中,这基本上是这样的:

let Profile;
if(mongoose.models.Profile) Profile = mongoose.models.Profile
else Profile = mongoose.model('Profile', profileSchema);

最新更新