跳到主要内容

Express框架完全指南

1. Express框架概述

Express是一个基于Node.js平台的Web应用开发框架,由TJ Holowaychuk于2010年创建。它提供了一系列强大特性帮助你创建各种Web和移动设备应用。Express被称为"Node.js的瑞士军刀",是Node.js生态系统中最流行的Web框架之一。

1.1 核心特点

  • 轻量级:仅提供必要的功能,无冗余代码
  • 灵活:不强制使用特定的模板引擎或ORM
  • 高性能:基于Node.js的非阻塞I/O模型
  • 丰富的中间件生态:通过中间件扩展功能
  • 简洁而灵活的路由系统:便于定义应用的端点
  • 高效的中间件机制:可扩展性强
  • 丰富的HTTP工具:简化请求和响应处理
  • 模板引擎支持:便于渲染动态内容
  • 易于集成数据库:可与多种数据库无缝集成
  • 社区活跃:拥有庞大的社区和丰富的资源

1.2 设计哲学

Express的核心philosophy是"少即是多",提供基础功能而不限制开发者的选择。它建立在Node.js的HTTP模块之上,通过添加路由、中间件和错误处理等功能,使Web开发更加高效和便捷。

2. 安装与配置

2.1 前提条件

在开始使用Express之前,确保你已经安装了:

  • Node.js(推荐LTS版本)
  • npm(Node.js包管理器)

2.2 安装Express

首先创建一个新的项目目录,然后初始化npm:

mkdir my-express-app
cd my-express-app
npm init -y

然后安装Express:

npm install express --save

2.3 创建第一个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(`Example app listening at http://localhost:${port}`)
})

运行应用:

node app.js

打开浏览器访问 http://localhost:3000,你将看到 "Hello World!" 消息。

3. 基本概念

3.1 应用程序对象

app 对象是Express的实例,它是整个应用的核心:

const app = express()

应用程序对象负责:

  • 定义路由
  • 配置中间件
  • 设置视图引擎
  • 启动HTTP服务器

3.2 路由

路由决定了应用程序如何响应客户端对特定端点的请求,包含一个URL路径和一个HTTP方法。

基本路由格式:

app.METHOD(PATH, HANDLER)

其中:

  • app 是Express实例
  • METHOD 是HTTP请求方法(get, post, put, delete等)
  • PATH 是服务器上的路径
  • HANDLER 是当路由匹配时执行的函数

3.3 请求和响应对象

路由处理函数接收两个主要对象:

  • req (请求对象):包含客户端请求的信息
  • res (响应对象):用于向客户端发送响应

常用的请求对象属性:

  • req.url:请求URL
  • req.method:请求方法
  • req.headers:请求头
  • req.body:请求体数据(需要body-parser中间件)
  • req.params:URL参数
  • req.query:查询字符串参数
  • req.cookies:Cookie(需要cookie-parser中间件)

常用的响应对象方法:

  • res.send():发送响应
  • res.json():发送JSON响应
  • res.status():设置HTTP状态码
  • res.setHeader():设置响应头
  • res.redirect():重定向
  • res.render():渲染视图模板

4. 中间件机制

Express.js的核心是中间件(Middleware)机制:

  • 中间件是一个函数,可以访问请求对象(req)、响应对象(res)和应用的请求-响应循环中的下一个中间件函数(next)
  • 中间件可以执行任何操作,修改请求和响应对象,结束请求-响应循环,或调用下一个中间件
  • 中间件按照定义的顺序执行

4.1 中间件类型

4.1.1 应用级中间件

绑定到应用程序实例的中间件:

app.use((req, res, next) => {
console.log('Time:', Date.now())
next()
})

4.1.2 路由级中间件

绑定到路由对象的中间件:

const router = express.Router()

router.use((req, res, next) => {
console.log('Time:', Date.now())
next()
})

4.1.3 错误处理中间件

处理请求过程中发生的错误的中间件:

app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
})

4.1.4 内置中间件

Express.js提供的内置中间件:

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

4.1.5 第三方中间件

通过NPM安装的中间件:

const cookieParser = require('cookie-parser');
app.use(cookieParser());

4.2 执行流程

请求处理的典型流程:

请求 -> 应用级中间件 -> 路由级中间件 -> 路由处理函数 -> 响应
| |
v v
错误处理中间件 <- 错误处理中间件

5. 路由系统

5.1 路由原理

Express.js的路由系统基于路径和HTTP方法进行匹配:

  1. 当请求到达服务器时,Express会根据请求的URL和方法查找匹配的路由处理函数
  2. 路由可以包含路径参数、查询字符串等
  3. 路由处理函数负责生成响应

5.2 路由匹配规则

  • 精确路径匹配:app.get('/users', ...) 匹配 /users
  • 路径参数:app.get('/users/:id', ...) 匹配 /users/123
  • 通配符:app.get('/users/*', ...) 匹配 /users/ 下的所有路径
  • 正则表达式:app.get(/^/users/d+$/, ...) 匹配 /users/ 后跟随数字的路径

