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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:58:14