为何Express的app.put()接口无法更新MongoDB中的文档数据?
问题原因&修复方案
核心问题点
- 原生HTML表单不支持PUT方法,你设置
method="PUT"浏览器会默认当成GET请求发送,根本不会触发你写的app.put路由 - 表单提交路径
action="/"和PUT路由配置的路径/${table}不一致,就算PUT可用也找不到对应接口 - MongoDB更新的查询条件
{w}完全无效:w是服务启动时生成的文档查询Promise,不是有效的文档匹配条件,MongoDB无法定位到要更新的目标文档 - 随机文档查询逻辑写在服务启动回调中,只会在服务启动时执行一次,所有用户所有请求永远展示同一个文档,不会重新随机
- PUT路由执行完更新后没有给客户端返回响应,会导致浏览器请求一直挂起
- 更新操作中
likes: "0"是字符串类型,如果你的字段设计为数字类型会出现类型不匹配问题
修复步骤
1. 安装配置method-override支持表单PUT请求
原生表单只支持GET/POST,用method-override中间件可以实现PUT/DELETE等方法的支持:
执行安装命令:
npm install method-override
在index.js中添加配置:
const methodOverride = require('method-override'); // 放在body-parser配置之后即可 app.use(methodOverride('_method'));
2. 修改home.ejs表单配置
修改PUT提交表单,补全正确的提交路径、隐藏字段传递目标文档ID:
<div> <form action="/<%= table %>?_method=PUT" method="POST"> <!-- 传递当前展示文档的_id,用于服务端定位更新 --> <input type="hidden" name="targetDocId" value="<%= numbers._id %>"> <button id="upvote" type="submit">upvote</button> </form> </div>
3. 修正首页GET路由逻辑
把随机文档查询逻辑移到路由内部,每次请求都重新生成随机文档,同时把table变量传递给EJS模板:
app.get('/', async (req, res) => { // 每次请求重新计算随机偏移量 const docCount = await collection.countDocuments(); const R = Math.floor(Math.random() * docCount) + 1; const w = collection.find().limit(-1).skip(R-1).next(); w.then(numbers => { // 传递table变量给模板,用于拼接表单提交路径 res.render("home.ejs", { numbers: numbers, table: table }); }) .catch(error => console.error(error)); })
4. 修正PUT路由逻辑
用前端传递的文档ID作为查询条件,修正更新字段类型,补充返回响应:
const { ObjectId } = require('mongodb'); // 顶部引入ObjectId用于转换_id格式 app.put(`/${table}`, (req, res) => { collection.findOneAndUpdate( // 用前端传递的_id匹配目标文档 { _id: new ObjectId(req.body.targetDocId) }, // 注意如果likes是数字类型,不要加引号写成字符串"0" { $set: { likes: 0 } } ) .then(result => { console.log("updated successfully"); // 更新完成后重定向回首页,刷新展示内容 res.redirect('/'); }) .catch(error => console.error(error)) });
内容的提问来源于stack exchange,提问作者Sebastian Przybylski
相关产品推荐
相关产品推荐

