1. 请求耗时中间件的增强版
问题:原版只能记录到控制台,如何记录到文件?
改进点:
- 使用process.hrtime()是什么?获取更高精度的时间
- 支持将日志写入文件
- 记录更多信息(IP地址、状态码)
- 工厂函数模式使中间件可配置
javascript
const fs = require('fs');
const path = require('path');
// 增强版耗时记录中间件
function createRequestLogger(logFilePath) {
const logStream = fs.createWriteStream(path.join(__dirname, logFilePath), { flags: 'a' });
return (req, res, next) => {
const start = process.hrtime();
const startDate = new Date().toISOString();
res.on('finish', () => {
const duration = process.hrtime(start);
const durationMs = (duration[0] * 1e3 + duration[1] / 1e6).toFixed(3);
const logEntry = `[${startDate}] ${req.ip} ${req.method} ${req.url} ${res.statusCode} ${durationMs}ms\n`;
logStream.write(logEntry);
console.log(logEntry.trim());
});
next();
};
}
// 使用方式
app.use(createRequestLogger('requests.log'));
2. API密钥验证中间件的进阶版
问题:如何支持多种验证方式?
改进点:
- 支持多种认证策略(API Key、JWT、Basic Auth)
- 异步验证支持
- 统一的错误处理
- 可扩展的工厂函数设计
javascript
// 支持多种验证策略的中间件工厂
function createAuthMiddleware(options = {}) {
return async (req, res, next) => {
try {
// 策略1: API Key验证
if (options.apiKey) {
const apiKey = req.headers['x-api-key'] || req.query.apiKey;
if (!apiKey) throw new Error('Missing API key');
if (!options.apiKey.keys.includes(apiKey)) throw new Error('Invalid API key');
req.authType = 'apiKey';
}
// 策略2: JWT验证
if (options.jwt) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) throw new Error('Missing token');
const decoded = await verifyJWT(token, options.jwt.secret);
req.user = decoded;
req.authType = 'jwt';
}
// 策略3: 基本认证
if (options.basicAuth) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
throw new Error('Missing basic auth');
}
const credentials = Buffer.from(authHeader.split(' ')[1], 'base64').toString();
const [username, password] = credentials.split(':');
if (username !== options.basicAuth.user || password !== options.basicAuth.pass) {
throw new Error('Invalid credentials');
}
req.authType = 'basic';
}
next();
} catch (error) {
res.status(401).json({
error: 'Authentication failed',
message: error.message
});
}
};
}
// 使用示例
app.use(createAuthMiddleware({
apiKey: {
keys: ['123-abc', '456-def']
},
jwt: {
secret: 'my-secret-key'
}
}));
3. 组合中间件的模式进阶
问题:如何更灵活地组合中间件?
改进点:
- 实现了类似Koa的中间件组合机制
- 添加条件中间件支持
- 更灵活的路径匹配
- 错误处理集成
javascript
// 中间件组合工具函数
function composeMiddlewares(...middlewares) {
return (req, res, next) => {
const dispatch = (i) => {
if (i >= middlewares.length) return next();
const middleware = middlewares[i];
try {
return middleware(req, res, () => dispatch(i + 1));
} catch (err) {
return next(err);
}
};
return dispatch(0);
};
}
// 条件中间件
function conditionalMiddleware(condition, middleware) {
return (req, res, next) => {
if (condition(req)) {
return middleware(req, res, next);
}
next();
};
}
// 使用示例
const isAdminRoute = req => req.path.startsWith('/admin');
const isApiRoute = req => req.path.startsWith('/api');
app.use(composeMiddlewares(
requestLogger,
conditionalMiddleware(
isApiRoute,
apiKeyValidator
),
conditionalMiddleware(
isAdminRoute,
adminCheck
)
));
// 等价于:
// app.use(requestLogger);
// app.use('/api', apiKeyValidator);
// app.use('/admin', adminCheck);
实用中间件模式
1. 数据转换中间件
javascript
function transformRequestBody(fields) {
return (req, res, next) => {
if (req.body) {
for (const [field, transform] of Object.entries(fields)) {
if (req.body[field] !== undefined) {
req.body[field] = transform(req.body[field]);
}
}
}
next();
};
}
// 使用示例
app.use(express.json());
app.use(transformRequestBody({
email: v => v.toLowerCase().trim(),
age: v => parseInt(v, 10),
isActive: v => v === 'true'
}));
2. 响应包装中间件
javascript
function responseWrapper() {
return (req, res, next) => {
const originalSend = res.send;
res.send = function(body) {
if (res.statusCode >= 400) {
originalSend.call(this, {
success: false,
error: body
});
} else {
originalSend.call(this, {
success: true,
data: body
});
}
};
next();
};
}
3. 请求限流中间件
javascript
function rateLimiter({ windowMs, maxRequests }) {
const requests = new Map();
setInterval(() => {
requests.clear();
}, windowMs);
return (req, res, next) => {
const ip = req.ip;
const count = requests.get(ip) || 0;
if (count >= maxRequests) {
return res.status(429).send('Too many requests');
}
requests.set(ip, count + 1);
next();
};
}
最佳实践建议
- 单一职责:每个中间件只做一件事
- 可重用性:设计为可配置的工厂函数
- 错误处理:始终捕获同步和异步错误
- 性能考虑:避免在中间件中进行阻塞操作
- 文档注释:清晰说明中间件的用途和参数。
本节就到这里,下节将继续深入讨论示例。