Express中PUT请求/publish-template端点出现404 Not Found错误排查
点击提交按钮调用/publish-template端点出现404 Not Found错误排查
问题描述
点击表单提交按钮时,调用/publish-template端点返回404 Not Found,但该端点已在代码中定义,Pug视图里的表单action路径也正确:
doctype html html head meta(charset='utf-8') title Translations link(rel="stylesheet", href="/css/style.css") link(rel="stylesheet", href="/css/translations.css") link(rel="stylesheet", href="https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400") body form(id="msform" role="form" action="/publish-template" method="PUT") fieldset h2.fs-title Translations h3.fs-subtitle Manage translations with Firebase Remote Config. div.grid-container div.grid-item label(for="uploads") Upload file (JSON) input(type="file", name="publish", id="uploads", accept=".json" multiple) input.submit(type="submit", name="submit", placeholder="Submit")
端点配置代码(使用multer中间件)如下:
import admin from "firebase-admin"; import express, { urlencoded } from "express"; import bodyParser from "body-parser"; import path from "path"; import { request as _request } from "https"; import * as fs from "fs"; import multer from "multer"; import serviceAccount from "./placeholders/service-account.json" assert { type: "json" }; import { getDirname } from "./utils.js"; const __dirname = getDirname(import.meta.url); const upload = multer({ dest: "uploads/" }); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), }); const app = express(); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`); }); app.set("views", path.join(__dirname, "views")); app.set("view engine", "pug"); app.use(bodyParser.json()); app.use(urlencoded({ extended: false })); app.use(express.static(path.join(__dirname, "public"))); app.use(function (req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS"); res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With"); // allow preflight if (req.method === "OPTIONS") { res.send(200); } else { next(); } }); app.get("/", (req, res) => res.render("index")); app.get("/translations", (req, res) => res.render("translations")); // ... function validateTemplate(template) { return admin .remoteConfig() .validateTemplate(template) .then(function (validatedTemplate) { return true; }) .catch(function (err) { console.error("Template is invalid and cannot be published", err); return false; }); } app.put( "/publish-template", upload.single("publish"), async (req, res, next) => { const config = admin.remoteConfig(); let template; try { if (!req.file) { throw new Error("No file uploaded"); } const fileContent = fs.readFileSync(req.file.path, "UTF8"); template = config.createTemplateFromJSON(fileContent); const isValid = await validateTemplate(template); if (!isValid) { return nex(new Error("Template is invalid")); // 拼写错误:nex → next } } catch (err) { return next(new Error(err)); } config .publishTemplate(template) .then((updatedTemplate) => { console.log("Template has been published"); console.log("ETag from server: " + updatedTemplate.etag); res.status(200).send("Template has been published"); }) .catch((err) => { console.error("Failed to publish template: ", err); next(err); }); }, );
可能的原因及解决方案
1. HTML表单原生不支持PUT方法
虽然表单标签中声明了method="PUT",但浏览器实际不会发送PUT请求,而是默认发送POST或GET请求,导致服务器找不到对应请求方法的/publish-template路由,返回404。
解决方案:
- 将表单
method改为POST,添加隐藏字段_method指定实际请求方法:
form(id="msform" role="form" action="/publish-template" method="POST") input(type="hidden" name="_method" value="PUT") // 其余表单内容保持不变
- 安装并引入
method-override中间件,让Express识别该字段:
import methodOverride from "method-override"; // 在bodyParser中间件之后添加 app.use(methodOverride('_method'));
2. 代码拼写错误导致的异常
在/publish-template处理函数中,return nex(new Error("Template is invalid")); 存在拼写错误,nex应为next。该错误会在模板验证失败时抛出未捕获异常,虽不直接导致404,但会破坏请求流程,需优先修复。
3. Multer上传目录缺失
Multer不会自动创建配置的uploads/目录,若目录不存在,文件上传时会报错,可能导致请求无法正常匹配路由。
解决方案:
手动创建uploads/目录,或在代码中添加目录创建逻辑:
const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); }
内容的提问来源于stack exchange,提问作者JRR
相关产品推荐
相关产品推荐

