Node.js Express 框架完全指南

小飞兽 Node.js 67 次阅读 2026-06-24

Express 框架简介

Express 是 Node.js 最流行的 Web 框架,以其简洁、灵活和强大的中间件机制著称。它本身非常轻量,核心功能(路由、中间件)只有约 2000 行代码,其余功能通过中间件扩展实现。

项目初始化与基础结构

mkdir my-express-app && cd my-express-app
npm init -y
npm install express
npm install --save-dev nodemon  # 开发时自动重启
// app.js - 最小 Express 应用
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

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

// 内置中间件:解析 URL-encoded 请求体(表单提交)
app.use(express.urlencoded({ extended: true }));

// 内置中间件:静态文件服务(public 目录)
app.use(express.static('public'));

// 自定义中间件:日志记录
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} - ${res.statusCode} [${duration}ms]`);
  });
  next();  // 必须调用 next() 传递控制权
});

// 路由
app.get('/', (req, res) => {
  res.send('<h1>Hello Express!</h1>');
});

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

路由详解

// routes/post.js
const express = require('express');
const router = express.Router();

// 获取文章列表
router.get('/', async (req, res) => {
  // query 参数: /posts?page=1&limit=10
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const skip = (page - 1) * limit;

  // 模拟数据库查询
  const posts = await getPosts({ skip, limit });
  const total = await getTotalPosts();

  res.json({
    data: posts,
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit)
    }
  });
});

// 获取单篇文章 - 路由参数
router.get('/:id', async (req, res) => {
  const { id } = req.params;  // URL 参数
  const post = await getPostById(id);

  if (!post) {
    // 返回 404 JSON
    return res.status(404).json({ error: '文章不存在' });
  }
  res.json(post);
});

// 创建文章 - POST 请求
router.post('/', async (req, res) => {
  const { title, content, authorId } = req.body;  // JSON 请求体

  // 数据验证
  if (!title || !content) {
    return res.status(400).json({ error: '标题和内容不能为空' });
  }

  const newPost = await createPost({ title, content, authorId });
  // 201 Created
  res.status(201).json(newPost);
});

// 更新文章 - PUT(完整更新)或 PATCH(部分更新)
router.put('/:id', async (req, res) => {
  const { id } = req.params;
  const updates = req.body;

  const updated = await updatePost(id, updates);
  if (!updated) {
    return res.status(404).json({ error: '文章不存在' });
  }
  res.json(updated);
});

// 删除文章
router.delete('/:id', async (req, res) => {
  const { id } = req.params;
  await deletePost(id);
  res.status(204).send();  // 204 No Content
});

module.exports = router;

中间件与错误处理

// middleware/auth.js
// JWT 认证中间件示例
const jwt = require('jsonwebtoken');

function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: '未提供认证令牌' });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;  // 将用户信息挂载到 req 上
    next();  // 验证通过,继续处理
  } catch (err) {
    return res.status(403).json({ error: '令牌无效或已过期' });
  }
}

// 错误处理中间件(4个参数,Express 识别为错误处理器)
function errorHandler(err, req, res, next) {
  console.error('服务器错误:', err);

  // Mongoose 验证错误
  if (err.name === 'ValidationError') {
    return res.status(400).json({
      error: '数据验证失败',
      details: Object.values(err.errors).map(e => e.message)
    });
  }

  // 其他错误
  res.status(err.status || 500).json({
    error: process.env.NODE_ENV === 'production'
      ? '服务器内部错误'
      : err.message
  });
}

module.exports = { authenticate, errorHandler };

完整项目结构示例

// server.js - 整合所有模块
const express = require('express');
const postRoutes = require('./routes/post');
const { authenticate, errorHandler } = require('./middleware/auth');

const app = express();

// 中间件
app.use(express.json());
app.use(express.static('public'));

// API 路由(/api/v1 前缀便于版本管理)
app.use('/api/v1/posts', postRoutes);

// 认证保护示例路由
app.get('/api/v1/profile', authenticate, (req, res) => {
  res.json({ user: req.user });
});

// 404 处理
app.use((req, res) => {
  res.status(404).json({ error: '接口不存在' });
});

// 错误处理器(必须放在最后)
app.use(errorHandler);

const PORT = process.env.PORT || 3000;
app.listen(Pelcome to, () => {
  console.log(`Express 服务启动: http://localhost:${PORT}`);
});

常见问题

Q1: Express 的中间件执行顺序是怎样的?

中间件按照 app.use() 的注册顺序依次执行。每个中间件通过 next() 将控制权传递给下一个中间件。如果某个中间件没有调用 next(),请求就会挂起,不再继续。需要特别注意的是:错误处理中间件必须有 4 个参数 (err, req, res, next),且放在所有路由之后。

Q2: 如何防止 SQL 注入和 XSS 攻击?

Express 本身不处理这些问题,需要引入安全中间件:

  • <code>express-mongo-sanitize</code> — 防止 NoSQL 注入

  • <code>helmet</code> — 设置安全的 HTTP 头(防止 XSS、点击劫持等)

  • <code>express-rate-limit</code> — 防止暴力破解和 DDoS

  • 对用户输入始终做验证(<code>joi</code>/<code>zod</code>)和转义

Q3: Express 和 Koa 有什么区别?

Express 是回调风格的异步编程,使用传统的 reqres 对象;Koa 使用 async/await + Context 对象(ctx),更轻量,没有内置路由(需单独安装 @koa/router),洋葱模型中间件执行方式更优雅。Express 更成熟、生态更丰富;Koa 更现代、代码更简洁。

延伸阅读

  • <a href="https://expressjs.com/zh-cn/">Express 官方文档</a>
  • <a href="https://expressjs.com/zh-cn/advanced/best-practice-security.html">Express 最佳实践</a>
  • <a href="https://restfulapi.cn/">RESTful API 设计规范</a>