5.3 基本路由示例

// 处理GET请求
app.get('/', (req, res) => {
res.send('Hello World!')
})

// 处理POST请求
app.post('/users', (req, res) => {
res.status(201).send('User created')
})

// 处理带参数的请求
app.get('/users/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`)
})

// 处理所有HTTP方法
app.all('/secret', (req, res, next) => {
console.log('Accessing the secret section...')
next() // 传递控制权给下一个处理函数
})

6. 静态文件服务

Express提供了内置的中间件来提供静态文件,如图片、CSS文件和JavaScript文件:

app.use(express.static('public'))

这会使public目录中的文件可以通过URL直接访问。例如,如果public目录中有一个images子目录,其中包含一个logo.png文件,那么可以通过http://localhost:3000/images/logo.png访问该文件。

你也可以为静态文件服务指定前缀:

app.use('/static', express.static('public'))

这样,文件将通过http://localhost:3000/static/images/logo.png访问。

7. 核心对象详解

7.1 请求对象(req)详解

请求对象包含HTTP请求的所有信息:

  • req.url:请求URL
  • req.method:请求方法
  • req.headers:请求头
  • req.body:请求体(需要body-parser中间件)
  • req.params:路径参数
  • req.query:查询字符串参数
  • req.cookies:Cookie(需要cookie-parser中间件)
  • req.ip:客户端IP地址
  • req.originalUrl:原始请求URL
  • req.path:请求路径
  • req.hostname:主机名
  • req.protocol:协议(http或https)

7.2 响应对象(res)详解

响应对象用于发送HTTP响应:

  • res.send():发送响应
  • res.json():发送JSON响应
  • res.status():设置HTTP状态码
  • res.setHeader():设置响应头
  • res.redirect():重定向
  • res.render():渲染模板(需要模板引擎)
  • res.sendFile():发送文件
  • res.download():触发文件下载
  • res.type():设置Content-Type
  • res.append():添加响应头
  • res.cookie():设置Cookie
  • res.clearCookie():清除Cookie

8. 实用示例

8.1 基本Express应用

// app.js
const express = require('express');
const app = express();
const port = 3000;

// 应用级中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

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

app.get('/users', (req, res) => {
// 模拟用户数据
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
];
res.json(users);
});

app.get('/users/:id', (req, res) => {
const userId = req.params.id;
// 模拟根据ID查询用户
res.json({ id: userId, name: `User ${userId}` });
});

app.post('/users', (req, res) => {
const newUser = req.body;
// 模拟创建用户
res.status(201).json({ id: 3, ...newUser });
});

// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});

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

8.2 路由模块化

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

// 路由级中间件
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});

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

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

module.exports = router;

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

8.3 中间件示例

// 日志中间件
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
};

// 错误处理中间件
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
};

app.use(logger);
// 其他路由...
app.use(errorHandler);

9. 练习项目

创建一个简单的Express应用,包含以下功能:

  1. 提供静态文件服务
  2. 实现几个基本路由
  3. 添加简单的中间件

10. 最佳实践

  1. 使用环境变量:存储配置信息,如端口号、数据库连接字符串等
  2. 模块化路由:将路由拆分为多个文件,提高可维护性
  3. 错误处理:使用错误处理中间件捕获和处理所有错误
  4. 日志记录:记录请求和响应信息,便于调试和监控
  5. 输入验证:验证所有用户输入,防止注入攻击
  6. 使用async/await:避免回调地狱
  7. 限制请求速率:防止DoS攻击
  8. 设置安全头部:使用helmet中间件增强安全性
  9. 代码风格一致:使用ESLint和Prettier保持代码风格一致
  10. 测试:编写单元测试和集成测试

11. 专业解决方案

11.1 路由模块化

  • 使用express.Router()创建模块化路由
  • 将不同功能的路由分离到不同文件中

11.2 身份验证

  • passport.js:灵活的身份验证中间件
  • jsonwebtoken:基于JWT的身份验证

11.3 数据库集成

  • mongoose:MongoDB的ODM
  • sequelize:ORM框架,支持多种关系型数据库

11.4 视图引擎

  • ejs:简单的模板引擎
  • pug:优雅的模板引擎

11.5 测试

  • mocha:JavaScript测试框架
  • supertest:HTTP断言库,用于测试Express应用

11.6 性能优化

  • 静态文件缓存
  • 压缩响应数据
  • 路由懒加载
  • 使用CDN分发静态资源

12. 下一步学习

现在你已经了解了Express的基础知识,接下来可以深入学习:

  • 路由系统详解
  • 中间件高级应用
  • 错误处理机制
  • 模板引擎使用
  • 数据库集成
  • Express安全最佳实践

继续阅读后续章节,掌握更多Express高级功能!