FORMA

Express

Express 是运行在 Node.js 之上的极简 Web 框架,提供路由、中间件与 HTTP 工具。见 后端入门概览EJS

一、Express 基础概念

  • 什么是 Express:Node.js 中最流行的 Web 开发框架,基于 Node.js 原生 HTTP 模块构建,提供了路由、中间件、模板引擎等核心能力,被用于构建 RESTful API、Web 网站、单页应用(SPA)后端等开发场景。
  • 核心特点
    • 极简而灵活,开发者可以根据需求组合中间件和工具
    • 基于中间件架构,处理请求的生命周期被拆分为多个可插拔的函数
    • 强大的路由系统,支持参数、正则表达式、模块化路由
    • 内置错误处理,便于进行异步和同步错误的统一管理
    • 与各种模板引擎(如 EJS、Pug)无缝集成
  • Express vs Node.js 原生 HTTP:Express 在 Node.js 的 http 模块之上做了封装,提供了更便捷的 API,降低了直接处理请求、路由匹配和响应处理的复杂度。

二、安装与环境

1. 安装 Express

bash
# 创建项目目录并初始化
mkdir my-app && cd my-app
npm init -y

# 安装 Express
npm install express

2. 最小可用示例

javascript
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

3. 开启开发模式(自动重启)

安装 nodemon

bash
npm install -D nodemon

package.json 中添加脚本:

json
{
  "scripts": {
    "dev": "nodemon app.js"
  }
}

运行 npm run dev 即可在代码变更后自动重启服务器。

三、请求对象(Request Object)

请求对象(req)包含了客户端发送的所有 HTTP 请求信息,是应用处理请求的重要依据。

属性/方法说明示例
req.params获取路由路径中的动态参数req.params.id(路由 /:id
req.query获取 URL 查询字符串参数req.query.page?page=2
req.body获取 POST/PUT 请求体中的参数req.body.username(需配合中间件)
req.headers获取客户端发送的 HTTP 头信息req.headers['content-type']
req.method获取 HTTP 请求方法GET, POST, PUT, DELETE
req.url获取请求的 URL 路径/api/users

各属性的详细用法:

javascript
// 路由参数 params: GET /user/123 -> { id: '123' }
app.get("/user/:id", (req, res) => {
  console.log(req.params.id);
});

// 查询参数 query: GET /search?q=express&page=1 -> { q: 'express', page: '1' }
app.get("/search", (req, res) => {
  console.log(req.query.q);
});

// 请求体 body: 需要 express.json() 或 express.urlencoded() 中间件
app.post("/login", (req, res) => {
  console.log(req.body.username);
});

// 请求头 headers
app.get("/info", (req, res) => {
  console.log(req.headers["user-agent"]);
});

注意:若要使用 req.body,必须先应用解析请求体的中间件,如 express.json()express.urlencoded()

四、响应对象(Response Object)

响应对象(res)用于向客户端返回数据。以下是最常用的响应方法:

方法说明示例
res.send()发送各种类型的响应(字符串、对象、Buffer 等)res.send('Hello')
res.json()发送 JSON 格式响应,自动设置 Content-Type: application/jsonres.json({ name: 'John' })
res.status()设置 HTTP 状态码,支持链式调用res.status(201).send('Created')
res.redirect()重定向到指定 URLres.redirect('/login')
res.render()渲染模板引擎(需配置)res.render('index', { title: '主页' })
res.sendFile()发送文件(用于文件下载)res.sendFile(path.join(__dirname, 'file.pdf'))
res.sendStatus()设置状态码并发送其对应的文本描述res.sendStatus(404)→'Not Found'

各方法的详细用法:

javascript
// 发送纯文本
app.get("/", (req, res) => {
  res.send("Hello World!");
});

// 发送 JSON
app.get("/api/user", (req, res) => {
  res.json({ id: 1, name: "Alice" });
});

// 链式调用:先设置状态码,再发送响应
app.post("/api/user", (req, res) => {
  res.status(201).json({ message: "用户创建成功" });
});

// 重定向
app.get("/old", (req, res) => {
  res.redirect("/new");
});

五、路由(Routing)

路由是 Express 的核心功能之一,它将 HTTP 方法、URL 路径与处理函数关联起来。

1. 基本路由

javascript
// GET 请求
app.get("/", (req, res) => {
  res.send("收到 GET 请求");
});

// POST 请求
app.post("/user", (req, res) => {
  res.send("收到 POST 请求");
});

// PUT 请求
app.put("/user/:id", (req, res) => {
  res.send(`更新用户 ${req.params.id}`);
});

// DELETE 请求
app.delete("/user/:id", (req, res) => {
  res.send(`删除用户 ${req.params.id}`);
});

// 处理所有 HTTP 方法(常用于全局中间件)
app.all("/secret", (req, res, next) => {
  console.log("访问了秘密路径");
  next();
});

2. 路由参数

javascript
// 单个参数
app.get("/users/:userId", (req, res) => {
  res.send(`用户 ID: ${req.params.userId}`);
});

// 多个参数
app.get("/posts/:postId/comments/:commentId", (req, res) => {
  res.send(`帖子 ${req.params.postId},评论 ${req.params.commentId}`);
});

3. express.Router 模块化路由(推荐用于项目结构)

随着应用规模增长,将所有路由写在同一个文件中会导致代码难以维护。express.Router 允许将相关的路由分组并导出,实现代码的模块化。

javascript
// routes/users.js 文件
const express = require("express");
const router = express.Router();

// 该路由下的中间件(仅对此模块生效)
router.use((req, res, next) => {
  console.log(`[Users] ${req.method} ${req.path}`);
  next();
});

router.get("/", (req, res) => {
  res.json({ users: ["Alice", "Bob"] });
});

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

router.post("/", (req, res) => {
  res.status(201).json({ message: "用户创建成功" });
});

module.exports = router;
javascript
// app.js 主文件
const express = require("express");
const userRoutes = require("./routes/users");

const app = express();
app.use(express.json());

// 挂载路由:所有以 /api/users 开头的请求都会进入 userRoutes
app.use("/api/users", userRoutes);

app.listen(3000);

六、中间件(Middleware)

中间件是 Express 的核心理念。一个中间件函数可以访问 reqresnext,它可以在请求到达最终处理函数之前/之后执行各类逻辑,如日志记录、身份验证、请求解析等。

1. 中间件的执行流程

javascript
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:添加自定义属性
app.use((req, res, next) => {
  req.requestTime = Date.now();
  next();
});

// 路由处理函数
app.get("/", (req, res) => {
  res.send(`请求时间: ${req.requestTime}`);
});

app.listen(3000);

2. 中间件的常见类型

  • 应用级中间件:通过 app.use()app.METHOD() 绑定到 app 对象
  • 路由级中间件:绑定到 express.Router() 实例,语法与应用级中间件相同
  • 内置中间件:Express 自带的中间件,如 express.json()express.urlencoded()express.static()
  • 第三方中间件:通过 npm 安装的中间件,如 corshelmetmorgan

各类型中间件的详细使用方式:

javascript
// 应用级中间件(全局生效)
app.use((req, res, next) => {
  console.log("所有请求都会经过这里");
  next();
});

// 路由级中间件(仅对该路径生效)
app.use("/api/protected", (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: "未授权" });
  }
  next();
});

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

