Express.js 基础

32 阅读10分钟

一、Express 简介与安装

什么是 Express

Express 是一个基于 Node.js 的极简且灵活的 Web 应用程序框架,提供了一组强大的功能,适用于 Web 和移动应用程序的开发。它构建在 Node.js 的 HTTP 模块之上,简化了服务器端应用程序的开发过程。

安装 Express

在使用 Express 之前,需要先安装它。Express 通过 npm(Node Package Manager)进行安装和管理。

# 创建项目目录
mkdir my-express-app
cd my-express-app

# 初始化项目(创建 package.json)
npm init -y

# 安装 Express
npm install express

二、应用创建

最简单的 Express 应用

// 引入 Express 模块
const express = require('express');

// 创建 Express 应用实例
const app = express();

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

// 启动服务器,监听指定端口
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

express() 函数

express() 是一个工厂函数,用于创建 Express 应用实例。这个实例包含了所有 Express 的功能,包括路由、中间件、模板引擎等。

const express = require('express');
const app = express();  // 创建应用实例

// app 对象现在包含了所有 Express 的方法和属性
console.log(typeof app);  // 'function'(实际上是一个函数对象)

app.listen() 方法

app.listen() 方法用于启动服务器并开始监听指定端口的 HTTP 请求。它是 Express 应用启动的关键方法。

基本语法:

app.listen(port, callback)

参数说明:

  • port: 要监听的端口号(数字)
    • 端口号范围:有效端口号为 0-65535
    • 推荐端口:开发环境常用 3000、3001、8000、8080、5000 等
    • 不能使用的端口
      • 0-1023:系统保留端口(需要管理员权限),常见的有:
        • 80:HTTP(通常需要 root 权限)
        • 443:HTTPS(通常需要 root 权限)
        • 22:SSH
        • 21:FTP
        • 25:SMTP
        • 3306:MySQL
        • 5432:PostgreSQL
      • 1024-49151:注册端口(IANA 注册),应避免与已知服务冲突
      • 49152-65535:动态/私有端口(相对安全,适合开发使用)
    • 注意事项:如果端口已被占用,会抛出 EADDRINUSE 错误
  • callback: 服务器启动后执行的回调函数(可选)

示例:

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

// 方式一:只指定端口
app.listen(3000);

// 方式二:指定端口和回调函数(推荐)
app.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

