下面我将为你提供四个实战项目的完整实现代码,每个项目都展示了Express中间件的实际应用场景。
1. API网关实现
const express = require('express');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const morgan = require('morgan');
const app = express();
// 1. 安全中间件
app.use(helmet());
// 2. 请求日志
app.use(morgan('combined'));
// 3. API密钥验证
app.use((req, res, next) => {
const apiKey = req.headers['x-api-key'];
const validKeys = ['key123', 'key456'];
if (!validKeys.includes(apiKey)) {
return res.status(401).json({ error: 'Invalid API Key' });
}
next();
});
// 4. 限流中间件
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求
message: 'Too many requests from this IP, please try again later'
});
app.use(apiLimiter);
// 5. 路由转发
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/users', createProxyMiddleware({
target: 'http://user-service:3000',
changeOrigin: true
}));
app.use('/products', createProxyMiddleware({
target: 'http://product-service:3000',
changeOrigin: true
}));
// 6. 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
app.listen(3000, () => {
console.log('API Gateway running on port 3000');
});
2. 文件处理管道
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const sharp = require('sharp');
const app = express();
// 1. 文件存储配置
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = 'uploads/';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`);
}
});
const upload = multer({ storage });
// 2. 文件类型验证中间件
function validateFile(req, res, next) {
const allowedTypes = ['image/jpeg', 'image/png'];
if (!allowedTypes.includes(req.file.mimetype)) {
return res.status(400).json({ error: 'Invalid file type' });
}
next();
}
// 3. 图片处理中间件
async function processImage(req, res, next) {
try {
const outputPath = `processed/${req.file.filename}`;
await sharp(req.file.path)
.resize(800, 800, { fit: 'inside' })
.toFormat('jpeg', { quality: 80 })
.toFile(outputPath);
req.file.processedPath = outputPath;
next();
} catch (err) {
next(err);
}
}
// 4. 文件上传路由
app.post('/upload',
upload.single('file'), // 1. 接收文件
validateFile, // 2. 验证类型
processImage, // 3. 处理图片
(req, res) => { // 4. 返回结果
res.json({
original: req.file.path,
processed: req.file.processedPath
});
}
);
// 错误处理
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'File processing failed' });
});
app.listen(3000, () => {
console.log('File processor running on port 3000');
});
3. A/B测试框架
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
// 1. A/B测试分配中间件
app.use((req, res, next) => {
// 如果已经有分组cookie,继续使用
if (req.cookies.abTestGroup) {
req.abTestGroup = req.cookies.abTestGroup;
return next();
}
// 随机分配A组或B组
const group = Math.random() < 0.5 ? 'A' : 'B';
res.cookie('abTestGroup', group, { maxAge: 30 * 24 * 60 * 60 * 1000 }); // 30天
req.abTestGroup = group;
next();
});
// 2. 不同版本的页面处理
function variantA(req, res) {
res.send(`
<h1 style="color: blue">Version A</h1>
<p>This is the original version of our page</p>
`);
}
function variantB(req, res) {
res.send(`
<h1 style="color: green">Version B</h1>
<p>This is the experimental version of our page</p>
`);
}
// 3. 路由处理
app.get('/home', (req, res) => {
// 根据分组显示不同版本
req.abTestGroup === 'A' ? variantA(req, res) : variantB(req, res);
});
// 4. 结果跟踪
app.get('/track-conversion', (req, res) => {
const { group } = req.query;
console.log(`Conversion recorded for group ${group}`);
res.sendStatus(200);
});
// 5. 分析仪表板
app.get('/dashboard', (req, res) => {
// 这里应该是从数据库获取实际数据
const stats = {
A: { views: 1000, conversions: 100 },
B: { views: 950, conversions: 120 }
};
res.json(stats);
});
app.listen(3000, () => {
console.log('A/B Testing Framework running on port 3000');
});
4. 多租户系统
const express = require('express');
const app = express();
// 模拟租户数据
const tenants = {
'tenant1': {
dbConfig: {
host: 'tenant1.db.example.com',
name: 'tenant1_db'
},
theme: 'blue'
},
'tenant2': {
dbConfig: {
host: 'tenant2.db.example.com',
name: 'tenant2_db'
},
theme: 'green'
}
};
// 1. 租户识别中间件
app.use((req, res, next) => {
// 从子域名识别租户 (tenant1.example.com)
const hostParts = req.hostname.split('.');
const tenantId = hostParts.length > 2 ? hostParts[0] : null;
// 或者从请求头识别
// const tenantId = req.headers['x-tenant-id'];
if (!tenantId || !tenants[tenantId]) {
return res.status(404).send('Tenant not found');
}
req.tenant = tenants[tenantId];
req.tenant.id = tenantId;
next();
});
// 2. 租户数据库连接中间件
app.use((req, res, next) => {
// 模拟数据库连接
console.log(`Connecting to tenant database: ${req.tenant.dbConfig.host}`);
// 实际应用中这里会建立数据库连接
next();
});
// 3. 租户特定路由
app.get('/dashboard', (req, res) => {
res.send(`
<h1 style="color: ${req.tenant.theme}">Dashboard for ${req.tenant.id}</h1>
<p>Connected to database: ${req.tenant.dbConfig.name}</p>
`);
});
// 4. 租户配置API
app.get('/api/config', (req, res) => {
res.json({
tenant: req.tenant.id,
theme: req.tenant.theme,
dbConfig: req.tenant.dbConfig
});
});
// 错误处理
app.use((err, req, res, next) => {
console.error(`[${req.tenant?.id}]`, err);
res.status(500).send('Tenant-specific error occurred');
});
app.listen(3000, () => {
console.log('Multi-tenant system running on port 3000');
});
项目部署与测试建议
API网关:
- 测试不同API密钥的访问
- 测试限流功能
- 使用Postman测试路由转发
文件处理管道:
- 测试不同文件类型的上传
- 检查处理后的图片质量
- 测试大文件上传性能
A/B测试框架:
- 清除cookie测试分组分配
- 模拟转换事件
- 分析不同版本的表现
多租户系统:
- 测试不同子域名的访问
- 模拟租户特定的错误
- 测试租户隔离性