// 内置中间件:解析 URL-encoded 表单数据
app.use(express.urlencoded({ extended: true }));

// 内置中间件:托管静态文件
app.use(express.static("public"));

// 第三方中间件示例:CORS
const cors = require("cors");
app.use(cors());

// 第三方中间件示例:安全头
const helmet = require("helmet");
app.use(helmet());

// 第三方中间件示例:Morgan 日志
const morgan = require("morgan");
app.use(morgan("dev"));

3. 中间件的执行顺序

中间件的加载顺序决定了执行的先后顺序。先加载的中间件先执行:

javascript
// ❌ 错误顺序:路由在前,中间件在后
app.get("/", (req, res) => res.send("Hello"));
app.use(logger); // logger 永远不会执行

// ✅ 正确顺序:先加载全局中间件,后加载路由
app.use(logger);
app.get("/", (req, res) => res.send("Hello"));
app.use(notFoundHandler); // 404 处理应放在最后
app.use(errorHandler); // 错误处理应放在最后

重要next() 是中间件链的“通行证”。如果不调用 next() 且没有结束响应(如用 res.send()),请求将被挂起,直到超时。

七、错误处理

Express 内置了一个默认的错误处理程序,同时支持自定义错误处理中间件。错误处理中间件与普通中间件的唯一区别是四个参数(err, req, res, next)

1. 同步错误

Express 会自动捕获同步代码中抛出的错误:

javascript
app.get("/", (req, res) => {
  throw new Error("出错了!"); // Express 会自动捕获
});

2. 异步错误

异步错误必须通过 next() 传递给 Express。在 Express 5 中,返回 Promise 的异步函数若拒绝,会自动调用 next

javascript
// Express 4 及以下:需要手动传递
app.get("/file", (req, res, next) => {
  fs.readFile("/some-file", (err, data) => {
    if (err) {
      next(err); // 将错误传递给 Express
    } else {
      res.send(data);
    }
  });
});

// Express 5:自动处理 Promise/promise reject
app.get("/user/:id", async (req, res, next) => {
  const user = await getUserById(req.params.id);
  res.send(user); // 如果 getUserById 抛出异常,Express 会自动调用 next
});

3. 自定义错误处理中间件

必须定义在所有路由之后,并声明四个参数:

javascript
// 普通路由
app.get("/", (req, res) => {
  res.send("Hello");
});

// 404 处理(没有匹配到任何路由时)
app.use((req, res, next) => {
  res.status(404).json({ error: "请求的资源不存在" });
});

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

八、常用中间件速查表

中间件用途安装命令
express.json()解析 JSON 格式请求体内置,无需安装
express.urlencoded()解析表单数据(URL-encoded)内置,无需安装
express.static()托管静态文件内置,无需安装
cors处理跨域资源共享npm install cors
helmet设置安全 HTTP 头,防御常见 Web 漏洞npm install helmet
morganHTTP 请求日志记录npm install morgan
compressionGzip 压缩响应内容npm install compression
express-rate-limit限制重复请求频率(防暴力 / DDoS)npm install express-rate-limit
cookie-parser解析 Cookienpm install cookie-parser

参考文献

以下链接在编写时均可正常访问:

资料说明
Express 指南路由
Express 5 迁移版本说明
Node.js http底层模块

Series

end

33 / 69