一、中间件本质与作用
-
定义
>中间件是能访问请求对象(
req
)、响应对象(res
)和下一个中间件函数(next
)的函数,用于预处理请求或后处理响应。
>类比“加工流水线”,请求从进入服务器到返回响应的过程中会经过多个中间件处。 -
核心功能
>执行任意代码(如日志记录、数据验证)
>修改req
/res
对象(如添加自定义属性)
>终止请求-响应循环(如res.send()
直接返回)或调用next()
传递控制权
二、中间件类型与用法
1.应用级中间件
>通过app.use()
全局生效,或绑定到特定HTTP方法(如app.get()
)
app.use((req, res, next) => {
console.log('全局中间件');
next();
});
2.路由级中间件
>绑定到express.Router()
实例,仅对特定路由生效
const router = express.Router();
router.use('/api', (req, res, next) => { ... });
3.错误处理中间件
>需包含4个参数(err, req, res, next)
,用于捕获异常
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误');
});
4.内置与第三方中间件
>内置:如express.json()
解析JSON请求体。
>第三方:如body-parser
(现已被Express内置替代)
三、关键注意事项
1.next()
调用
若中间件未终止请求(如未调用res.send()
),必须调用next()
传递控制权,否则请求会挂起
2. 执行顺序
>中间件按注册顺序执行,需合理排列(如日志中间件应优先于路由)
3.错误处理优先级
>错误中间件需定义在最后,确保能捕获所有前置中间件的异常
四、典型应用场景
1.日志记录
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
2.身份验证
app.use('/admin', (req, res, next) => {
if (req.headers.token === 'secret') next();
else res.status(403).send('无权访问');
});
3.静态文件服务
app.use(express.static('public'));
五、重点说下中间件执行顺序
Express 中间件的执行顺序是框架的核心机制之一,理解它对构建可靠应用至关重要。以下是核心规则和实际示例:
核心执行规则
-
注册顺序即执行顺序
>中间件按照app.use()
或路由方法(app.get()
等)的代码书写顺序依次执行
>类似流水线处理,请求依次通过每个中间件 -
>调用next()
控制权传递next()
将控制权交给下一个中间件
>不调用next()
将终止请求处理链 -
响应发送即终止
>任何中间件执行res.send()
,res.json()
等响应方法后,后续中间件不会执行
关键执行流程图示
graph LR
A[请求进入] --> B[中间件1]
B -- next() --> C[中间件2]
C -- next() --> D[路由处理器]
D -- res.send() --> E[响应返回]
C -- 未调用next --> F[流程终止]
典型执行场景示例
场景1:基础顺序控制
app.use((req, res, next) => {
console.log('第一层中间件');
next(); // 必须调用next传递控制权
});
app.use((req, res, next) => {
console.log('第二层中间件');
next();
});
app.get('/', (req, res) => {
res.send('最终响应'); // 终止流程
});
// 访问 '/' 的输出顺序:
// 第一层中间件 → 第二层中间件 → 最终响应
场景2:路由级中间件顺序
// 全局中间件
app.use((req, res, next) => {
console.log('全局中间件');
next();
});
// 路由专属中间件
app.use('/api', (req, res, next) => {
console.log('API路由中间件');
next();
});
// 路由处理器
app.get('/api/data', (req, res) => {
res.json({ data: 'test' });
});
// 访问 '/api/data' 的输出:
// 全局中间件 → API路由中间件 → 路由处理器
场景3:错误处理中间件(必须最后注册)
// 常规中间件
app.get('/error', (req, res, next) => {
next(new Error('测试错误')); // 主动触发错误
});
// 错误处理中间件(4个参数)
app.use((err, req, res, next) => {
console.error('错误处理:', err.message);
res.status(500).send('服务器错误');
});
// 访问 '/error' 的流程:
// 路由处理器 → 错误处理中间件