中间件机制与执行流程
中间件机制与执行流程
Express的中间件机制是其核心特性之一,允许开发者在请求和响应周期中插入自定义处理逻辑。这种机制通过函数堆栈的形式组织,每个中间件函数可以访问请求对象、响应对象以及下一个中间件函数的引用。
中间件的基本结构
一个典型的Express中间件函数包含三个参数:req
、res
和next
。当中间件完成工作后,它可以调用next()
将控制权传递给下一个中间件,或者直接结束请求响应周期。
const express = require('express');
const app = express();
// 最简单的日志中间件
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// 路由处理中间件
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000);
中间件类型
Express支持多种类型的中间件,每种类型在调用时机和作用域上有所不同:
- 应用级中间件:通过
app.use()
或app.METHOD()
绑定到应用实例 - 路由级中间件:通过
router.use()
绑定到路由实例 - 错误处理中间件:接收四个参数
(err, req, res, next)
- 内置中间件:如
express.static
- 第三方中间件:如
body-parser
// 应用级中间件示例
app.use(express.json()); // 解析JSON请求体
app.use(express.urlencoded({ extended: true })); // 解析URL编码请求体
// 错误处理中间件示例
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
中间件执行顺序
中间件的执行顺序至关重要,Express按照中间件注册的顺序依次执行它们。这个顺序决定了请求如何被处理和响应如何被构建。
app.use((req, res, next) => {
console.log('First middleware');
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
app.get('/user', (req, res) => {
res.send('User profile');
});
在上面的例子中,访问/user
路径时,控制台会依次输出"First middleware"和"Second middleware"。
中间件执行流程控制
next()
函数是控制中间件流程的关键。它可以实现以下功能:
- 传递控制权给下一个中间件
- 跳过剩余中间件
- 将错误传递给错误处理中间件
// 验证令牌的中间件
app.use('/api', (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).send('Unauthorized');
}
next();
});
// 只有令牌验证通过才会执行这个中间件
app.get('/api/data', (req, res) => {
res.json({ data: 'secret' });
});
异步中间件处理
现代Express支持异步中间件函数,可以使用async/await
语法处理异步操作:
app.use(async (req, res, next) => {
try {
const user = await User.findById(req.userId);
req.user = user;
next();
} catch (err) {
next(err);
}
});
中间件组合与重用
Express提供了Router
实例来组织和重用中间件:
const router = express.Router();
// 为所有路由添加中间件
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// 定义路由
router.get('/user/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});
// 将路由挂载到应用
app.use('/api', router);
中间件的错误处理
错误处理中间件需要四个参数,并且通常放在其他中间件之后:
app.get('/error', (req, res, next) => {
const err = new Error('Something went wrong');
next(err); // 传递给错误处理中间件
});
// 错误处理中间件
app.use((err, req, res, next) => {
res.status(500).json({
error: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
});
中间件的性能考虑
中间件虽然强大,但不当使用会影响性能:
- 避免在中间件中进行不必要的计算
- 尽早终止无效请求
- 合理使用缓存
// 性能优化示例:缓存静态资源
const cacheMiddleware = (req, res, next) => {
res.set('Cache-Control', 'public, max-age=3600');
next();
};
app.use('/static', cacheMiddleware, express.static('public'));
中间件的实际应用场景
- 请求日志:记录每个请求的详细信息
- 身份验证:验证用户凭证
- 数据验证:检查请求数据的有效性
- 响应格式化:统一API响应格式
- 速率限制:防止滥用API
// 请求日志中间件
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`);
});
next();
});
// 速率限制中间件
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 每个IP限制100个请求
});
app.use('/api/', limiter);
中间件的测试与调试
测试中间件需要模拟请求和响应对象:
// 测试身份验证中间件
test('auth middleware rejects requests without token', () => {
const req = {
headers: {}
};
const res = {
status: jest.fn().mockReturnThis(),
send: jest.fn()
};
const next = jest.fn();
authMiddleware(req, res, next);
expect(res.status).toHaveBeenCalledWith(401);
expect(res.send).toHaveBeenCalledWith('Unauthorized');
expect(next).not.toHaveBeenCalled();
});
中间件的进阶模式
- 中间件工厂:创建可配置的中间件
- 中间件组合:将多个中间件合并为一个
- 条件中间件:根据条件应用中间件
// 中间件工厂示例:创建可配置的日志中间件
function createLoggerMiddleware(format) {
return (req, res, next) => {
const message = format
.replace(':method', req.method)
.replace(':url', req.url);
console.log(message);
next();
};
}
app.use(createLoggerMiddleware(':method :url'));
// 条件中间件示例
function conditionalMiddleware(condition, middleware) {
return (req, res, next) => {
if (condition(req)) {
middleware(req, res, next);
} else {
next();
}
};
}
app.use(conditionalMiddleware(
req => req.path.startsWith('/api'),
bodyParser.json()
));
中间件与Express 5的新特性
Express 5引入了一些改进中间件处理的新特性:
- 更好的异步错误处理
- 改进的路由参数处理
- 更灵活的响应方法
// Express 5中的改进错误处理
app.get('/user/:id', async (req, res, next) => {
try {
const user = await User.find(req.params.id);
if (!user) throw new Error('User not found');
res.json(user);
} catch (err) {
next(err); // 自动传递给错误处理中间件
}
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:路由系统与URL处理
下一篇:请求与响应对象详解