4. Express 框架入门:Express 安装与路由、中间件机制、请求与响应处理

好,咱们进入正题。Express 是什么?说白了,它是 Node.js 生态里最流行的 Web 框架。我当年刚接触 Node 时,第一个学的就是它。为什么?因为它足够轻量,又足够强大。你想想看,一个框架能活十几年还这么火,肯定有它的道理。

4.1 安装 Express

安装 Express 非常简单。前提是你已经装好了 Node.js。我个人习惯用 npm 来管理依赖。

# 创建一个新项目
mkdir my-express-app
cd my-express-app

# 初始化 package.json
npm init -y

# 安装 Express
npm install express

嗯,这里要注意:npm init -y 会生成一个默认的 package.json。如果你想要更精细的控制,可以去掉 -y 手动填写。

装完之后,你的项目里会多出一个 node_modules 文件夹。别管它,那是依赖包。我们直接写代码。

4.2 第一个 Express 应用

创建一个 app.js 文件,写下这段代码:

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

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

app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

运行 node app.js,打开浏览器访问 http://localhost:3000。你会看到 "Hello World!"。就这么简单。

我在项目中遇到过不少新手,他们以为 Express 很复杂。其实核心就这几行代码。你想想看,一个 Web 服务器无非就是「接收请求 → 处理逻辑 → 返回响应」。Express 把这三步包装得特别优雅。

4.3 路由机制

路由,说白了就是「根据不同的 URL 和 HTTP 方法,执行不同的处理函数」。Express 的路由设计非常直观。

4.3.1 基本路由

// GET 请求
app.get('/users', (req, res) => {
  res.send('获取用户列表');
});

// POST 请求
app.post('/users', (req, res) => {
  res.send('创建新用户');
});

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

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

看到 :id 了吗?这是路由参数。Express 会自动解析 URL 中的动态部分,放到 req.params 里。我曾经踩过一个坑:路由参数的命名不要用连字符,比如 :user-id 在某些版本里会有问题。建议用下划线::user_id

4.3.2 路由分组

当路由多了以后,都堆在 app 上会很难维护。Express 提供了 Router 来分组:

const router = express.Router();

router.get('/', (req, res) => {
  res.send('首页');
});

router.get('/about', (req, res) => {
  res.send('关于我们');
});

// 挂载到 /site 路径下
app.use('/site', router);

这样,访问 /site//site/about 就会分别触发对应的处理函数。我个人习惯把每个模块的路由单独放在一个文件里,比如 routes/users.jsroutes/products.js。项目大了以后,这种组织方式会让你少掉很多头发。

4.4 中间件机制

中间件是 Express 最核心的概念。没有之一。你想想看,一个请求从进入服务器到返回响应,中间要经过多少步骤?日志记录、身份验证、参数解析、错误处理……这些步骤如果都写在路由处理函数里,代码会变得又臭又长。

中间件就是用来解决这个问题的。它是一个函数,可以访问请求对象、响应对象,以及下一个中间件函数。

4.4.1 中间件的结构

function myMiddleware(req, res, next) {
  // 做一些处理
  console.log('中间件执行了');
  // 调用下一个中间件
  next();
}

// 使用中间件
app.use(myMiddleware);

注意:一定要调用 next(),否则请求会卡住。我曾经调试一个 bug 调了半小时,最后发现就是忘了写 next()。嗯,这种错误犯过一次就不会再犯了。

4.4.2 常用内置中间件

Express 自带了一些中间件,我列几个最常用的:

中间件 作用 使用方式
express.json() 解析 JSON 请求体 app.use(express.json())
express.urlencoded() 解析 URL 编码的请求体 app.use(express.urlencoded({ extended: true }))
express.static() 提供静态文件服务 app.use(express.static('public'))

举个例子,如果你要处理表单提交,必须加上 express.urlencoded()。否则 req.body 会是 undefined。我见过太多人在这上面栽跟头了。

4.4.3 自定义中间件实战

咱们写一个日志中间件,记录每个请求的方法和 URL:

const logger = (req, res, next) => {
  const start = Date.now();
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  
  // 监听响应结束事件
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`  响应状态: ${res.statusCode},耗时: ${duration}ms`);
  });
  
  next();
};

app.use(logger);

这个中间件会在每个请求进来时打印日志,并在响应结束时打印耗时。你想想看,如果每个路由都手动加这些代码,那得多痛苦。中间件就是用来干这种「横切关注点」的事。

4.5 请求与响应处理

这部分是实际开发中打交道最多的。咱们一个一个说。

4.5.1 请求对象 (req)

req 对象包含了客户端发来的所有信息。常用的属性有:

  • req.params - 路由参数,比如 /users/:id 中的 id
  • req.query - URL 查询参数,比如 ?name=john&age=25
  • req.body - 请求体,需要配合中间件解析
  • req.headers - 请求头
  • req.method - HTTP 方法
  • req.path - 请求路径
app.get('/search', (req, res) => {
  const { q, page = 1 } = req.query;
  res.send(`搜索关键词: ${q},页码: ${page}`);
});

app.post('/users', (req, res) => {
  const { name, email } = req.body;
  res.send(`创建用户: ${name} (${email})`);
});

4.5.2 响应对象 (res)

res 对象用来给客户端返回数据。常用的方法:

  • res.send() - 发送各种类型的数据(字符串、Buffer、对象、数组)
  • res.json() - 发送 JSON 格式数据
  • res.status() - 设置 HTTP 状态码
  • res.redirect() - 重定向
  • res.render() - 渲染模板(需要配置模板引擎)
app.get('/api/user', (req, res) => {
  const user = {
    id: 1,
    name: '张三',
    email: 'zhangsan@example.com'
  };
  
  // 设置状态码并返回 JSON
  res.status(200).json({
    success: true,
    data: user
  });
});

app.get('/old-page', (req, res) => {
  // 重定向到新页面
  res.redirect(301, '/new-page');
});
小技巧: 我习惯在 API 响应中统一使用 { success: true/false, data: ..., message: ... } 这种格式。前端处理起来特别方便,后端也容易维护。

4.6 错误处理中间件

最后聊一个重要的东西:错误处理。Express 有专门的错误处理中间件,它有四个参数:

app.use((err, req, res, next) => {
  console.error('发生错误:', err.stack);
  
  res.status(err.status || 500).json({
    success: false,
    message: err.message || '服务器内部错误'
  });
});

注意:错误处理中间件必须放在所有路由和中间件之后。而且它必须有四个参数,少一个都不行。我曾经因为少写了 next 参数,导致错误处理不生效,查了半天才发现。

避坑指南: 在异步路由中,如果使用了 async/await,一定要用 try/catch 捕获错误,或者使用 express-async-errors 这样的库。否则未捕获的 Promise 异常会导致服务器崩溃。

好了,Express 的基础就这些。你想想看,从安装到路由,从中间件到请求响应,其实核心概念并不多。但就是这些基础的东西,组合起来能构建出非常强大的 API。下一章咱们会深入 GraphQL 与 Express 的集成,到时候你会看到这些知识是如何串联起来的。