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

Express.js/Node.js/EJS同名功能脚本的两种实现差异解析

关于Express+EJS实现姓名字母统计的两种方案对比疑问

任务背景

我需要完成以下课程练习任务:

  • 创建包含h1标题和姓名输入表单的index.ejs文件
  • 在index.js中接收表单POST请求的姓名参数
  • 统计全名字母的总数
  • 根据统计结果修改h1的显示内容

我用Express.js、Node.js和EJS完成了功能,代码可正常运行,还额外添加了请求日志中间件,但我的实现和老师的版本存在差异,想搞清楚老师处理app.get()和app.post()请求响应的原因,同时确认我的实现是否更便捷直观。


我的实现代码

index.ejs

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Name Letters</title>
</head>

<body>
  <% if (!countLetters) { %>
    <h1>Enter your name here!</h1>
  <% } else { %>
    <h1>There are <%= countLetters %> letters in your name.</h1>
  <% } %>
  
  <form action="/submit" method="POST" autocomplete="off">
    <input type="text" name="fName" placeholder="First name">
    <input type="text" name="lName" placeholder="Last name">
    <input type="submit" value="OK">
  </form>
</body>

</html>

index.js

import express from "express";

const app = express();
const port = 3000;

app.use(express.urlencoded({ extended: true }));
app.use(logger);

let countLetters = 0;

app.get("/", (req, res) => {
  res.render("index.ejs", {countLetters: countLetters});
});

app.post("/submit", (req, res) => {
  const firstName = req.body.fName;
  const lastName = req.body.lName;
  countLetters = firstName.length + lastName.length;
  res.redirect("/");
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

function logger(req, res, next) {
  console.log("Request: " + req.method + " " + req.url);
  next();
}

老师的实现代码

index.ejs

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Name Letters</title>
</head>

<body>
  <% if (locals.letterNumber) { %>
    <h1>There are <%= letterNumber %> letters in your name.</h1>
    <% } else { %>
      <h1>Enter your name below 👇</h1>
      <% } %>
        <form action="/submit" method="POST">
          <input type="text" name="fName" placeholder="First name">
          <input type="text" name="lName" placeholder="Last name">
          <input type="submit" value="OK">
        </form>
</body>

</html>

index.js

import express from "express";
import bodyParser from "body-parser";

const app = express();
const port = 3000;

app.use(bodyParser.urlencoded({ extended: true }));

app.get("/", (req, res) => {
  res.render("index.ejs");
});

app.post("/submit", (req, res) => {
  const numLetters = req.body["fName"].length + req.body["lName"].length;
  res.render("index.ejs", { numberOfLetters: numLetters });
});

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

两种实现的思路解析

老师的实现逻辑

  1. app.get('/')处理:直接渲染index.ejs,不传递任何数据。EJS模板通过locals.letterNumber判断该变量是否存在,以此切换h1的显示内容。这种方式初始状态下无需额外数据传递,代码逻辑更简洁。
  2. app.post('/submit')处理:计算完字母总数后,直接渲染index.ejs并传递统计结果。属于直接渲染响应,少了一次重定向的步骤,流程更短。

我的实现逻辑

  1. 用全局变量countLetters存储统计结果,每次app.get('/')都将该变量传递给EJS,通过变量值是否为0来切换h1内容。
  2. POST请求计算完成后,通过res.redirect('/')重定向到GET请求,这是**PRG模式(Post/Redirect/Get)**的典型应用,核心目的是避免用户刷新页面时重复提交POST请求。

优劣对比与建议

  • 老师的实现:代码更简洁,没有全局变量的风险,但存在一个隐患:用户刷新结果页面时,浏览器会重新发送POST请求,在涉及数据库写入等场景下,会导致重复操作(这个案例中影响不大,但生产环境需注意)。
  • 我的实现:采用PRG模式避免了表单重复提交问题,还添加了日志中间件方便调试,但全局变量countLetters是严重的问题——Express是多用户并发的服务,全局变量会被所有请求共享,A用户的统计结果会被B用户看到,完全不适合多用户场景。

如果想保留PRG模式的优势,同时解决全局变量的问题,可以改用URL参数传递结果:

// 修改POST请求的处理逻辑
app.post("/submit", (req, res) => {
  const firstName = req.body.fName;
  const lastName = req.body.lName;
  const countLetters = firstName.length + lastName.length;
  res.redirect(`/?count=${countLetters}`);
});

// 修改GET请求的处理逻辑
app.get("/", (req, res) => {
  const countLetters = req.query.count || 0;
  res.render("index.ejs", {countLetters: countLetters});
});

内容的提问来源于stack exchange,提问作者fmerighi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 21:25:11