阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 中间件的部署与配置管理

中间件的部署与配置管理

作者:陈川 阅读数:18676人阅读 分类: Node.js

中间件的概念与作用

Express中间件本质上是函数,能够访问请求对象(req)、响应对象(res)和应用程序的请求-响应周期中的下一个中间件函数。这些函数可以执行以下操作:

  • 修改请求和响应对象
  • 结束请求-响应周期
  • 调用堆栈中的下一个中间件
// 最简单的中间件示例
app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

中间件的类型

Express中的中间件主要分为以下几种类型:

  1. 应用级中间件
// 使用app.use()加载的应用级中间件
app.use('/user/:id', (req, res, next) => {
  console.log('Request Type:', req.method);
  next();
});
  1. 路由级中间件
// 路由特定的中间件
router.get('/user/:id', (req, res, next) => {
  console.log('Request URL:', req.originalUrl);
  next();
}, (req, res) => {
  res.send('User Info');
});
  1. 错误处理中间件
// 错误处理中间件需要四个参数
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
  1. 内置中间件
// Express内置的静态文件中间件
app.use(express.static('public'));
  1. 第三方中间件
// 常用的第三方中间件示例
const bodyParser = require('body-parser');
app.use(bodyParser.json());

中间件的加载顺序

中间件的执行顺序至关重要,Express会按照中间件加载的顺序依次执行:

// 第一个中间件
app.use((req, res, next) => {
  console.log('First middleware');
  next();
});

// 第二个中间件
app.use('/user/:id', (req, res, next) => {
  console.log('Request Type:', req.method);
  next();
});

// 路由处理程序
app.get('/user/:id', (req, res) => {
  res.send('User Info');
});

中间件的配置管理

合理的配置管理可以提高中间件的可维护性:

  1. 环境变量配置
const morgan = require('morgan');

if (process.env.NODE_ENV === 'development') {
  app.use(morgan('dev'));
}
  1. 配置文件分离
// config/middlewares.js
module.exports = (app) => {
  app.use(require('helmet')());
  app.use(require('compression')());
};
  1. 条件加载中间件
const setupMiddlewares = (app) => {
  // 基础中间件
  app.use(express.json());
  
  // 开发环境专用中间件
  if (process.env.NODE_ENV !== 'production') {
    app.use(require('morgan')('dev'));
    app.use(require('errorhandler')());
  }
};

常用中间件的配置示例

  1. CORS配置
const cors = require('cors');
app.use(cors({
  origin: ['https://example.com', 'https://api.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));
  1. Helmet安全中间件
const helmet = require('helmet');
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com']
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));
  1. Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 每个IP限制100个请求
  message: 'Too many requests from this IP, please try again later'
});
app.use('/api/', limiter);

自定义中间件开发

开发自定义中间件可以满足特定业务需求:

  1. 请求日志中间件
function requestLogger(format) {
  return (req, res, next) => {
    const log = format
      .replace(':method', req.method)
      .replace(':url', req.url)
      .replace(':date', new Date().toISOString());
    console.log(log);
    next();
  };
}

app.use(requestLogger(':method :url at :date'));
  1. 认证中间件
function authenticate(req, res, next) {
  const authHeader = req.headers['authorization'];
  if (!authHeader) return res.sendStatus(401);
  
  const token = authHeader.split(' ')[1];
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
}

app.get('/protected', authenticate, (req, res) => {
  res.json({ message: 'Protected data' });
});
  1. 数据验证中间件
function validate(schema) {
  return (req, res, next) => {
    const { error } = schema.validate(req.body);
    if (error) {
      return res.status(400).json({
        error: error.details[0].message
      });
    }
    next();
  };
}

const userSchema = Joi.object({
  username: Joi.string().alphanum().min(3).max(30).required(),
  password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$'))
});

app.post('/register', validate(userSchema), (req, res) => {
  // 处理注册逻辑
});

中间件的性能优化

  1. 中间件缓存
const cache = require('memory-cache');
const memCache = new cache.Cache();

function cacheMiddleware(duration) {
  return (req, res, next) => {
    const key = '__express__' + req.originalUrl || req.url;
    const cacheContent = memCache.get(key);
    if (cacheContent) {
      res.send(cacheContent);
      return;
    } else {
      res.sendResponse = res.send;
      res.send = (body) => {
        memCache.put(key, body, duration * 1000);
        res.sendResponse(body);
      };
      next();
    }
  };
}

app.get('/api/data', cacheMiddleware(30), (req, res) => {
  // 从数据库获取数据
  res.json({ data: 'Expensive data' });
});
  1. 中间件并行处理
const { parallelMiddlewares } = require('express-parallel-middleware');

const middleware1 = (req, res, next) => {
  setTimeout(() => {
    req.data1 = 'Data from middleware 1';
    next();
  }, 1000);
};

const middleware2 = (req, res, next) => {
  setTimeout(() => {
    req.data2 = 'Data from middleware 2';
    next();
  }, 1500);
};

app.use(parallelMiddlewares([middleware1, middleware2]));

app.get('/parallel', (req, res) => {
  res.json({
    data1: req.data1,
    data2: req.data2
  });
});
  1. 中间件懒加载
function lazyLoadMiddleware(modulePath) {
  let middleware = null;
  
  return (req, res, next) => {
    if (!middleware) {
      middleware = require(modulePath);
    }
    return middleware(req, res, next);
  };
}

app.use(lazyLoadMiddleware('./heavy-middleware'));

中间件的测试策略

  1. 单元测试中间件
const request = require('supertest');
const express = require('express');

