中间件的基本概念与工作原理
中间件是Express框架的核心概念之一,用于处理HTTP请求和响应的过程中插入自定义逻辑。它通过链式调用实现请求的预处理、后处理或拦截,为Web应用提供灵活的扩展能力。
中间件的定义与作用
中间件本质上是具有特定签名的函数,接收三个参数:req
(请求对象)、res
(响应对象)和next
(回调函数)。其典型结构如下:
function middleware(req, res, next) {
// 处理逻辑
next(); // 传递给下一个中间件
}
作用主要体现在:
- 修改请求或响应对象(如添加头部信息)
- 终止请求-响应周期(如权限验证失败时返回403)
- 调用下一个中间件(通过
next()
)
中间件的工作流程
Express应用的请求处理过程可以看作是一个中间件管道。当HTTP请求到达时,会按照中间件注册顺序依次执行:
- 请求进入第一个中间件
- 中间件处理完毕后调用
next()
- 控制权移交至下一个中间件
- 重复上述过程直到响应被发送
示例流程:
app.use((req, res, next) => {
console.log('First middleware');
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
app.get('/', (req, res) => {
res.send('Hello World');
});
请求/
时将依次输出"First middleware"和"Second middleware"。
中间件的类型
应用级中间件
通过app.use()
或app.METHOD()
绑定到应用实例:
// 对所有请求生效
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// 只对GET /user生效
app.get('/user', (req, res, next) => {
res.send('USER');
});
路由级中间件
作用于特定路由路径:
const userRouter = express.Router();
userRouter.use((req, res, next) => {
console.log('User route middleware');
next();
});
app.use('/users', userRouter);
错误处理中间件
接收四个参数(err, req, res, next):
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
内置中间件
Express提供的开箱即用中间件:
express.json()
:解析JSON请求体express.urlencoded()
:解析URL编码请求体express.static()
:托管静态文件
app.use(express.json());
app.use(express.static('public'));
第三方中间件
社区提供的功能扩展:
const morgan = require('morgan');
app.use(morgan('dev')); // 日志记录
中间件的执行顺序
中间件的执行顺序严格依赖注册顺序。以下示例展示顺序的重要性:
// 这个中间件不会被执行
app.use((req, res, next) => {
console.log('This will never run');
next();
});
// 因为前一个中间件终止了流程
app.use((req, res, next) => {
res.send('Early response');
});
// 正确顺序应该是:
app.use((req, res, next) => {
const isValid = checkAuth(req);
if (!isValid) return res.status(401).end();
next();
});
app.get('/protected', (req, res) => {
res.send('Protected content');
});
中间件的组合与模块化
多个中间件可以组合使用:
function logOriginalUrl(req, res, next) {
console.log('Request URL:', req.originalUrl);
next();
}
function logMethod(req, res, next) {
console.log('Request Type:', req.method);
next();
}
app.get('/user', [logOriginalUrl, logMethod], (req, res) => {
res.send('User Info');
});
模块化组织示例:
// logger.js
module.exports = function(req, res, next) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
};
// app.js
const logger = require('./logger');
app.use(logger);
中间件的错误处理机制
错误处理中间件需要特殊定义:
// 同步错误会自动捕获
app.get('/error', (req, res) => {
throw new Error('BROKEN');
});
// 异步错误需要显式传递
app.get('/async-error', (req, res, next) => {
fs.readFile('/nonexistent', (err, data) => {
if (err) next(err); // 必须手动传递错误
else res.send(data);
});
});
// 错误处理中间件
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
中间件的性能优化
- 过滤不必要的中间件执行:
app.use('/api', (req, res, next) => {
// 只对/api路径生效
next();
});
- 使用条件中间件:
const isAdmin = (req, res, next) => {
if (req.user.role === 'admin') return next();
res.status(403).end();
};
app.get('/admin', isAdmin, (req, res) => {
res.send('Admin panel');
});
- 中间件缓存:
const cache = {};
app.use('/data', (req, res, next) => {
const key = req.originalUrl;
if (cache[key]) return res.json(cache[key]);
// 模拟耗时操作
setTimeout(() => {
const data = { value: Math.random() };
cache[key] = data;
res.json(data);
}, 1000);
});
中间件的实际应用场景
身份验证
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) return res.status(401).send('Unauthorized');
try {
req.user = verifyToken(token);
next();
} catch (err) {
res.status(403).send('Invalid token');
}
};
app.get('/profile', authenticate, (req, res) => {
res.json(req.user);
});
请求数据处理
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post('/submit', (req, res) => {
console.log(req.body); // 自动解析的请求体
res.send('Data received');
});
响应时间监控
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
});
CORS处理
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
next();
});
高级中间件模式
中间件工厂
返回中间件的函数:
function createLogger(format) {
return function(req, res, next) {
console.log(`[${format}] ${req.method} ${req.url}`);
next();
};
}
app.use(createLogger('short'));
可配置中间件
function configurableMiddleware(options = {}) {
return function(req, res, next) {
if (options.logRequest) {
console.log(`${req.method} ${req.url}`);
}
if (options.addTimestamp) {
req.timestamp = Date.now();
}
next();
};
}
app.use(configurableMiddleware({
logRequest: true,
addTimestamp: true
}));
中间件组合工具
const compose = require('koa-compose'); // 也可以自己实现
const middleware1 = (req, res, next) => {
console.log('Middleware 1');
next();
};
const middleware2 = (req, res, next) => {
console.log('Middleware 2');
next();
};
app.use(compose([middleware1, middleware2]));
中间件的测试方法
使用supertest测试中间件:
const request = require('supertest');
const app = require('../app');
describe('Auth Middleware', () => {
it('should block unauthorized access', async () => {
const res = await request(app)
.get('/protected')
.expect(401);
});
it('should allow authorized access', async () => {
const res = await request(app)
.get('/protected')
.set('Authorization', 'valid-token')
.expect(200);
});
});
中间件的调试技巧
- 使用调试占位符:
app.use((req, res, next) => {
console.log('Request reached middleware');
next();
console.log('Request left middleware');
});
- 检查中间件堆栈:
app.use((req, res, next) => {
console.log('Current stack:', this._router.stack);
next();
});
- 使用调试工具:
const debug = require('debug')('app:middleware');
app.use((req, res, next) => {
debug('Request headers:', req.headers);
next();
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:性能优化与缓存策略
下一篇:内置中间件与第三方中间件