MongoDB更新文章时sanitizedHtml无法同步更新的问题咨询
编辑文章时无法更新sanitizedHtml字段的解决方案
我正在尝试创建一个更新路由,但每当我尝试更新内容时,仅能更新title、description和markdown字段,无法更新sanitizedHtml字段。请问在编辑时如何更新sanitizedHtml字段?
我的article.js模型代码
const mongoose = require('mongoose'); const marked = require('marked'); const slugify = require('slugify'); const createDomPurify = require('dompurify'); const { JSDOM } = require('jsdom'); const dompurify = createDomPurify(new JSDOM().window); const articleSchema = new mongoose.Schema({ title: { type: String, required: true, }, description: { type: String, }, markdown: { type: String, required: true, }, createdAt: { type: Date, default: Date.now, }, slug: { type: String, required: true, unique: true, }, sanitizedHtml: { type: String, required: true, }, }); articleSchema.pre('validate', function (next) { if (this.title) { this.slug = slugify(this.title, { lower: true, strict: true }); } if (this.markdown) { this.sanitizedHtml = dompurify.sanitize(marked.parse(this.markdown)); } next(); }); module.exports = mongoose.model('Article', articleSchema);
我的PUT路由代码
router.put('/:id', async (req, res) => { const { id } = req.params; const article = Article.findById(id); const newArticle = { title: req.body.title, description: req.body.description, markdown: req.body.markdown, }; const updatedArticle = await Article.findByIdAndUpdate(id, newArticle); await updatedArticle.save(); res.redirect(`/articles/${article.slug}`); });
问题原因
findByIdAndUpdate默认不会触发mongoose的pre('validate')中间件,而sanitizedHtml是在这个中间件里生成的,所以更新时不会自动重新计算。- 路由存在两处错误:
const article = Article.findById(id);未加await,得到的是Promise对象而非实际文档,后续调用article.slug会出错。findByIdAndUpdate默认返回更新前的文档,调用updatedArticle.save()会把旧文档重新存储,逻辑错误。
解决方案
修改PUT路由,配置findByIdAndUpdate触发验证器和中间件,同时修复其他bug:
router.put('/:id', async (req, res) => { const { id } = req.params; const updatedArticle = await Article.findByIdAndUpdate( id, { title: req.body.title, description: req.body.description, markdown: req.body.markdown, }, { runValidators: true, // 触发验证规则及pre('validate')中间件 new: true // 返回更新后的文档 } ); res.redirect(`/articles/${updatedArticle.slug}`); });
原理说明
添加runValidators: true后,mongoose会在更新时执行模型验证逻辑,同时触发pre('validate')中间件,这样就能根据新的markdown重新生成sanitizedHtml,也能根据新的title重新生成slug。new: true确保我们拿到的是更新后的文档,避免跳转时使用旧的slug。
另一种等价写法(先查询文档再保存,同样触发中间件):
router.put('/:id', async (req, res) => { const { id } = req.params; const article = await Article.findById(id); article.title = req.body.title; article.description = req.body.description; article.markdown = req.body.markdown; await article.save(); // 保存时触发pre('validate')中间件 res.redirect(`/articles/${article.slug}`); });
两种方式均可解决问题,可根据习惯选择。
内容的提问来源于stack exchange,提问作者Rosan Paudel
相关产品推荐
相关产品推荐

