Node.js Express 框架

原生 http 模块写多了会很啰嗦——路由分发、参数解析、错误处理都要自己来。Express 是 Node 最经典、最流行的 Web 框架,把这些常用能力封装成了简洁的 API。它是几乎所有 Node Web 框架(Koa、Fastify、NestJS)的鼻祖,学它就是学整个 Node Web 生态的根基。

1. 创建 Express 应用

npm install express,然后几行代码就能起一个比原生 http 优雅得多的服务:

// 先安装:npm install express
const express = require("express");
const app = express();

// 路由:GET /
app.get("/", (req, res) => {
  res.send("首页");
});

// 路由:带路径参数
app.get("/users/:id", (req, res) => {
  // req.params 拿路径参数
  res.json({ id: req.params.id });
});

// 启动服务
app.listen(3000, () => {
  console.log("Express 服务跑在 http://localhost:3000");
});

对比原生 httpif/else 路由,Express 用 app.get/post/put/delete 直接把方法和路径绑定到处理函数,清晰得多。每个处理函数收到增强过的 reqres——多了 req.paramsreq.queryres.json()res.send() 等便捷能力。

2. 路由:URL 与方法的映射

Express 的核心就是路由。一个典型的 RESTful API 长这样:

const express = require("express");
const app = express();

// 不同 HTTP 方法的路由
app.get("/users", (req, res) => {        // GET 查列表
  res.json([{ id: 1 }, { id: 2 }]);
});

app.post("/users", (req, res) => {       // POST 新建
  res.status(201).json({ ok: true });
});

app.put("/users/:id", (req, res) => {    // PUT 全量更新
  res.json({ updated: req.params.id });
});

app.delete("/users/:id", (req, res) => { // DELETE 删除
  res.status(204).end();
});

// 查询字符串:?name=abc&page=2
app.get("/search", (req, res) => {
  // req.query 拿查询参数
  res.json(req.query);   // { name: "abc", page: "2" }
});

// 路由参数:/users/42 → id = "42"
app.get("/users/:id", (req, res) => {
  res.send("用户 ID:" + req.params.id);
});

区分两个最容易混的参数来源:

还有请求体 req.body——需要中间件解析(下面讲)。

3. 中间件:Express 的精髓

理解 Express 就是理解中间件(middleware)。中间件就是一个依次接收 reqresnext 三个参数的函数,每个请求依次穿过所有匹配的中间件,你可以在任意环节插入日志、鉴权、参数校验等逻辑:

// 中间件:一个 (req, res, next) => {} 函数
// app.use 注册的中间件,每个请求都会穿过

const express = require("express");
const app = express();

// 1. 应用级中间件:所有请求都过
app.use((req, res, next) => {
  console.log(req.method, req.url, new Date().toISOString());
  next();   // 必须调用 next,否则请求卡住
});

// 2. 解析 JSON 请求体(内置中间件)
app.use(express.json());

// 3. 解析 URL 编码的表单数据
app.use(express.urlencoded({ extended: true }));

// 4. 只对特定路径生效
app.use("/api", (req, res, next) => {
  console.log("API 请求");
  next();
});

// 5. 自定义业务中间件:鉴权
function auth(req, res, next) {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: "未登录" });
  }
  req.user = verifyToken(token);  // 把用户信息挂到 req 上
  next();
}

// 受保护的路由:中间件 + 处理函数
app.get("/profile", auth, (req, res) => {
  res.json(req.user);
});

几个关键认知:

4. 增强的请求和响应对象

Express 给 reqres 加了大量便捷方法,告别原生 res.writeHead 那套啰嗦写法:

// Express 给 res 加了很多便捷方法

// 返回 JSON
res.json({ ok: true, data: [] });

// 返回纯文本
res.send("hello");

// 返回 HTML
res.send("<h1>首页</h1>");

// 设置状态码
res.status(404).send("没找到");
res.status(201).json({ created: true });

// 重定向
res.redirect("/new-path");
res.redirect(301, "https://example.com");  // 永久重定向

// 设置响应头
res.set("Content-Type", "text/html");
res.set("X-Custom", "value");

// 设置 Cookie
res.cookie("token", "abc123", {
  httpOnly: true,
  maxAge: 86400000   // 1 天(毫秒)
});

// 链式调用
res
  .status(200)
  .set("X-Powered-By", "MyApp")
  .json({ ok: true });

这些方法让代码可读性大幅提升——res.json() 一行搞定 JSON 响应,不用手动 JSON.stringify 再设 Content-Type。