// 方式三:使用环境变量配置端口
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 ${PORT} 端口`);
});

使用 Node.js HTTP 模块(底层方式):

Express 的 app.listen() 实际上是对 Node.js http.createServer() 的封装。你也可以直接使用 HTTP 模块:

const express = require('express');
const http = require('http');

const app = express();
const server = http.createServer(app);

server.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

基本应用结构

一个典型的 Express 应用结构如下:

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

// 1. 配置中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 2. 定义路由
app.get('/', (req, res) => {
  res.send('Hello World!');
});

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

三、环境变量

process.env

process.env 是 Node.js 的全局对象,用于访问环境变量。在 Express 应用中,常用环境变量来配置端口、数据库连接等。

基本用法

// 获取环境变量
const PORT = process.env.PORT || 3000;
const NODE_ENV = process.env.NODE_ENV || 'development';

app.listen(PORT, () => {
  console.log(`服务器运行在 ${PORT} 端口`);
  console.log(`当前环境: ${NODE_ENV}`);
});

环境配置

使用 .env 文件

安装 dotenv 包来管理环境变量:

npm install dotenv

创建 .env 文件:

PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key

在应用入口文件中加载:

require('dotenv').config();

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

const PORT = process.env.PORT || 3000;
const NODE_ENV = process.env.NODE_ENV || 'development';

app.listen(PORT, () => {
  console.log(`服务器运行在 ${PORT} 端口`);
  console.log(`当前环境: ${NODE_ENV}`);
});

开发/生产环境区分

require('dotenv').config();

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

const NODE_ENV = process.env.NODE_ENV || 'development';
const PORT = process.env.PORT || 3000;

// 根据环境配置不同的行为
if (NODE_ENV === 'development') {
  // 开发环境:启用详细日志
  app.use((req, res, next) => {
    console.log(`${req.method} ${req.path}`);
    next();
  });
} else if (NODE_ENV === 'production') {
  // 生产环境:启用压缩、安全头等
  app.use(express.static('public'));
}

app.get('/', (req, res) => {
  res.json({
    message: 'Hello World!',
    environment: NODE_ENV
  });
});

app.listen(PORT, () => {
  console.log(`服务器运行在 ${PORT} 端口`);
  console.log(`环境: ${NODE_ENV}`);
});

四、应用配置

app.set() - 设置应用配置

app.set() 用于设置应用级别的配置值。

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

// 设置配置项
app.set('port', 3000);
app.set('env', 'development');
app.set('view engine', 'ejs');  // 设置模板引擎
app.set('views', './views');    // 设置视图目录

app.get() - 获取应用配置

app.get() 用于获取应用级别的配置值。

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

// 设置配置
app.set('port', 3000);
app.set('env', 'development');

// 获取配置
const port = app.get('port');
const env = app.get('env');

console.log(`端口: ${port}`);  // 端口: 3000
console.log(`环境: ${env}`);   // 环境: development

应用级配置

Express 提供了一些内置的配置项:

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

// 常用配置项
app.set('env', process.env.NODE_ENV || 'development');  // 环境
app.set('port', process.env.PORT || 3000);              // 端口
app.set('view engine', 'ejs');                           // 模板引擎
app.set('views', './views');                             // 视图目录
app.set('json spaces', 2);                               // JSON 格式化空格数
app.set('x-powered-by', false);                          // 禁用 X-Powered-By 头

// 获取配置
const port = app.get('port');
app.listen(port, () => {
  console.log(`服务器运行在 ${port} 端口`);
});

五、核心对象

请求对象(req)

请求对象(req)代表 HTTP 请求,包含了客户端发送的所有信息,包括请求头、请求参数、请求体等。

req 的常用属性

app.get('/example', (req, res) => {
  // 请求方法
  console.log(req.method);        // 'GET'
  
  // 请求路径(不包含查询字符串)
  console.log(req.path);          // '/example'
  
  // 完整 URL(包含查询字符串)
  console.log(req.url);           // '/example?name=John'
  
  // 请求头
  console.log(req.headers);       // 请求头对象
  
  // 客户端 IP 地址
  console.log(req.ip);            // '::ffff:127.0.0.1'
  
  // 协议
  console.log(req.protocol);      // 'http'
  
  // 主机名
  console.log(req.hostname);      // 'localhost'
  
  res.send('查看控制台输出');
});

响应对象(res)

响应对象(res)代表 HTTP 响应,用于向客户端发送数据、设置响应头、设置状态码等。

res 的常用属性

app.get('/example', (req, res) => {
  // 设置响应头
  res.set('Content-Type', 'application/json');
  
  // 获取响应头
  const contentType = res.get('Content-Type');
  
  // 设置状态码
  res.status(200);
  
  // 发送响应
  res.json({ message: 'Hello' });
});

六、请求对象扩展

req.params - 路径参数

路径参数是 URL 路径中的动态部分,使用 : 定义,通过 req.params 对象访问。

基本用法

// 定义路径参数
app.get('/users/:id', (req, res) => {
  console.log(req.params);  // { id: '123' }
  console.log(req.params.id);  // '123'
  
  res.json({
    userId: req.params.id,
    message: '获取用户信息'
  });
});

// 访问 /users/123
// req.params = { id: '123' }

多个路径参数

一个路由可以定义多个路径参数:

app.get('/users/:userId/posts/:postId', (req, res) => {
  console.log(req.params);
  // 访问 /users/123/posts/456
  // req.params = { userId: '123', postId: '456' }
  
  res.json({
    userId: req.params.userId,
    postId: req.params.postId
  });
});

req.query - 查询参数

查询参数是 URL 中 ? 后面的键值对,通过 req.query 对象访问。查询参数用于过滤、排序、分页等操作。

基本用法

// 访问 /search?q=express&page=1
app.get('/search', (req, res) => {
  console.log(req.query);  // { q: 'express', page: '1' }
  console.log(req.query.q);  // 'express'
  console.log(req.query.page);  // '1'
  
  res.json({
    query: req.query.q,
    page: req.query.page
  });
});

多个查询参数

// 访问 /products?category=electronics&minPrice=100&maxPrice=500&sort=price
app.get('/products', (req, res) => {
  const { category, minPrice, maxPrice, sort } = req.query;
  
  console.log('分类:', category);
  console.log('最低价格:', minPrice);
  console.log('最高价格:', maxPrice);
  console.log('排序:', sort);
  
  res.json({
    category: category,
    minPrice: minPrice,
    maxPrice: maxPrice,
    sort: sort
  });
});

数组查询参数

查询参数可以是数组:

// 访问 /products?tags=javascript&tags=nodejs&tags=express
app.get('/products', (req, res) => {
  console.log(req.query.tags);  // ['javascript', 'nodejs', 'express']
  
  res.json({
    tags: req.query.tags
  });
});

req.body - 请求体

请求体包含 POST、PUT 等请求中发送的数据。需要中间件来解析(如 express.json())。

JSON 数据

// 需要先配置中间件
app.use(express.json());

app.post('/users', (req, res) => {
  console.log(req.body);  // { name: 'John', email: 'john@example.com' }
  console.log(req.body.name);  // 'John'
  
  res.json({
    id: 1,
    name: req.body.name,
    email: req.body.email
  });
});

URL 编码数据

// 配置中间件解析 URL 编码数据
app.use(express.urlencoded({ extended: true }));

app.post('/users', (req, res) => {
  console.log(req.body);  // { name: 'John', email: 'john@example.com' }
  
  res.json({
    id: 1,
    name: req.body.name,
    email: req.body.email
  });
});

七、响应方法

res.send() - 通用响应方法

res.send() 是最灵活的响应方法,可以发送各种类型的数据。Express 会根据数据类型自动设置适当的 Content-Type

app.get('/', (req, res) => {
  res.send('Hello World!');  // 发送字符串,Content-Type: text/html
});

app.get('/json', (req, res) => {
  res.send({ message: 'Hello' });  // 发送对象,Content-Type: application/json
});

app.get('/buffer', (req, res) => {
  res.send(Buffer.from('Hello'));  // 发送 Buffer,Content-Type: application/octet-stream
});

app.get('/api/users', (req, res) => {
  res.send([
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
  ]);  // 发送数组,Content-Type: application/json
});

res.json() - JSON 响应

res.json() 专门用于发送 JSON 响应,会自动设置 Content-Typeapplication/json,并调用 JSON.stringify()

app.get('/api/user', (req, res) => {
  res.json({
    id: 1,
    name: 'John Doe',
    email: 'john@example.com'
  });
});

app.get('/api/users', (req, res) => {
  res.json([
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
  ]);
});

res.status() - 设置状态码

res.status() 用于设置 HTTP 响应状态码,通常与其他响应方法链式调用。

// 成功响应(200)
app.get('/success', (req, res) => {
  res.status(200).json({ message: '成功' });
});

// 创建成功(201)
app.post('/users', (req, res) => {
  res.status(201).json({ id: 1, name: 'John' });
});

// 未找到(404)
app.get('/not-found', (req, res) => {
  res.status(404).json({ error: '资源未找到' });
});

// 服务器错误(500)
app.get('/error', (req, res) => {
  res.status(500).json({ error: '服务器内部错误' });
});

常用 HTTP 状态码

// 2xx - 成功
res.status(200).json({ message: 'OK' });           // 成功
res.status(201).json({ message: 'Created' });       // 创建成功
res.status(204).send();                             // 无内容(常用于 DELETE)

// 4xx - 客户端错误
res.status(400).json({ error: 'Bad Request' });     // 请求错误
res.status(401).json({ error: 'Unauthorized' });   // 未授权
res.status(403).json({ error: 'Forbidden' });       // 禁止访问
res.status(404).json({ error: 'Not Found' });       // 未找到
res.status(409).json({ error: 'Conflict' });       // 冲突

// 5xx - 服务器错误
res.status(500).json({ error: 'Internal Server Error' });  // 服务器错误
res.status(503).json({ error: 'Service Unavailable' });     // 服务不可用

res.redirect() - 重定向

res.redirect() 用于将客户端重定向到另一个 URL。这对于页面跳转、URL 重写等场景非常有用。

// 临时重定向(302)
app.get('/old-page', (req, res) => {
  res.redirect('/new-page');
});

// 永久重定向(301)
app.get('/old-url', (req, res) => {
  res.redirect(301, '/new-url');
});

// 重定向到外部 URL
app.get('/external', (req, res) => {
  res.redirect('https://www.example.com');
});

// 重定向到相对路径
app.get('/login', (req, res) => {
  // 登录成功后重定向到首页
  res.redirect('/');
});

app.get('/users/:id/edit', (req, res) => {
  // 编辑完成后重定向到用户详情页
  const userId = req.params.id;
  res.redirect(`/users/${userId}`);
});

八、路由定义

路由是 Express 应用的核心功能之一。它定义了应用如何响应客户端对不同 URL 路径和 HTTP 方法的请求。

什么是路由

路由是指确定应用程序如何响应客户端对特定端点的请求,该端点是 URI(或路径)和特定的 HTTP 请求方法(GET、POST、PUT、DELETE 等)的组合。

路由的基本结构

每个路由都有一个或多个处理函数,当路由匹配时执行:

app.METHOD(path, handler)
  • app: Express 应用实例
  • METHOD: HTTP 请求方法(get、post、put、delete 等)
  • path: 服务器上的路径
  • handler: 路由匹配时执行的函数

app.get() - GET 请求

GET 请求用于获取资源,是最常用的 HTTP 方法。

// 获取所有用户
app.get('/users', (req, res) => {
  res.json([
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
  ]);
});

// 获取单个用户
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: 'John' });
});

app.post() - POST 请求

POST 请求用于创建新资源。

// 需要配置中间件解析请求体
app.use(express.json());

// 创建用户
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  
  // 创建用户的逻辑...
  
  res.status(201).json({
    id: 1,
    name: name,
    email: email,
    message: '用户创建成功'
  });
});

app.put() - PUT 请求

PUT 请求用于更新整个资源。

app.use(express.json());

// 更新用户(完整更新)
app.put('/users/:id', (req, res) => {
  const userId = req.params.id;
  const { name, email } = req.body;
  
  // 更新用户的逻辑...
  
  res.json({
    id: userId,
    name: name,
    email: email,
    message: '用户更新成功'
  });
});

app.delete() - DELETE 请求

DELETE 请求用于删除资源。

// 删除用户
app.delete('/users/:id', (req, res) => {
  const userId = req.params.id;
  
  // 删除用户的逻辑...
  
  res.status(204).send();  // 204 No Content
});

路由路径匹配

Express 支持多种路由路径匹配方式,包括字符串匹配、正则表达式匹配和参数匹配。

字符串路径匹配

最简单的路由路径是字符串匹配:

// 精确匹配
app.get('/about', (req, res) => {
  res.send('关于页面');
});

// 匹配根路径
app.get('/', (req, res) => {
  res.send('首页');
});

路径参数匹配

使用 : 定义路径参数:

// 单个参数
app.get('/users/:id', (req, res) => {
  res.json({ userId: req.params.id });
});

// 多个参数
app.get('/users/:userId/posts/:postId', (req, res) => {
  res.json({
    userId: req.params.userId,
    postId: req.params.postId
  });
});

可选参数

使用 ? 定义可选参数:

// /users 和 /users/:id 都可以匹配
app.get('/users/:id?', (req, res) => {
  if (req.params.id) {
    res.json({ userId: req.params.id });
  } else {
    res.json({ message: '所有用户' });
  }
});

通配符匹配

使用 * 进行通配符匹配:

// 匹配 /users/ 后面的所有路径
app.get('/users/*', (req, res) => {
  res.send('用户相关页面');
});

正则表达式匹配

使用正则表达式进行复杂匹配:

// 只匹配数字 ID
app.get('/users/:id(\\d+)', (req, res) => {
  res.json({ userId: req.params.id });
});

// 匹配特定格式
app.get('/files/:filename(.*\\.(jpg|png|gif))', (req, res) => {
  res.send(`图片文件: ${req.params.filename}`);
});

九、路由参数

路径参数和查询参数是 Express 路由中两种重要的参数类型。详细的使用方法请参考"六、请求对象扩展"章节。本节重点说明两者的区别和使用场景。

路径参数(:id、req.params)

路径参数是 URL 路径中的动态部分,使用 : 定义,通过 req.params 对象访问。详细用法请参考"六、请求对象扩展"章节中的 req.params 部分。

特点:

  • 用于标识资源(如用户 ID、文章 ID)
  • URL 路径的一部分,必需
  • 示例:/users/123(123 是用户 ID)

路径参数命名规则:

  • 名称可以是字母、数字和下划线的组合
  • 不能包含特殊字符(如连字符)
// 有效
app.get('/users/:id', handler);
app.get('/users/:userId', handler);
app.get('/users/:user_id', handler);

// 无效(不能包含特殊字符)
// app.get('/users/:user-id', handler);  // 错误

查询参数(req.query)

查询参数是 URL 中 ? 后面的键值对,通过 req.query 对象访问。详细用法请参考"六、请求对象扩展"章节中的 req.query 部分。

特点:

  • 用于过滤、排序、分页等操作
  • URL 的可选部分
  • 示例:/users?page=1&limit=10(page 和 limit 用于分页)

路径参数 vs 查询参数

理解路径参数和查询参数的区别和使用场景很重要:

特性路径参数(req.params)查询参数(req.query)
用途标识资源(如用户 ID、文章 ID)过滤、排序、分页等操作
位置URL 路径的一部分URL 中 ? 后面的键值对
是否必需必需(定义在路由路径中)可选
示例/users/123(123 是用户 ID)/users?page=1&limit=10(分页参数)
组合使用可以同时使用:/users/123/posts?page=1

最佳实践:

  • 使用路径参数标识资源:GET /users/:idDELETE /posts/:id
  • 使用查询参数进行过滤和分页:GET /users?page=1&limit=10&role=admin

十、路由模块化

express.Router()

express.Router() 用于创建模块化的路由处理器。它允许将路由组织到单独的文件中,使代码更加清晰和可维护。

基本用法

创建一个路由模块:

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

// 定义路由
router.get('/', (req, res) => {
  res.json([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
});

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

router.post('/', (req, res) => {
  res.status(201).json({ id: 1, name: req.body.name });
});

module.exports = router;

在主应用中使用:

// app.js
const express = require('express');
const usersRouter = require('./routes/users');

const app = express();

app.use(express.json());

// 挂载路由
app.use('/users', usersRouter);

app.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

路由分离

将不同功能的路由分离到不同的文件中:

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

router.get('/', (req, res) => {
  res.json({ message: '获取所有用户' });
});

router.get('/:id', (req, res) => {
  res.json({ message: `获取用户 ${req.params.id}` });
});

module.exports = router;
// routes/posts.js
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.json({ message: '获取所有文章' });
});

router.get('/:id', (req, res) => {
  res.json({ message: `获取文章 ${req.params.id}` });
});

module.exports = router;
// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const postsRouter = require('./routes/posts');

const app = express();

app.use(express.json());

// 挂载路由
app.use('/users', usersRouter);
app.use('/posts', postsRouter);

app.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

路由模块化

在路由模块中可以定义中间件:

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

// 路由级别的中间件
router.use((req, res, next) => {
  console.log('用户路由请求:', req.method, req.path);
  next();
});

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

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

module.exports = router;

路由组织

可以创建嵌套的路由结构:

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

router.get('/', (req, res) => {
  res.json({ message: '获取所有用户' });
});

router.get('/:id', (req, res) => {
  res.json({ message: `获取用户 ${req.params.id}` });
});

// 嵌套路由:用户文章
router.get('/:id/posts', (req, res) => {
  res.json({ message: `获取用户 ${req.params.id} 的文章` });
});

module.exports = router;

app.use() 挂载路由

使用 app.use() 将路由挂载到应用上:

const express = require('express');
const usersRouter = require('./routes/users');
const postsRouter = require('./routes/posts');

const app = express();

app.use(express.json());

// 挂载路由(无前缀)
app.use(usersRouter);

// 挂载路由(带前缀)
app.use('/api/users', usersRouter);
app.use('/api/posts', postsRouter);

app.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

路由前缀

使用路由前缀可以统一管理 API 版本或路径:

// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const postsRouter = require('./routes/posts');

const app = express();

app.use(express.json());

// 使用路由前缀
app.use('/api/v1/users', usersRouter);
app.use('/api/v1/posts', postsRouter);

// 或者统一前缀
const apiRouter = express.Router();
apiRouter.use('/users', usersRouter);
apiRouter.use('/posts', postsRouter);
app.use('/api/v1', apiRouter);

app.listen(3000, () => {
  console.log('服务器运行在 3000 端口');
});

十一、完整示例:RESTful API

下面是一个完整的 RESTful API 示例,展示了 Express 的各种功能的使用:

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

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

// 模拟数据存储
let users = [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];

// GET /users - 获取所有用户(支持分页和过滤)
app.get('/users', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  const role = req.query.role;  // 可选的角色过滤
  
  res.status(200).json({
    success: true,
    page: page,
    limit: limit,
    count: users.length,
    data: users
  });
});

// GET /users/:id - 获取单个用户
app.get('/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  const user = users.find(u => u.id === userId);
  
  if (!user) {
    return res.status(404).json({
      success: false,
      error: '用户未找到'
    });
  }
  
  res.status(200).json({
    success: true,
    data: user
  });
});

// POST /users - 创建用户
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  
  if (!name || !email) {
    return res.status(400).json({
      success: false,
      error: '缺少必要字段:name 和 email'
    });
  }
  
  const newUser = {
    id: users.length + 1,
    name: name,
    email: email
  };
  
  users.push(newUser);
  
  res.status(201).json({
    success: true,
    message: '用户创建成功',
    data: newUser
  });
});

// PUT /users/:id - 更新用户(完整更新)
app.put('/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  const userIndex = users.findIndex(u => u.id === userId);
  
  if (userIndex === -1) {
    return res.status(404).json({
      success: false,
      error: '用户未找到'
    });
  }
  
  const { name, email } = req.body;
  users[userIndex] = { ...users[userIndex], name, email };
  
  res.status(200).json({
    success: true,
    message: '用户更新成功',
    data: users[userIndex]
  });
});

// DELETE /users/:id - 删除用户
app.delete('/users/:id', (req, res) => {
  const userId = parseInt(req.params.id);
  const userIndex = users.findIndex(u => u.id === userId);
  
  if (userIndex === -1) {
    return res.status(404).json({
      success: false,
      error: '用户未找到'
    });
  }
  
  users.splice(userIndex, 1);
  
  res.status(204).send();  // 204 No Content
});

// 获取用户的文章列表(路径参数 + 查询参数)
app.get('/users/:userId/posts', (req, res) => {
  const userId = req.params.userId;  // 路径参数
  const page = parseInt(req.query.page) || 1;  // 查询参数
  const limit = parseInt(req.query.limit) || 10;  // 查询参数
  const sort = req.query.sort || 'createdAt';  // 查询参数
  
  res.json({
    userId: userId,
    page: page,
    limit: limit,
    sort: sort,
    posts: []
  });
});

// 重定向示例
app.get('/home', (req, res) => {
  res.redirect('/');
});

app.get('/', (req, res) => {
  res.send('欢迎访问用户管理 API');
});

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

总结

  1. Express 简介与安装:了解 Express 是什么以及如何安装
  2. 应用创建:通过 express() 创建应用实例,使用 app.listen() 启动服务器
  3. 环境变量:使用 process.env 管理环境配置,区分开发/生产环境
  4. 应用配置:使用 app.set()app.get() 进行应用级配置
  5. 核心对象:请求对象(req)和响应对象(res)的常用属性
  6. 请求对象扩展:通过 req.bodyreq.paramsreq.query 获取请求数据
  7. 响应方法:使用 res.send()res.json()res.status()res.redirect() 等方法发送响应
  8. 路由定义:使用 app.get()app.post()app.put()app.delete() 等方法定义路由
  9. 路由参数:路径参数(:idreq.params)和查询参数(req.query)的使用
  10. 路由模块化:使用 express.Router() 进行路由分离和模块化组织

参考资源