中间件是 Express 的核心概念之一,它们可以拦截请求和响应,以便在请求到达路由处理函数之前或在响应发送给客户端之前执行某些操作。在本章中,我们将深入探讨中间件的类型、用法以及如何编写自定义中间件。
1 中间件概述
中间件是具有访问请求对象 (req
)、响应对象 (res
) 和下一个中间件函数 (next
) 的函数。中间件函数可以执行以下任务:
- 执行任何代码。
- 修改请求和响应对象。
- 结束请求-响应循环。
- 调用堆栈中的下一个中间件函数。
1.1 应用级中间件
应用级中间件绑定到 app
实例,作用于应用的全局或特定路由。
示例:
const express = require('express');
const app = express();
// 应用级中间件,所有请求都会执行
app.use((req, res, next) => {
console.log('Time:', Date.now());
next(); // 调用下一个中间件
});
// 应用级中间件,特定路径请求才会执行
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
// 路由
app.get('/user/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
// 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
代码详解:
app.use((req, res, next) => {})
:定义应用级中间件。next()
:调用下一个中间件或路由处理函数。
1.2 路由级中间件
路由级中间件绑定到 express.Router
实例,作用于特定路由。
示例:
const router = express.Router();
// 路由级中间件
router.use((req, res, next) => {
console.log('Router-level middleware');
next();
});
// 路由
router.get('/profile', (req, res) => {
res.send('User profile page');
});
router.get('/settings', (req, res) => {
res.send('User settings page');
});
// 将路由器挂载到应用中
app.use('/user', router);
代码详解:
router.use((req, res, next) => {})
:定义路由级中间件。app.use('/user', router)
:将路由器挂载到应用中。
1.3 错误处理中间件
错误处理中间件是带有四个参数的函数,用于处理传递过来的错误。
示例:
// 普通中间件
app.get('/error', (req, res, next) => {
const err = new Error('Something went wrong');
next(err); // 将错误传递给错误处理中间件
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Internal Server Error');
});
代码详解:
- 错误处理中间件:带有四个参数
err
、req
、res
和next
。
1.4 内置中间件
Express 提供了一些内置的中间件,如 express.json()
和 express.urlencoded()
。
示例:
// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码的请求体
app.use(express.urlencoded({ extended: true }));
代码详解:
express.json()
:解析 JSON 请求体。express.urlencoded({ extended: true })
:解析 URL 编码的请求体。
1.5 第三方中间件
可以使用许多第三方中间件来扩展 Express 的功能,如 morgan
、cors
等。
示例:
const morgan = require('morgan');
// 使用 morgan 中间件记录 HTTP 请求日志
app.use(morgan('combined'));
代码详解:
morgan('combined')
:使用morgan
记录 HTTP 请求日志。
2 编写自定义中间件
自定义中间件是你自己编写的用于特定需求的中间件。自定义中间件可以在应用级或路由级使用。
2.1 简单的自定义中间件
示例:
// 自定义中间件,记录请求方法和路径
app.use((req, res, next) => {
console.log(`Request Method: ${req.method}, Request Path: ${req.path}`);
next(); // 调用下一个中间件或路由处理函数
});
代码详解:
console.log()
:记录请求方法和路径。
2.2 带参数的自定义中间件
可以编写带参数的自定义中间件,以便灵活配置。
示例:
// 带参数的自定义中间件工厂函数
function requestLogger(options) {
return (req, res, next) => {
if (options.logMethod) {
console.log(`Request Method: ${req.method}`);
}
if (options.logPath) {
console.log(`Request Path: ${req.path}`);
}
next();
};
}
// 使用带参数的自定义中间件
app.use(requestLogger({ logMethod: true, logPath: true }));
代码详解:
requestLogger(options)
:自定义中间件工厂函数,根据传入的选项参数配置中间件行为。
2.3 异步自定义中间件
自定义中间件可以是异步的,用于处理异步操作。
示例:
// 异步自定义中间件,模拟数据库查询
app.use(async (req, res, next) => {
try {
const data = await fetchDataFromDatabase();
req.data = data; // 将数据存储在请求对象中
next(); // 调用下一个中间件或路由处理函数
} catch (error) {
next(error); // 将错误传递给错误处理中间件
}
});
async function fetchDataFromDatabase() {
// 模拟异步数据库查询
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ message: 'Data fetched from database' });
}, 1000);
});
}
代码详解:
async (req, res, next)
:异步自定义中间件。await fetchDataFromDatabase()
:等待异步操作完成。next(error)
:将错误传递给错误处理中间件。