Express.js 教程

1. 项目概述

Express.js 是基于 Node.js 的快速、无偏见、极简的 Web 框架,为构建 Web 应用和 API 提供了强大的功能。它是 Node.js 生态系统中最流行的 Web 框架之一,被广泛用于构建各种规模的后端服务。

1.1 主要特性

  • 轻量级设计:核心功能精简,通过中间件扩展功能
  • 灵活的路由系统:支持 RESTful API 设计
  • 强大的中间件支持:可自定义和使用第三方中间件
  • 模板引擎集成:支持多种模板引擎
  • 错误处理机制:内置错误处理
  • 静态文件服务:方便提供静态资源
  • 生态系统丰富:拥有大量第三方模块

1.2 适用场景

  • 构建 RESTful API
  • Web 应用后端开发
  • 微服务架构
  • 实时应用(结合 Socket.io)
  • 原型开发和快速迭代

2. 安装与设置

2.1 环境要求

  • Node.js 14.0 或更高版本
  • npm 或 yarn 包管理器

2.2 安装步骤

方法一:使用 Express 生成器

# 全局安装 Express 生成器
npm install -g express-generator

# 创建项目
express my-express-app

# 进入项目目录
cd my-express-app

# 安装依赖
npm install

方法二:手动创建项目

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

# 初始化项目
npm init -y

# 安装 Express
npm install express

2.3 基本项目结构

my-express-app/
├── node_modules/
├── public/            # 静态资源
├── routes/            # 路由文件
├── views/             # 模板文件
├── app.js             # 应用主文件
├── package.json       # 项目配置
└── package-lock.json  # 依赖锁定

3. 核心概念

3.1 应用实例

Express 应用是通过 express() 函数创建的,它是整个应用的核心。

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

// 启动服务器
app.listen(3000, () => {
  console.log('Server running on port 3000');
});

3.2 路由

路由定义了应用如何响应客户端对特定端点的请求,包括 URL 和 HTTP 请求方法。

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

// 带参数的路由
app.get('/users/:id', (req, res) => {
  res.send(`User ID: ${req.params.id}`);
});

// 处理 POST 请求
app.post('/users', (req, res) => {
  // 处理用户创建逻辑
  res.send('User created');
});

3.3 中间件

中间件函数是可以访问请求对象 (req)、响应对象 (res) 和应用程序请求-响应循环中的下一个中间件函数的函数。

// 自定义中间件
const logger = (req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
  next(); // 调用下一个中间件
};

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

// 内置中间件
app.use(express.json()); // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true })); // 解析 URL 编码的请求体
app.use(express.static('public')); // 提供静态文件

3.4 请求对象 (req)

请求对象包含了客户端请求的所有信息。

app.get('/profile', (req, res) => {
  console.log(req.params); // 路由参数
  console.log(req.query); // 查询字符串参数
  console.log(req.body); // 请求体
  console.log(req.headers); // 请求头
  console.log(req.method); // HTTP 方法
  console.log(req.url); // 请求 URL
  res.send('Profile page');
});

3.5 响应对象 (res)

响应对象用于向客户端发送响应。

app.get('/data', (req, res) => {
  // 发送文本响应
  res.send('Hello');
  
  // 发送 JSON 响应
  res.json({ name: 'John', age: 30 });
  
  // 发送文件
  res.sendFile(__dirname + '/index.html');
  
  // 设置状态码
  res.status(404).send('Not Found');
  
  // 重定向
  res.redirect('/home');
});

4. 基本使用

4.1 创建简单的 Web 服务器

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

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

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

4.2 构建 RESTful API

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

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

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

// GET 所有用户
app.get('/api/users', (req, res) => {
  res.json(users);
});

// GET 单个用户
app.get('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).send('User not found');
  res.json(user);
});

// POST 创建用户
app.post('/api/users', (req, res) => {
  const newUser = {
    id: users.length + 1,
    name: req.body.name,
    email: req.body.email
  };
  users.push(newUser);
  res.status(201).json(newUser);
});

// PUT 更新用户
app.put('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).send('User not found');
  
  user.name = req.body.name;
  user.email = req.body.email;
  res.json(user);
});

// DELETE 删除用户
app.delete('/api/users/:id', (req, res) => {
  const userIndex = users.findIndex(u => u.id === parseInt(req.params.id));
  if (userIndex === -1) return res.status(404).send('User not found');
  
  users.splice(userIndex, 1);
  res.status(204).send();
});

app.listen(3000, () => console.log('Server running on port 3000'));

4.3 组织路由

使用路由模块

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

// 模拟数据
let users = [/* 用户数据 */];

// 路由处理
router.get('/', (req, res) => {
  res.json(users);
});

// 其他路由...

module.exports = router;

在主应用中使用

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

// 使用路由
app.use('/api/users', usersRouter);

app.listen(3000);

5. 高级功能

5.1 中间件开发

创建自定义中间件

// middleware/auth.js
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization;
  
  if (!token) {
    return res.status(401).json({ message: 'Access denied' });
  }
  
  // 验证 token...
  
  next();
};

module.exports = authMiddleware;

使用中间件

const authMiddleware = require('./middleware/auth');

// 应用于所有路由
app.use(authMiddleware);

// 应用于特定路由
app.get('/protected', authMiddleware, (req, res) => {
  res.send('Protected route');
});

5.2 错误处理

全局错误处理中间件

// 404 错误处理
app.use((req, res, next) => {
  res.status(404).json({ message: 'Route not found' });
});

// 全局错误处理
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ message: 'Internal server error' });
});

5.3 环境配置

