1. 請求耗時中間件的增強版
問題:原版只能記錄到控制臺,如何記錄到文件?
改進點:
- 使用
process.hrtime()是什么?
獲取更高精度的時間 - 支持將日志寫入文件
- 記錄更多信息(IP地址、狀態碼)
- 工廠函數模式使中間件可配置
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)
- 異步驗證支持
- 統一的錯誤處理
- 可擴展的工廠函數設計
// 支持多種驗證策略的中間件工廠
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的中間件組合機制
- 添加條件中間件支持
- 更靈活的路徑匹配
- 錯誤處理集成
// 中間件組合工具函數
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. 數據轉換中間件
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. 響應包裝中間件
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. 請求限流中間件
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();};
}
最佳實踐建議
- ?單一職責:每個中間件只做一件事
- ?可重用性:設計為可配置的工廠函數
- ?錯誤處理:始終捕獲同步和異步錯誤
- ?性能考慮:避免在中間件中進行阻塞操作
- ?文檔注釋:清晰說明中間件的用途和參數。
本節就到這里,下節將繼續深入討論示例。
Express中間件(Middleware)詳解:從零開始掌握(3)-CSDN博客