Express.js提交按钮路由异常:PATCH请求无法正确跳转
待办事项项目PATCH请求问题排查
我正在做一个练习项目,用各类HTTP请求实现简单待办事项(to-do list)。除了PATCH请求,其他请求都正常:这个请求本该重定向到首页“/”,但实际跳转到“/posts/2”,出现“Cannot POST /posts/2”提示,而且数组里的元素也没修改。我想要它重定向到首页,展示包含已修改项的所有元素,但找不到错误。
后端代码(index.js)
import express from 'express'; import bodyParser from 'body-parser'; const app = express(); const port = 3000; let todoList = [ { id: 1, content: "Chase flogging pressgang aye overhaul dead men tell no tales mutiny lee Pieces of Eight draught.", }, { id: 2, content: "Barque trysail gunwalls yo-ho-ho scurvy gangway Blimey Davy Jones' Locker bounty weigh anchor.", }, ]; let done = []; var id = 2; var data = { todo: todoList, done: done, }; app.use(bodyParser.urlencoded({ extended : true })); app.use(express.static('public')); app.use(express.json()); app.get('/', (req, res) => { console.log('GET'); res.render('index.ejs', data); }); // 获取指定ID的待办项 app.get('/posts/:id', (req, res) => { if(todoList.length>0){ let pId = parseInt(req.params.id); const post = todoList.find(i => i.id === pId); if(post){ res.render('post.ejs', { post: post}); }else { res.send('<h2>Post not found: <a href="/">返回首页</a></h2>'); } } else { res.send('<h2>列表为空: <a href="/">返回首页</a><h2>'); } }); // 编辑页面路由 app.get('/edit/:id', (req, res) => { try { let pId = parseInt(req.params.id); const post = todoList.find(i => i.id === pId); if(!post) res.status(404).json({ message: "获取待办项失败。" }); res.render("edit.ejs", {todo: post}); } catch (error) { res.status(500).json({ message: "获取待办项失败。" }); } }); // 新增待办项 app.post('/posts', (req, res) => { if(req.body.post ){ const newId = id += 1; const newTodo = { id: newId, content: req.body.post }; todoList.push(newTodo); } res.redirect("/"); }); // 更新待办项(PATCH请求) app.patch('/posts/:id', (req, res) => { console.log("PATCH"); const pId = parseInt(req.params.id); console.log("pId: " + pId); const post = todoList.find((todo) => todo.id === pId); console.log("post.id: " + post.id); if(!post) res.status(404).json({message: '待办项不存在'}); const replacementPost = { id: pId, content: req.body.content || post.content, }; const postIndex = todoList.findIndex((todo) => todo.id === pId); if (postIndex > -1){ todoList[postIndex] = replacementPost; console.log("Updated: " + todoList[postIndex]); res.redirect("/"); } else { res.send("未找到待办项内容"); } });
前端模板代码
index.ejs
<%- include('partials/header.ejs') %> <% function excrept(text) { // 生成待办项短文本 let shortTxt = text; let maxLength = 35; if (shortTxt.length > maxLength) { shortTxt = text.slice(0, maxLength) + "..."; } return shortTxt; } %> <h1>待办事项列表</h1> <form action="/posts" method="POST"> <textarea id="text" name="post" placeholder="输入你的待办事项" rows="5" autofocus required> </textarea> <input type="submit" value="添加待办"> </form> <div class="todo"> <% if(locals.todo && todo.length > 0) {%> <h2 id="todoList"> 待完成</h2> <% todo.forEach(element => { %> <div class="checklist-item"> <input type="checkbox" id="<%= element.id %>" name="checkbox" class="checkbox" value="post-<%= element.id %>"> <label for="<%= element.id %>"><a href="/posts/<%= element.id %>"><%= excrept(element.content) %></a></label> <a class="edit" href="/edit/<%= element.id %>">编辑</a> <br> </div> <% }); %> <% } %> </div> <%- include('partials/footer.ejs') %>
edit.ejs
<%- include('partials/header.ejs') %> <h1>修改待办项</h1> <% if(locals.todo) { %> <form id="editTodo" action="/posts/<%= todo.id %>" method="post"> <textarea name="content" id="txtcontent" rows="5"><%= todo.content %></textarea> <button type="submit" class="modify">保存修改</button> </form> <% } %> <%- include('partials/footer.ejs') %>
问题分析与修复
核心问题
- 请求方法不匹配:edit.ejs中的表单用的是
method="post",但后端只定义了app.patch('/posts/:id')路由,没有对应POST路由,因此触发Cannot POST /posts/2错误。 - HTML表单限制:原生HTML的
<form>标签仅支持GET和POST两种方法,直接写method="patch"不会生效。
修复方案
方案1:改用POST路由处理更新(最简单)
把后端的app.patch修改为app.post,让表单的POST请求能匹配到路由:
// 将原来的patch改为post app.post('/posts/:id', (req, res) => { console.log("POST 更新"); const pId = parseInt(req.params.id); const post = todoList.find((todo) => todo.id === pId); if(!post) res.status(404).json({message: '待办项不存在'}); const replacementPost = { id: pId, content: req.body.content || post.content, }; const postIndex = todoList.findIndex((todo) => todo.id === pId); if (postIndex > -1){ todoList[postIndex] = replacementPost; res.redirect("/"); } else { res.send("未找到待办项内容"); } });
方案2:用隐藏字段模拟PATCH方法(REST风格)
如果想严格遵循REST规范保留PATCH路由,需要借助method-override中间件模拟PATCH请求:
- 安装依赖:
npm install method-override
- 在index.js中引入并配置中间件:
import methodOverride from 'method-override'; // 放在bodyParser之后配置 app.use(methodOverride('_method'));
- 修改edit.ejs的表单,添加隐藏字段:
<form id="editTodo" action="/posts/<%= todo.id %>" method="post"> <input type="hidden" name="_method" value="PATCH"> <textarea name="content" id="txtcontent" rows="5"><%= todo.content %></textarea> <button type="submit" class="modify">保存修改</button> </form>
额外优化点
index.ejs中存在表单嵌套错误:form标签在forEach外部打开,却在内部闭合,会导致HTML结构异常,建议移除多余的form标签(当前checkbox的提交逻辑未实现,暂时不需要包裹)。
内容的提问来源于stack exchange,提问作者Claudio Cassol
相关产品推荐
相关产品推荐