describe('Authentication Middleware', () => {
  let app;
  
  beforeEach(() => {
    app = express();
    app.use(express.json());
    app.use(require('./middlewares/auth'));
    app.get('/test', (req, res) => res.status(200).send());
  });

  it('should return 401 without token', async () => {
    const res = await request(app).get('/test');
    expect(res.statusCode).toEqual(401);
  });

  it('should return 200 with valid token', async () => {
    const token = generateValidToken();
    const res = await request(app)
      .get('/test')
      .set('Authorization', `Bearer ${token}`);
    expect(res.statusCode).toEqual(200);
  });
});
  1. 集成测试中间件链
describe('Middleware Chain', () => {
  it('should execute in correct order', (done) => {
    const app = express();
    const order = [];
    
    app.use((req, res, next) => {
      order.push(1);
      next();
    });
    
    app.use((req, res, next) => {
      order.push(2);
      next();
    });
    
    app.get('/', (req, res) => {
      order.push(3);
      res.send();
      expect(order).toEqual([1, 2, 3]);
      done();
    });
    
    request(app).get('/').end(() => {});
  });
});
  1. 性能测试中间件
const loadTest = require('loadtest');

describe('Rate Limiter Middleware Performance', () => {
  it('should handle 100 requests in under 2 seconds', (done) => {
    const options = {
      url: 'http://localhost:3000/api',
      maxRequests: 100,
      concurrency: 10,
      method: 'GET'
    };
    
    loadTest.loadTest(options, (error, results) => {
      if (error) return done(error);
      expect(results.totalTimeSeconds).toBeLessThan(2);
      done();
    });
  });
});

中间件的错误处理

  1. 异步错误处理
function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next))
      .catch(next);
  };
}

app.get('/async', asyncHandler(async (req, res) => {
  const data = await someAsyncOperation();
  res.json(data);
}));
  1. 错误分类处理
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
    this.status = 400;
  }
}

app.use((err, req, res, next) => {
  if (err.name === 'ValidationError') {
    return res.status(err.status).json({
      error: err.message
    });
  }
  next(err);
});
  1. 错误日志中间件
function errorLogger(err, req, res, next) {
  console.error(`[${new Date().toISOString()}] ${err.stack}`);
  next(err);
}

function clientErrorHandler(err, req, res, next) {
  if (req.xhr) {
    res.status(500).json({ error: 'Something failed!' });
  } else {
    next(err);
  }
}

app.use(errorLogger);
app.use(clientErrorHandler);

中间件的版本管理

  1. API版本控制中间件
function apiVersion(version) {
  return (req, res, next) => {
    req.apiVersion = version;
    next();
  };
}

const v1Router = express.Router();
v1Router.use(apiVersion('v1'));
v1Router.get('/users', (req, res) => {
  res.json({ version: req.apiVersion, users: [] });
});

const v2Router = express.Router();
v2Router.use(apiVersion('v2'));
v2Router.get('/users', (req, res) => {
  res.json({ version: req.apiVersion, users: [], meta: {} });
});

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
  1. 中间件版本兼容性检查
function checkCompatibility(minVersion) {
  return (req, res, next) => {
    const clientVersion = req.headers['x-api-version'];
    if (compareVersions(clientVersion, minVersion) < 0) {
      return res.status(426).json({
        error: 'Upgrade required',
        minVersion: minVersion
      });
    }
    next();
  };
}

app.use(checkCompatibility('1.2.0'));

中间件的动态加载

  1. 按需加载中间件
function dynamicLoader(config) {
  return (req, res, next) => {
    const middlewareName = req.headers['x-middleware'];
    if (config[middlewareName]) {
      return config[middlewareName](req, res, next);
    }
    next();
  };
}

const middlewares = {
  logger: require('./middlewares/logger'),
  auth: require('./middlewares/auth')
};

app.use(dynamicLoader(middlewares));
  1. 功能开关中间件
function featureToggle(features) {
  return (req, res, next) => {
    req.features = {};
    for (const [name, isActive] of Object.entries(features)) {
      req.features[name] = isActive;
    }
    next();
  };
}

app.use(featureToggle({
  newDashboard: process.env.FEATURE_DASHBOARD === 'true',
  experimentalAPI: false
}));

app.get('/dashboard', (req, res) => {
  if (req.features.newDashboard) {
    return res.send('New Dashboard');
  }
  res.send('Old Dashboard');
});

中间件的监控与指标

  1. 请求指标收集
const prometheus = require('prom-client');

const httpRequestDurationMicroseconds = new prometheus.Histogram({
  name: 'http_request_duration_ms',
  help: 'Duration of HTTP requests in ms',
  labelNames: ['method', 'route', 'code'],
  buckets: [0.1, 5, 15, 50, 100, 200, 300, 400, 500]
});

function metricsMiddleware(req, res, next) {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    httpRequestDurationMicroseconds
      .labels(req.method, req.route.path, res.statusCode)
      .observe(duration);
  });
  
  next();
}

app.use(metricsMiddleware);
  1. 健康检查中间件
function healthCheck(options = {}) {
  const { path = '/health', checks = [] } = options;
  
  return (req, res, next) => {
    if (req.path !== path) return next();
    
    Promise.all(checks.map(check => check()))
      .then(results => {
        const isHealthy = results.every(result => result.healthy);
        res.status(isHealthy ? 200 : 503).json({
          status: isHealthy ? 'OK' : 'Service Unavailable',
          details: results
        });
      })
      .catch(() => {
        res.status(503).json({ status: 'Service Unavailable' });
      });
  };
}

const dbCheck = () => new Promise(resolve => {
  // 检查数据库连接
  resolve({ healthy: true, service: 'database' });
});

app.use(healthCheck({
  checks: [dbCheck]
}));

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