1. 中间件是什么?
想象中间件就像一个"加工流水线",请求(Request)从进入服务器到返回响应(Response)的过程中,会经过一个个"工作站"进行处理。
简单定义:中间件是能够访问请求对象(req)、响应对象(res)和下一个中间件函数(next)的函数。
2. 基本结构
一个最基本的中间件函数长这样:
function myMiddleware(req, res, next) {
// 对req或res做一些处理
console.log('这是中间件');
next(); // 告诉Express转到下一个中间件
}
3. 中间件的工作原理
Express应用的请求处理流程就像这样:
请求 →
中间件1 →
中间件2 →
... →
路由处理器 →
响应
每个中间件可以:
- 执行任何代码
- 修改请求和响应对象
- 结束请求-响应循环
- 调用下一个中间件
类型:
类型 | 示例 | 特点 |
---|---|---|
应用级 | app.use() | 对所有请求生效 |
路由级 | router.use() | 仅对特定路由生效 |
错误处理 | app.use((err, req, res, next)) | 必须4个参数 |
内置 | express.json() | Express提供 |
第三方 | body-parser | 社区贡献 |
关键点:
- 每个中间件通过调用
next()
将控制权传递给下一个 - 如果不调用
next()
,请求处理会终止 - 错误处理中间件需要4个参数:
(err, req, res, next)
4. 实际例子:记录请求日志
const express = require('express');
const app = express();
// 自定义日志中间件
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next(); // 必须调用next()才能继续
});
app.get('/', (req, res) => {
res.send('首页');
});
app.listen(3000);
看下日志:
5. 中间件类型
应用级中间件
// 对所有路由生效
app.use(myMiddleware);
// 对特定路由生效
app.use('/admin', adminMiddleware);
路由级中间件
router.use(middlewareFunction);
内置中间件
app.use(express.json()); // 解析JSON请求体
app.use(express.urlencoded({ extended: true })); // 解析表单数据
app.use(express.static('public')); // 静态文件服务
错误处理中间件
// 注意有4个参数
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('出错了!');
});
第三方中间件
const morgan = require('morgan');
app.use(morgan('dev')); // 日志中间件
6. 中间件执行顺序练习
app.use((req, res, next) => {
console.log('第一');
next();
});
app.use((req, res, next) => {
console.log('第二');
next();
});
app.get('/', (req, res) => {
console.log('最后');
res.send('完成');
});
7. 重要原则
- 顺序很重要:中间件按添加顺序执行
- 必须调用next():除非你想终止流程
- 错误处理中间件:必须放在最后
- 可以修改req/res:后面的中间件会看到修改
8. 练习
8.1. 记录请求耗时的中间件
const express = require('express');
const app = express();
// 记录请求耗时的中间件
app.use((req, res, next) => {
const startTime = Date.now(); // 记录开始时间
// 在响应结束时计算耗时
res.on('finish', () => {
const duration = Date.now() - startTime;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next(); // 继续处理请求
});
// 测试路由
app.get('/', (req, res) => {
// 模拟耗时操作
setTimeout(() => {
res.send('首页');
}, 300);
});
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
8.2. 记录请求耗时的中间件
// 验证API密钥的中间件
const express = require('express');
const app = express();
// API密钥验证中间件
const apiKeyValidator = (req, res, next) => {
const validApiKeys = ['123-abc', '456-def', '789-ghi'];
const apiKey = req.headers['x-api-key'] || req.query.apiKey;
if (!apiKey) {
return res.status(401).json({ error: '缺少API密钥' });
}
if (!validApiKeys.includes(apiKey)) {
return res.status(403).json({ error: '无效的API密钥' });
}
console.log(`API密钥验证通过: ${apiKey}`);
next(); // 验证通过
};
// 受保护的路由
app.get('/protected', apiKeyValidator, (req, res) => {
res.json({ message: '你已访问受保护的内容' });
});
// 测试方式:
// 1. curl http://localhost:3000/protected?apiKey=123-abc
// 2. curl -H "x-api-key: 123-abc" http://localhost:3000/protected
app.listen(3000);
8.3. 组合多个中间件
const express = require('express');
const app = express();
// 中间件1: 记录请求信息
const requestLogger = (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.ip} ${req.method} ${req.path}`);
next();
};
// 中间件2: 验证用户身份
const userAuth = (req, res, next) => {
// 模拟用户验证
const isAuthenticated = Math.random() > 0.3; // 70%概率通过
if (isAuthenticated) {
req.user = { id: 123, name: '示例用户' }; // 附加用户信息到请求对象
next();
} else {
res.status(401).send('请先登录');
}
};
// 中间件3: 检查管理员权限
const adminCheck = (req, res, next) => {
if (req.user && req.user.isAdmin) {
next();
} else {
res.status(403).send('需要管理员权限');
}
};
// 组合使用多个中间件
app.get('/profile',
requestLogger,
userAuth,
(req, res) => {
res.send(`欢迎, ${req.user.name}`);
}
);
app.get('/admin',
requestLogger,
userAuth,
adminCheck,
(req, res) => {
res.send('管理员面板');
}
);
// 测试路由
app.get('/public', requestLogger, (req, res) => {
res.send('公开内容');
});
app.listen(3000);
本节到这里就结束了,下节将讲解进阶版中间件。
Express中间件(Middleware)详解:从零开始掌握(2)-CSDN博客