中间件概念 #
一、什么是中间件? #
1.1 定义 #
中间件(Middleware)是Express的核心概念。中间件函数是可以访问请求对象(req)、响应对象(res)和应用程序请求-响应循环中下一个中间件函数的函数。
1.2 中间件函数结构 #
javascript
function middleware(req, res, next) {
}
| 参数 | 说明 |
|---|---|
| req | 请求对象 |
| res | 响应对象 |
| next | 调用下一个中间件的函数 |
1.3 基本示例 #
javascript
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('这是一个中间件');
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(3000);
二、中间件工作原理 #
2.1 请求处理流程 #
text
请求 → 中间件1 → 中间件2 → 中间件3 → 路由处理 → 响应
每个中间件可以:
- 执行任何代码
- 修改请求和响应对象
- 结束请求-响应循环
- 调用下一个中间件
2.2 执行顺序 #
javascript
app.use((req, res, next) => {
console.log('中间件1 - 开始');
next();
console.log('中间件1 - 结束');
});
app.use((req, res, next) => {
console.log('中间件2');
next();
});
app.get('/', (req, res) => {
console.log('路由处理');
res.send('Hello');
});
输出顺序:
text
中间件1 - 开始
中间件2
路由处理
中间件1 - 结束
2.3 next()的作用 #
javascript
app.use((req, res, next) => {
console.log('中间件A');
next();
console.log('中间件A - next之后');
});
app.use((req, res, next) => {
console.log('中间件B');
next();
});
app.get('/', (req, res) => {
res.send('Hello');
});
重要: next() 调用后,当前中间件代码会暂停,等待后续中间件执行完毕后再继续执行。
2.4 不调用next() #
javascript
app.use((req, res, next) => {
res.send('请求在这里被拦截');
});
app.get('/', (req, res) => {
res.send('这段代码永远不会执行');
});
如果不调用 next(),请求-响应循环会在此终止。
三、中间件类型 #
3.1 应用级中间件 #
使用 app.use() 或 app.METHOD() 绑定到app实例:
javascript
app.use((req, res, next) => {
console.log('应用级中间件');
next();
});
app.get('/users', (req, res, next) => {
console.log('路由特定中间件');
next();
}, (req, res) => {
res.json({ users: [] });
});
3.2 路由级中间件 #
使用 router.use() 绑定到router实例:
javascript
const express = require('express');
const router = express.Router();
router.use((req, res, next) => {
console.log('路由级中间件');
next();
});
router.get('/users', (req, res) => {
res.json({ users: [] });
});
module.exports = router;
3.3 错误处理中间件 #
有四个参数的中间件:
javascript
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '服务器错误' });
});
3.4 内置中间件 #
Express内置的中间件:
javascript
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
3.5 第三方中间件 #
javascript
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');
app.use(morgan('dev'));
app.use(cors());
app.use(helmet());
四、中间件挂载方式 #
4.1 全局挂载 #
javascript
app.use((req, res, next) => {
console.log('所有请求都会经过这里');
next();
});
4.2 路径挂载 #
javascript
app.use('/api', (req, res, next) => {
console.log('只处理/api开头的请求');
next();
});
4.3 路由特定中间件 #
javascript
app.get('/users',
(req, res, next) => {
console.log('中间件1');
next();
},
(req, res, next) => {
console.log('中间件2');
next();
},
(req, res) => {
res.json({ users: [] });
}
);
4.4 中间件数组 #
javascript
const middleware1 = (req, res, next) => {
console.log('中间件1');
next();
};
const middleware2 = (req, res, next) => {
console.log('中间件2');
next();
};
app.get('/protected', [middleware1, middleware2], (req, res) => {
res.send('受保护的内容');
});
五、常见中间件模式 #
5.1 日志记录 #
javascript
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
});
next();
});
5.2 请求计时 #
javascript
app.use((req, res, next) => {
req.startTime = Date.now();
next();
});
app.get('/', (req, res) => {
const duration = Date.now() - req.startTime;
res.send(`请求处理时间: ${duration}ms`);
});
5.3 请求ID #
javascript
const uuid = require('uuid');
app.use((req, res, next) => {
req.id = uuid.v4();
res.set('X-Request-Id', req.id);
next();
});
5.4 认证检查 #
javascript
const authMiddleware = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).json({ error: '未授权' });
}
try {
const decoded = verifyToken(token);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: '无效的token' });
}
};
app.get('/protected', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
5.5 权限检查 #
javascript
const checkRole = (...roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: '未登录' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: '无权限' });
}
next();
};
};
app.delete('/users/:id', authMiddleware, checkRole('admin'), (req, res) => {
res.json({ message: '删除成功' });
});
5.6 数据验证 #
javascript
const validateUser = (req, res, next) => {
const { name, email, password } = req.body;
const errors = [];
if (!name) errors.push('用户名不能为空');
if (!email) errors.push('邮箱不能为空');
if (!password || password.length < 6) errors.push('密码至少6个字符');
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
app.post('/users', validateUser, (req, res) => {
res.status(201).json(req.body);
});
5.7 请求限流 #
javascript
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: '请求过于频繁,请稍后再试' }
});
app.use('/api', limiter);
六、中间件执行顺序 #
6.1 顺序很重要 #
javascript
app.use(express.json());
app.use((req, res, next) => {
console.log(req.body);
next();
});
app.post('/data', (req, res) => {
res.json(req.body);
});
如果 express.json() 放在后面,req.body 会是 undefined。
6.2 错误处理中间件位置 #
javascript
app.get('/', (req, res) => {
res.send('Hello');
});
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
错误处理中间件必须放在所有路由之后。
6.3 404处理 #
javascript
app.get('/', (req, res) => {
res.send('Hello');
});
app.use((req, res) => {
res.status(404).json({ error: '路由未找到' });
});
404中间件放在所有路由之后,错误处理中间件之前。
七、中间件最佳实践 #
7.1 模块化中间件 #
middlewares/logger.js:
javascript
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
};
module.exports = logger;
使用:
javascript
const logger = require('./middlewares/logger');
app.use(logger);
7.2 中间件工厂 #
javascript
const createLogger = (options = {}) => {
return (req, res, next) => {
if (options.logBody) {
console.log('Body:', req.body);
}
console.log(`${req.method} ${req.url}`);
next();
};
};
app.use(createLogger({ logBody: true }));
7.3 异步中间件处理 #
javascript
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/users', asyncHandler(async (req, res) => {
const users = await User.find();
res.json(users);
}));
7.4 条件中间件 #
javascript
const conditionalMiddleware = (condition, middleware) => {
return (req, res, next) => {
if (condition(req)) {
return middleware(req, res, next);
}
next();
};
};
app.use(conditionalMiddleware(
req => req.path.startsWith('/api'),
(req, res, next) => {
console.log('API请求');
next();
}
));
八、中间件调试 #
8.1 调试中间件链 #
javascript
app.use((req, res, next) => {
console.log('1. 第一个中间件');
next();
});
app.use((req, res, next) => {
console.log('2. 第二个中间件');
next();
});
app.get('/', (req, res, next) => {
console.log('3. 路由处理');
next();
});
app.use((req, res) => {
console.log('4. 最后的处理');
res.send('Done');
});
8.2 使用debug模块 #
bash
npm install debug
javascript
const debug = require('debug')('app:middleware');
app.use((req, res, next) => {
debug('请求: %s %s', req.method, req.url);
next();
});
运行:
bash
DEBUG=app:* node app.js
九、总结 #
中间件核心概念:
| 概念 | 说明 |
|---|---|
| 定义 | 访问req、res、next的函数 |
| next() | 调用下一个中间件 |
| 执行顺序 | 按定义顺序执行 |
| 类型 | 应用级、路由级、错误处理、内置、第三方 |
| 挂载 | app.use()、router.use() |
下一步,让我们深入学习应用级中间件!
最后更新:2026-03-28