5. 路由模块化:express.Router

项目大了,所有路由堆在 app.js 里没法维护。express.Router 让你按模块拆分路由:

// express.Router:把路由按模块拆分
// 适合中大型项目,避免所有路由堆在一个文件

// ---- routes/users.js ----
const express = require("express");
const router = express.Router();

router.get("/", (req, res) => {
  res.json([{ id: 1 }, { id: 2 }]);
});

router.post("/", (req, res) => {
  res.status(201).json(req.body);
});

router.get("/:id", (req, res) => {
  res.json({ id: req.params.id });
});

module.exports = router;

// ---- app.js ----
const users = require("./routes/users");
app.use("/users", users);   // 所有 /users/* 走 users 路由
app.use("/orders", require("./routes/orders"));
app.use("/products", require("./routes/products"));

这种结构让不同业务模块各自管自己的路由文件,主文件只负责挂载。是 Node 后端项目的标准组织方式。

6. 错误处理

Express 有专门的"错误处理中间件"——它和普通中间件的区别是有 4 个参数(多了个 err):

// Express 的错误处理:专门的"错误中间件"

// 同步错误:throw 或 next(err) 会被错误中间件捕获
app.get("/broken", (req, res) => {
  throw new Error("故意出错");
});

// 异步错误:必须 next(err) 传给错误中间件
app.get("/async-broken", async (req, res, next) => {
  try {
    const data = await fetchData();
    res.json(data);
  } catch (err) {
    next(err);    // 关键:必须手动传给 next
  }
});

// Express 5 / async handler 自动捕获,不用 try/catch

// 错误处理中间件:4 个参数(必须有 next)
app.use((err, req, res, next) => {
  console.error("出错:", err.stack);
  res.status(err.status || 500).json({
    error: err.message || "服务器内部错误"
  });
});

// 必须放在所有路由之后注册

注意几个坑:

7. 静态文件与模板引擎

// 静态文件托管:一行搞定
const express = require("express");
const app = express();
const path = require("path");

// 把 public 目录下的文件对外提供
app.use(express.static("public"));
//   访问 /logo.png → 返回 public/logo.png
//   访问 /css/style.css → 返回 public/css/style.css

// 多个静态目录
app.use(express.static("public"));
app.use(express.static("uploads"));

// 加虚拟前缀:访问 /static/xxx
app.use("/static", express.static("public"));

// 用绝对路径(更稳)
app.use(express.static(path.join(__dirname, "public")));

// 模板引擎:以 EJS 为例
// npm install ejs
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

app.get("/", (req, res) => {
  // 渲染 views/index.ejs,注入数据
  res.render("index", { title: "首页", user: "小明" });
});

express.static 是托管前端静态资源(HTML/CSS/JS/图片)的标准做法,比手写 fs 读文件安全高效得多。模板引擎(EJS、Pug、Handlebars)适合做服务端渲染(SSR),现代项目更多用前后端分离 + Vue/React,模板引擎用得少了。

8. 实战:一个完整的 RESTful API

const express = require("express");
const app = express();

// 中间件
app.use(express.json());                    // 解析 JSON body
app.use((req, res, next) => {              // 日志
  console.log(req.method, req.url);
  next();
});

// 简单的内存"数据库"
let users = [
  { id: 1, name: "小明" },
  { id: 2, name: "小红" }
];
let nextId = 3;

// RESTful 路由
app.get("/users", (req, res) => {
  res.json(users);
});

app.get("/users/:id", (req, res) => {
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: "用户不存在" });
  res.json(user);
});

app.post("/users", (req, res) => {
  const user = { id: nextId++, name: req.body.name };
  users.push(user);
  res.status(201).json(user);
});

// 错误处理中间件(放最后)
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: "服务器错误" });
});

app.listen(3000, () => console.log("http://localhost:3000"));

这就是一个生产可用雏形——加上数据库、参数校验(joi/zod)、JWT 鉴权、单元测试,就是一个真实项目。学完这些,你已经能独立写后端 API 了。

9. Express 之外的框架选择

学 Express 是基础——它的中间件思想是所有这些框架的共同语言。先 Express 入门,再按需选其他。

小结

Express 是 Node Web 开发的入门必修。记住核心:路由(app.get/post) + 中间件(app.use,next 传递控制权) + 错误中间件(4 参数,放最后)。掌握它,你已经能写出真实可用的后端 API。下一篇讲调试——代码出 bug 怎么排查。

← 上一篇 Node.js 包管理

下一篇 Node.js 调试

✈️💬