Search K
Appearance
Appearance
📊 SEO元描述:2024年最新Express中间件开发教程,详解内置中间件、第三方中间件、自定义中间件开发。包含完整代码示例,适合Node.js开发者掌握中间件编程技术。
核心关键词:Express中间件开发2024、Node.js中间件编程、Express自定义中间件、中间件最佳实践、Express中间件架构
长尾关键词:Express中间件怎么开发、自定义中间件编写方法、Express中间件原理、中间件开发教程、Node.js中间件设计模式
通过本节Express中间件开发教程,你将系统性掌握:
中间件架构是什么?这是Express框架的核心概念。中间件是一个函数,它可以访问请求对象(req)、响应对象(res)和应用程序请求-响应循环中的下一个中间件函数(next),也是Express应用架构的基础。
💡 设计理念:中间件遵循"关注点分离"原则,让每个组件专注于特定任务
让我们深入了解Express的内置中间件:
// 🎉 Express内置中间件详解
const express = require('express');
const path = require('path');
const app = express();
// 1. express.json() - JSON请求体解析
app.use(express.json({
limit: '10mb', // 限制请求体大小
strict: true, // 严格模式,只接受数组和对象
type: 'application/json', // 指定Content-Type
verify: (req, res, buf, encoding) => {
// 验证函数,可以在解析前验证原始数据
console.log('JSON数据大小:', buf.length);
if (buf.length > 1024 * 1024) { // 1MB
throw new Error('JSON payload too large');
}
}
}));
// 2. express.urlencoded() - URL编码数据解析
app.use(express.urlencoded({
extended: true, // 使用qs库解析(支持嵌套对象)
limit: '10mb', // 限制数据大小
parameterLimit: 1000, // 限制参数数量
type: 'application/x-www-form-urlencoded'
}));
// 3. express.static() - 静态文件服务
app.use('/public', express.static(path.join(__dirname, 'public'), {
maxAge: '1d', // 缓存时间
etag: true, // 启用ETag
lastModified: true, // 启用Last-Modified
index: ['index.html'], // 默认文件
dotfiles: 'ignore', // 忽略点文件
setHeaders: (res, path, stat) => {
// 自定义响应头
if (path.endsWith('.js')) {
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
}
}
}));
// 4. express.raw() - 原始数据解析
app.use('/upload', express.raw({
limit: '50mb',
type: 'application/octet-stream'
}));
// 5. express.text() - 文本数据解析
app.use('/webhook', express.text({
limit: '1mb',
type: 'text/plain'
}));
// 内置中间件使用示例
app.post('/api/data', (req, res) => {
console.log('JSON数据:', req.body);
res.json({
message: '数据接收成功',
received: req.body,
contentType: req.get('Content-Type')
});
});
app.post('/api/form', (req, res) => {
console.log('表单数据:', req.body);
res.json({
message: '表单提交成功',
formData: req.body
});
});
app.post('/upload/binary', (req, res) => {
console.log('二进制数据大小:', req.body.length);
res.json({
message: '二进制数据上传成功',
size: req.body.length
});
});
console.log('内置中间件配置完成');// 🔧 第三方中间件集成示例
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
const app = express();
// 1. helmet - 安全中间件
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// 2. cors - 跨域资源共享
app.use(cors({
origin: function (origin, callback) {
// 允许的域名列表
const allowedOrigins = [
'http://localhost:3000',
'https://myapp.com',
'https://www.myapp.com'
];
// 允许没有origin的请求(如移动应用)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}));
// 3. morgan - 日志中间件
app.use(morgan('combined', {
skip: function (req, res) {
// 跳过静态文件请求的日志
return req.url.startsWith('/public');
},
stream: {
write: function(message) {
// 自定义日志输出
console.log(message.trim());
}
}
}));
// 4. compression - 响应压缩
app.use(compression({
filter: function (req, res) {
// 自定义压缩条件
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
},
level: 6, // 压缩级别 (0-9)
threshold: 1024 // 只压缩大于1KB的响应
}));
// 5. express-rate-limit - 限流中间件
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP 15分钟内最多100个请求
message: {
error: 'Too many requests',
message: '请求过于频繁,请稍后再试'
},
standardHeaders: true, // 返回限流信息在 `RateLimit-*` 头中
legacyHeaders: false, // 禁用 `X-RateLimit-*` 头
handler: (req, res) => {
res.status(429).json({
error: 'Too Many Requests',
message: '请求频率超过限制',
retryAfter: Math.round(req.rateLimit.resetTime / 1000)
});
}
});
// 6. express-slow-down - 请求减速
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000, // 15分钟
delayAfter: 50, // 50个请求后开始延迟
delayMs: 500 // 每个请求延迟500ms
});
// 应用限流中间件
app.use('/api/', limiter);
app.use('/api/', speedLimiter);
// API路由示例
app.get('/api/public', (req, res) => {
res.json({
message: '公开API接口',
timestamp: new Date().toISOString(),
headers: {
userAgent: req.get('User-Agent'),
origin: req.get('Origin')
}
});
});
// 7. 自定义第三方中间件配置
const multer = require('multer');
// 文件上传中间件配置
const upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
}),
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
files: 5 // 最多5个文件
},
fileFilter: function (req, file, cb) {
// 文件类型过滤
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('不支持的文件类型'));
}
}
});
app.post('/api/upload', upload.array('images', 5), (req, res) => {
res.json({
message: '文件上传成功',
files: req.files.map(file => ({
filename: file.filename,
originalname: file.originalname,
size: file.size,
mimetype: file.mimetype
}))
});
});
// 错误处理
app.use((error, req, res, next) => {
if (error instanceof multer.MulterError) {
if (error.code === 'LIMIT_FILE_SIZE') {
return res.status(413).json({
error: 'File too large',
message: '文件大小超过限制'
});
}
}
console.error('中间件错误:', error);
res.status(500).json({
error: 'Internal Server Error',
message: '服务器内部错误'
});
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`第三方中间件演示服务器启动:http://localhost:${PORT}`);
});第三方中间件集成要点:
// 🚀 自定义中间件开发详解
const express = require('express');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const app = express();
// 1. 基础自定义中间件
function requestId(req, res, next) {
// 为每个请求生成唯一ID
req.id = crypto.randomUUID();
res.setHeader('X-Request-ID', req.id);
next();
}
// 2. 带配置的中间件工厂函数
function createLogger(options = {}) {
const {
format = 'combined',
logFile = null,
skipSuccessful = false
} = options;
return function logger(req, res, next) {
const start = Date.now();
// 监听响应完成事件
res.on('finish', () => {
const duration = Date.now() - start;
const statusCode = res.statusCode;
// 根据配置决定是否跳过成功请求
if (skipSuccessful && statusCode < 400) {
return;
}
let logMessage;
if (format === 'simple') {
logMessage = `${req.method} ${req.url} - ${statusCode} - ${duration}ms`;
} else {
logMessage = `${new Date().toISOString()} - ${req.id} - ${req.method} ${req.url} - ${statusCode} - ${duration}ms - ${req.get('User-Agent')}`;
}
console.log(logMessage);
// 写入日志文件
if (logFile) {
fs.appendFileSync(logFile, logMessage + '\n');
}
});
next();
};
}
// 3. 异步中间件
function asyncMiddleware(fn) {
return function(req, res, next) {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// 异步验证中间件示例
const validateUser = asyncMiddleware(async (req, res, next) => {
const userId = req.params.userId;
if (!userId) {
return res.status(400).json({ error: '缺少用户ID' });
}
try {
// 模拟异步数据库查询
const user = await new Promise((resolve, reject) => {
setTimeout(() => {
if (userId === '123') {
resolve({ id: 123, name: '测试用户', email: 'test@example.com' });
} else {
reject(new Error('用户不存在'));
}
}, 100);
});
req.user = user;
next();
} catch (error) {
res.status(404).json({
error: 'User not found',
message: error.message
});
}
});
// 4. 条件中间件
function conditionalMiddleware(condition, middleware) {
return function(req, res, next) {
if (condition(req)) {
middleware(req, res, next);
} else {
next();
}
};
}
// 5. 缓存中间件
function createCacheMiddleware(options = {}) {
const {
ttl = 300, // 缓存时间(秒)
keyGenerator = (req) => req.originalUrl
} = options;
const cache = new Map();
return function cacheMiddleware(req, res, next) {
// 只缓存GET请求
if (req.method !== 'GET') {
return next();
}
const key = keyGenerator(req);
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl * 1000) {
console.log(`缓存命中: ${key}`);
res.setHeader('X-Cache', 'HIT');
return res.json(cached.data);
}
// 拦截res.json方法
const originalJson = res.json;
res.json = function(data) {
// 缓存响应数据
cache.set(key, {
data,
timestamp: Date.now()
});
res.setHeader('X-Cache', 'MISS');
console.log(`缓存存储: ${key}`);
// 调用原始方法
originalJson.call(this, data);
};
next();
};
}
// 6. API版本控制中间件
function apiVersion(version) {
return function(req, res, next) {
const requestedVersion = req.headers['api-version'] || req.query.version || '1.0';
if (requestedVersion !== version) {
return res.status(400).json({
error: 'API Version Mismatch',
message: `请求版本 ${requestedVersion},当前支持版本 ${version}`
});
}
req.apiVersion = version;
res.setHeader('API-Version', version);
next();
};
}
// 7. 请求验证中间件
function validateRequest(schema) {
return function(req, res, next) {
const errors = [];
// 验证必需字段
if (schema.required) {
for (const field of schema.required) {
if (!(field in req.body)) {
errors.push(`缺少必需字段: ${field}`);
}
}
}
// 验证字段类型
if (schema.fields) {
for (const [field, rules] of Object.entries(schema.fields)) {
const value = req.body[field];
if (value !== undefined) {
if (rules.type && typeof value !== rules.type) {
errors.push(`字段 ${field} 类型错误`);
}
if (rules.minLength && value.length < rules.minLength) {
errors.push(`字段 ${field} 长度不足`);
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`字段 ${field} 格式错误`);
}
}
}
}
if (errors.length > 0) {
return res.status(400).json({
error: 'Validation Failed',
details: errors
});
}
next();
};
}
// 8. 性能监控中间件
function performanceMonitor() {
return function(req, res, next) {
const start = process.hrtime.bigint();
res.on('finish', () => {
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000; // 转换为毫秒
// 记录性能指标
console.log(`性能监控 - ${req.method} ${req.url}:`);
console.log(` 响应时间: ${duration.toFixed(2)}ms`);
console.log(` 状态码: ${res.statusCode}`);
console.log(` 内存使用: ${JSON.stringify(process.memoryUsage())}`);
// 设置性能头
res.setHeader('X-Response-Time', `${duration.toFixed(2)}ms`);
// 性能警告
if (duration > 1000) {
console.warn(`⚠️ 慢请求警告: ${req.url} 耗时 ${duration.toFixed(2)}ms`);
}
});
next();
};
}
// 使用自定义中间件
app.use(requestId);
app.use(createLogger({ format: 'combined', logFile: 'access.log' }));
app.use(performanceMonitor());
// 应用级缓存
app.use('/api/cached', createCacheMiddleware({ ttl: 60 }));
// 路由级中间件使用
app.get('/api/v1/users/:userId',
apiVersion('1.0'),
validateUser,
(req, res) => {
res.json({
message: '获取用户信息',
user: req.user,
apiVersion: req.apiVersion,
requestId: req.id
});
}
);
app.post('/api/users',
validateRequest({
required: ['name', 'email'],
fields: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
age: { type: 'number' }
}
}),
(req, res) => {
res.json({
message: '用户创建成功',
user: req.body,
requestId: req.id
});
}
);
// 条件中间件示例
app.use('/admin', conditionalMiddleware(
(req) => req.headers['x-admin-key'] === 'secret-key',
(req, res, next) => {
req.isAdmin = true;
next();
}
));
app.get('/admin/dashboard', (req, res) => {
if (!req.isAdmin) {
return res.status(403).json({ error: '需要管理员权限' });
}
res.json({
message: '管理员仪表板',
isAdmin: req.isAdmin
});
});
// 缓存测试路由
app.get('/api/cached/data', (req, res) => {
res.json({
message: '这是缓存的数据',
timestamp: new Date().toISOString(),
random: Math.random()
});
});
console.log('自定义中间件配置完成');自定义中间件开发要点:
// 🛡️ 错误处理中间件详解
const express = require('express');
const app = express();
// 1. 自定义错误类
class AppError extends Error {
constructor(message, statusCode, code = null) {
super(message);
this.statusCode = statusCode;
this.code = code;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, details = []) {
super(message, 400, 'VALIDATION_ERROR');
this.details = details;
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404, 'NOT_FOUND');
}
}
class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(message, 401, 'UNAUTHORIZED');
}
}
// 2. 错误日志中间件
function errorLogger(err, req, res, next) {
const errorInfo = {
timestamp: new Date().toISOString(),
requestId: req.id,
method: req.method,
url: req.url,
userAgent: req.get('User-Agent'),
ip: req.ip,
error: {
name: err.name,
message: err.message,
code: err.code,
statusCode: err.statusCode,
stack: err.stack
}
};
// 根据错误级别记录不同日志
if (err.statusCode >= 500) {
console.error('🚨 服务器错误:', JSON.stringify(errorInfo, null, 2));
} else if (err.statusCode >= 400) {
console.warn('⚠️ 客户端错误:', JSON.stringify(errorInfo, null, 2));
} else {
console.log('ℹ️ 其他错误:', JSON.stringify(errorInfo, null, 2));
}
next(err);
}
// 3. 错误响应格式化中间件
function errorFormatter(err, req, res, next) {
let statusCode = err.statusCode || 500;
let message = err.message || 'Internal Server Error';
let code = err.code || 'INTERNAL_ERROR';
// 处理特定类型的错误
if (err.name === 'ValidationError') {
statusCode = 400;
code = 'VALIDATION_ERROR';
} else if (err.name === 'CastError') {
statusCode = 400;
code = 'INVALID_ID';
message = '无效的ID格式';
} else if (err.code === 11000) {
statusCode = 409;
code = 'DUPLICATE_ENTRY';
message = '数据已存在';
} else if (err.name === 'JsonWebTokenError') {
statusCode = 401;
code = 'INVALID_TOKEN';
message = '无效的认证令牌';
} else if (err.name === 'TokenExpiredError') {
statusCode = 401;
code = 'TOKEN_EXPIRED';
message = '认证令牌已过期';
}
// 构建错误响应
const errorResponse = {
error: {
code,
message,
timestamp: new Date().toISOString(),
requestId: req.id
}
};
// 开发环境包含更多错误信息
if (process.env.NODE_ENV === 'development') {
errorResponse.error.stack = err.stack;
errorResponse.error.details = err.details;
}
// 验证错误包含详细信息
if (err instanceof ValidationError) {
errorResponse.error.details = err.details;
}
res.status(statusCode).json(errorResponse);
}
// 4. 异步错误捕获包装器
function catchAsync(fn) {
return function(req, res, next) {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// 5. 全局未捕获异常处理
process.on('uncaughtException', (err) => {
console.error('💥 未捕获的异常:', err);
console.error('应用即将退出...');
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('💥 未处理的Promise拒绝:', reason);
console.error('Promise:', promise);
// 优雅关闭服务器
server.close(() => {
process.exit(1);
});
});
// 6. 404错误处理
function notFoundHandler(req, res, next) {
const error = new NotFoundError(`Route ${req.originalUrl}`);
next(error);
}
// 7. 错误恢复中间件
function errorRecovery(err, req, res, next) {
// 尝试从某些错误中恢复
if (err.code === 'ECONNRESET' || err.code === 'EPIPE') {
console.log('连接错误,尝试恢复...');
return res.status(503).json({
error: {
code: 'SERVICE_UNAVAILABLE',
message: '服务暂时不可用,请稍后重试'
}
});
}
next(err);
}
// 使用中间件
app.use(express.json());
// 示例路由
app.get('/api/test/error', (req, res, next) => {
// 抛出自定义错误
throw new AppError('这是一个测试错误', 400, 'TEST_ERROR');
});
app.get('/api/test/validation', (req, res, next) => {
const errors = [
'用户名不能为空',
'邮箱格式不正确',
'密码长度至少8位'
];
throw new ValidationError('数据验证失败', errors);
});
app.get('/api/test/async-error', catchAsync(async (req, res, next) => {
// 模拟异步操作错误
await new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('异步操作失败'));
}, 100);
});
res.json({ message: '不会执行到这里' });
}));
app.get('/api/test/unauthorized', (req, res, next) => {
throw new UnauthorizedError('需要登录才能访问此资源');
});
app.get('/api/test/not-found', (req, res, next) => {
throw new NotFoundError('用户');
});
// 错误处理中间件链(顺序很重要)
app.use(notFoundHandler); // 404处理
app.use(errorLogger); // 错误日志
app.use(errorRecovery); // 错误恢复
app.use(errorFormatter); // 错误格式化
const PORT = 3000;
const server = app.listen(PORT, () => {
console.log(`错误处理演示服务器启动:http://localhost:${PORT}`);
console.log('测试错误处理路由:');
console.log('- GET /api/test/error (自定义错误)');
console.log('- GET /api/test/validation (验证错误)');
console.log('- GET /api/test/async-error (异步错误)');
console.log('- GET /api/test/unauthorized (未授权错误)');
console.log('- GET /api/test/not-found (未找到错误)');
console.log('- GET /nonexistent (404错误)');
});错误处理中间件要点:
通过本节Express中间件开发教程的学习,你已经掌握:
A: 中间件按定义顺序执行,错误的顺序可能导致功能失效。例如:1)body-parser必须在需要解析请求体的路由前;2)认证中间件应在需要认证的路由前;3)错误处理中间件必须在最后;4)CORS中间件通常放在最前面。
A: 使用async/await或Promise,并确保错误能传递给错误处理中间件:1)使用catchAsync包装器;2)在Promise.catch中调用next(error);3)避免在异步操作中直接发送响应;4)使用try-catch包装async函数。
A: 设计原则:1)单一职责:每个中间件只做一件事;2)可配置性:支持参数配置;3)可测试性:易于单元测试;4)错误处理:正确传播错误;5)性能考虑:避免阻塞操作;6)文档完善:提供清晰的使用说明。
A: 性能优化策略:1)避免同步I/O操作;2)使用缓存减少重复计算;3)合理使用条件中间件;4)优化正则表达式;5)减少内存分配;6)使用性能监控工具分析瓶颈。
A: 最佳实践:1)区分操作错误和程序错误;2)记录详细的错误日志;3)提供一致的错误响应格式;4)在生产环境隐藏敏感信息;5)实现错误恢复机制;6)监控错误率和类型。
"中间件是Express框架的灵魂,掌握了中间件开发技术,你就具备了构建复杂Web应用的核心能力。继续探索模板引擎和会话管理,向Express专家的目标迈进!"