使用 dotenv 管理环境变量

npm install dotenv
// .env
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
// app.js
require('dotenv').config();
const express = require('express');
const app = express();

const port = process.env.PORT || 3000;

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

5.4 数据库集成

集成 MongoDB(使用 Mongoose)

npm install mongoose
const mongoose = require('mongoose');

// 连接数据库
mongoose.connect('mongodb://localhost:27017/myapp')
  .then(() => console.log('Connected to MongoDB'))
  .catch(err => console.error('MongoDB connection error:', err));

// 定义模型
const User = mongoose.model('User', {
  name: String,
  email: String
});

// 使用模型
app.get('/users', async (req, res) => {
  const users = await User.find();
  res.json(users);
});

6. 实用案例

6.1 构建博客 API

项目结构

blog-api/
├── app.js
├── routes/
│   ├── posts.js
│   └── users.js
├── models/
│   ├── Post.js
│   └── User.js
└── package.json

核心代码

// models/Post.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Post', postSchema);
// routes/posts.js
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');

// 获取所有文章
router.get('/', async (req, res) => {
  const posts = await Post.find().populate('author');
  res.json(posts);
});

// 创建文章
router.post('/', async (req, res) => {
  const post = new Post({
    title: req.body.title,
    content: req.body.content,
    author: req.body.authorId
  });
  await post.save();
  res.status(201).json(post);
});

// 其他路由...

module.exports = router;

6.2 实现文件上传

安装依赖

npm install multer

实现代码

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

// 配置存储
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname));
  }
});

// 创建上传中间件
const upload = multer({ storage: storage });

// 确保上传目录存在
const fs = require('fs');
if (!fs.existsSync('uploads')) {
  fs.mkdirSync('uploads');
}

// 提供静态文件
app.use('/uploads', express.static('uploads'));

// 文件上传路由
app.post('/upload', upload.single('file'), (req, res) => {
  res.json({ 
    message: 'File uploaded successfully',
    file: req.file
  });
});

// 多文件上传
app.post('/upload-multiple', upload.array('files', 5), (req, res) => {
  res.json({ 
    message: 'Files uploaded successfully',
    files: req.files
  });
});

app.listen(3000);

7. 性能优化

7.1 代码优化

  • 使用路由模块化:将路由分离到不同文件
  • 合理使用中间件:只在需要的地方使用中间件
  • 避免同步操作:使用异步函数和 Promise
  • 优化数据库查询:使用索引和分页

7.2 服务器配置

  • 启用压缩:使用 compression 中间件
  • 设置缓存头:为静态文件设置合理的缓存
  • 使用 HTTPS:提高安全性和 SEO
  • 配置 CORS:根据需要设置跨域资源共享

7.3 监控与调试

  • 使用 Morgan 日志:记录请求信息
  • 实现请求计时:监控响应时间
  • 错误日志记录:捕获和记录错误
  • 使用 PM2 管理进程:实现负载均衡和自动重启

8. 最佳实践

8.1 代码组织

  • 模块化设计:将功能分离到不同模块
  • 一致的命名规范:使用清晰的命名
  • 代码注释:为复杂逻辑添加注释
  • 版本控制:使用 Git 管理代码

8.2 安全措施

  • 输入验证:验证所有用户输入
  • 防止 SQL 注入:使用参数化查询或 ORM
  • 防止 XSS 攻击:转义输出
  • 使用 Helmet:设置安全相关的 HTTP 头
  • 密码加密:使用 bcrypt 等库

8.3 部署建议

  • 使用环境变量:分离配置和代码
  • 使用 Docker:容器化部署
  • 持续集成/持续部署:自动化测试和部署
  • 监控系统:使用工具监控应用状态

9. 常见问题与解决方案

9.1 路由冲突

问题:路由顺序导致某些路由不被触发

解决方案

  • 确保更具体的路由在前面
  • 使用 app.use() 时注意路径匹配

9.2 中间件执行顺序

问题:中间件执行顺序不符合预期

解决方案

  • 按照正确的顺序注册中间件
  • 理解中间件的执行流程

9.3 CORS 错误

问题:跨域请求被阻止

解决方案

  • 使用 cors 中间件
  • 手动设置 CORS 头

9.4 内存泄漏

问题:应用内存使用持续增长

解决方案

  • 避免全局变量累积
  • 正确关闭数据库连接
  • 使用内存分析工具

10. 参考资源

10.1 官方文档

10.2 学习资源

10.3 第三方模块

10.4 工具与服务

11. 总结

Express.js 是一个轻量级但功能强大的 Node.js Web 框架,它的设计理念是 "少即是多",通过简洁的 API 提供了构建 Web 应用所需的核心功能。它的灵活性和可扩展性使其成为 Node.js 生态系统中最受欢迎的 Web 框架之一。

通过本教程,你应该已经掌握了 Express.js 的基本使用方法和高级功能,包括:

  • 项目创建和配置
  • 路由和中间件
  • RESTful API 构建
  • 数据库集成
  • 文件上传
  • 错误处理
  • 性能优化
  • 最佳实践

Express.js 适合从简单的个人项目到复杂的企业级应用的各种场景。结合其丰富的生态系统和活跃的社区,它可以帮助你快速构建高性能、可维护的 Web 应用后端。

随着你对 Express.js 的深入了解,你会发现它不仅是一个 Web 框架,更是一种构建 Web 应用的思想和方法论。希望本教程对你的学习和开发有所帮助!

« 上一篇 Next.js 教程 - 基于 React 的全栈框架 下一篇 » NestJS 教程 - 基于 TypeScript 的 Node.js 框架