Express中间件(Middleware)详解:从零开始掌握(4)

发布于:2025-04-16 ⋅ 阅读:(10) ⋅ 点赞:(0)

下面我将为你提供四个实战项目的完整实现代码,每个项目都展示了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');
});

项目部署与测试建议

  1. API网关

    • 测试不同API密钥的访问
    • 测试限流功能
    • 使用Postman测试路由转发
  2. 文件处理管道

    • 测试不同文件类型的上传
    • 检查处理后的图片质量
    • 测试大文件上传性能
  3. A/B测试框架

    • 清除cookie测试分组分配
    • 模拟转换事件
    • 分析不同版本的表现
  4. 多租户系统

    • 测试不同子域名的访问
    • 模拟租户特定的错误
    • 测试租户隔离性

网站公告

今日签到

点亮在社区的每一天
去签到