You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}`);
});

问题原因

  1. findByIdAndUpdate默认不会触发mongoose的pre('validate')中间件,而sanitizedHtml是在这个中间件里生成的,所以更新时不会自动重新计算。
  2. 路由存在两处错误:
    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:10:26