您现在的位置是:网站首页 > 自定义中间件的开发方法文章详情
自定义中间件的开发方法
陈川
【
Node.js
】
15961人已围观
7166字
理解中间件的基本概念
Express中间件本质上是处理HTTP请求的函数,能够访问请求对象(req)、响应对象(res)和应用程序的请求-响应周期中的下一个中间件函数(next)。中间件可以执行以下操作:
- 执行任何代码
- 修改请求和响应对象
- 结束请求-响应周期
- 调用堆栈中的下一个中间件
function simpleMiddleware(req, res, next) {
console.log('Request received at', new Date());
next(); // 必须调用next()才能继续执行后续中间件
}
中间件的类型
Express中的中间件主要分为以下几种类型:
- 应用级中间件:使用app.use()或app.METHOD()绑定到应用程序实例
- 路由器级中间件:与应用级中间件类似,但绑定到express.Router()实例
- 错误处理中间件:专门处理错误的中间件,接收四个参数(err, req, res, next)
- 内置中间件:Express自带的中间件,如express.static
- 第三方中间件:社区开发的中间件,如body-parser
创建自定义中间件的基本结构
自定义中间件的基本结构包含三个参数:req、res和next。下面是一个最简单的自定义中间件示例:
const myMiddleware = (req, res, next) => {
// 中间件逻辑
console.log('Custom middleware executed');
// 修改请求对象
req.customProperty = 'Some value';
// 继续处理下一个中间件
next();
};
// 使用中间件
app.use(myMiddleware);
实现请求日志中间件
一个实用的自定义中间件例子是请求日志记录器,它可以记录每个请求的详细信息:
const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} - ${res.statusCode} [${duration}ms]`);
});
next();
};
app.use(requestLogger);
开发认证中间件
认证是Web应用中常见的需求,下面是一个简单的JWT认证中间件:
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
};
// 保护路由
app.get('/protected', authMiddleware, (req, res) => {
res.json({ message: 'Protected content', user: req.user });
});
数据验证中间件
数据验证是另一个常见场景,可以创建专门的验证中间件:
const validateUserInput = (req, res, next) => {
const { username, email, password } = req.body;
const errors = [];
if (!username || username.length < 3) {
errors.push('Username must be at least 3 characters');
}
if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}
if (!password || password.length < 6) {
errors.push('Password must be at least 6 characters');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
app.post('/register', validateUserInput, (req, res) => {
// 处理注册逻辑
});
错误处理中间件
错误处理中间件有四个参数,专门用于捕获和处理错误:
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
const statusCode = err.statusCode || 500;
const message = statusCode === 500 ? 'Something went wrong' : err.message;
res.status(statusCode).json({
error: {
message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
};
// 使用方式 - 放在所有路由之后
app.use(errorHandler);
中间件组合与链式调用
多个中间件可以组合使用,形成处理链:
const logRequest = (req, res, next) => {
console.log(`Incoming request: ${req.method} ${req.url}`);
next();
};
const parseBody = (req, res, next) => {
if (req.body) {
req.parsedBody = JSON.stringify(req.body);
}
next();
};
const respond = (req, res) => {
res.send(`Processed: ${req.parsedBody || 'No body'}`);
};
app.post('/process', logRequest, parseBody, respond);
异步中间件处理
现代JavaScript中,异步操作很常见,中间件也可以处理异步逻辑:
const asyncMiddleware = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
// 使用示例
const fetchUserData = asyncMiddleware(async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new Error('User not found');
}
req.user = user;
next();
});
app.get('/users/:id', fetchUserData, (req, res) => {
res.json(req.user);
});
中间件的配置选项
更灵活的自定义中间件可以接受配置选项:
const createRateLimiter = (options = {}) => {
const {
windowMs = 15 * 60 * 1000, // 15分钟
max = 100, // 每个IP最多100个请求
message = 'Too many requests'
} = options;
const requests = new Map();
return (req, res, next) => {
const ip = req.ip;
const now = Date.now();
if (!requests.has(ip)) {
requests.set(ip, { count: 1, startTime: now });
return next();
}
const record = requests.get(ip);
if (now - record.startTime > windowMs) {
requests.set(ip, { count: 1, startTime: now });
return next();
}
if (record.count >= max) {
return res.status(429).json({ error: message });
}
record.count++;
next();
};
};
// 使用带配置的中间件
app.use(createRateLimiter({ max: 50 }));
中间件的测试方法
测试自定义中间件是确保其可靠性的重要环节:
const testMiddleware = require('supertest');
const express = require('express');
describe('Auth Middleware', () => {
let app;
beforeEach(() => {
app = express();
app.use(express.json());
app.use(authMiddleware);
app.get('/test', (req, res) => res.json({ user: req.user }));
});
it('should reject requests without token', async () => {
const res = await testMiddleware(app).get('/test');
expect(res.statusCode).toBe(401);
});
it('should accept requests with valid token', async () => {
const token = jwt.sign({ id: 1 }, process.env.JWT_SECRET);
const res = await testMiddleware(app)
.get('/test')
.set('Authorization', `Bearer ${token}`);
expect(res.statusCode).toBe(200);
});
});
中间件的性能考虑
开发中间件时需要考虑性能影响:
- 避免阻塞操作:长时间运行的同步操作会阻塞事件循环
- 缓存常用数据:如认证信息、配置数据等
- 精简中间件栈:只使用必要的中间件
- 异步处理:将I/O操作异步化
// 优化后的缓存中间件示例
const createCacheMiddleware = (ttl = 60) => {
const cache = new Map();
return (req, res, next) => {
const key = req.originalUrl;
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl * 1000) {
return res.json(cached.data);
}
const originalSend = res.json;
res.json = (data) => {
cache.set(key, { data, timestamp: Date.now() });
originalSend.call(res, data);
};
next();
};
};
中间件的实际应用场景
自定义中间件在实际项目中有广泛的应用:
- 请求转换:修改请求数据格式
- 响应处理:统一响应格式
- 性能监控:记录请求处理时间
- 流量控制:实现限流、熔断
- AOP编程:实现日志、事务等横切关注点
// 统一响应格式的中间件
const responseFormatter = (req, res, next) => {
const originalJson = res.json;
res.json = (data) => {
originalJson.call(res, {
success: true,
data,
timestamp: new Date().toISOString()
});
};
next();
};
app.use(responseFormatter);
中间件的调试技巧
调试自定义中间件时可以采用以下方法:
- 日志记录:详细记录中间件执行流程
- 断点调试:使用Node.js调试工具
- 单元测试:隔离测试中间件功能
- 错误边界:捕获并处理中间件中的异常
// 带有调试功能的中间件
const debugMiddleware = (namespace) => {
return (req, res, next) => {
const start = Date.now();
console.log(`[${namespace}] Start processing`, req.method, req.url);
res.on('finish', () => {
console.log(
`[${namespace}] Completed in ${Date.now() - start}ms`,
res.statusCode
);
});
next();
};
};
app.use(debugMiddleware('app'));
上一篇: 内置中间件与第三方中间件
下一篇: 中间件的执行顺序与控制