您现在的位置是:网站首页 > 中间件的部署与配置管理文章详情

中间件的部署与配置管理

中间件的概念与作用

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

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

中间件类型

应用级中间件

通过app.use()和app.METHOD()函数绑定到app对象:

// 对所有请求生效
app.use((req, res, next) => {
  console.log('应用级中间件');
  next();
});

// 只对特定路由生效
app.use('/api', (req, res, next) => {
  console.log('API路由中间件');
  next();
});

路由级中间件

工作方式与应用级中间件类似,但绑定到express.Router()实例:

const router = express.Router();

router.use((req, res, next) => {
  console.log('路由级中间件');
  next();
});

错误处理中间件

专门处理错误的中间件,需要四个参数:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('服务器错误!');
});

内置中间件

Express提供的内置中间件:

  • express.static: 托管静态文件
  • express.json: 解析JSON请求体
  • express.urlencoded: 解析URL编码请求体
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

第三方中间件

社区提供的中间件,需要单独安装:

const morgan = require('morgan');
const helmet = require('helmet');

app.use(morgan('dev'));  // 日志记录
app.use(helmet());       // 安全相关HTTP头

中间件执行顺序

中间件的执行顺序非常重要,代码中定义的顺序就是执行的顺序:

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

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

app.get('/', (req, res) => {
  res.send('Hello World');
});

中间件配置管理

环境相关配置

根据环境变量配置不同的中间件:

if (process.env.NODE_ENV === 'development') {
  app.use(require('morgan')('dev'));
}

if (process.env.NODE_ENV === 'production') {
  app.use(require('compression')());
}

条件中间件

根据条件动态启用中间件:

const unless = (path, middleware) => {
  return (req, res, next) => {
    if (path === req.path) {
      return next();
    } else {
      return middleware(req, res, next);
    }
  };
};

app.use(unless('/login', require('authenticate')));

中间件工厂模式

创建可配置的中间件:

function createLogger(format) {
  return (req, res, next) => {
    console.log(`[${format}] ${req.method} ${req.url}`);
    next();
  };
}

app.use(createLogger('dev'));

常用中间件配置示例

跨域配置

const cors = require('cors');

// 基本配置
app.use(cors());

// 自定义配置
app.use(cors({
  origin: 'https://example.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type']
}));

请求体解析

// JSON解析
app.use(express.json({
  limit: '1mb',          // 限制请求体大小
  strict: true,          // 只接受数组和对象
  type: 'application/json' // 指定Content-Type
}));

// URL编码解析
app.use(express.urlencoded({
  extended: true,        // 使用qs库解析
  parameterLimit: 1000,  // 限制参数数量
  limit: '1mb'           // 限制请求体大小
}));

会话管理

const session = require('express-session');

app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: { 
    secure: process.env.NODE_ENV === 'production',
    maxAge: 24 * 60 * 60 * 1000 // 24小时
  }
}));

静态文件服务

app.use(express.static('public', {
  dotfiles: 'ignore',    // 忽略点文件
  etag: true,            // 启用ETag
  extensions: ['html'],  // 自动补全扩展名
  index: 'index.html',   // 默认索引文件
  maxAge: '1d',          // 缓存时间
  redirect: true         // 重定向到目录
}));

中间件性能优化

中间件缓存

const apicache = require('apicache');
let cache = apicache.middleware;

// 缓存所有GET请求5分钟
app.use(cache('5 minutes'));

// 特定路由缓存
app.get('/api/data', cache('10 minutes'), (req, res) => {
  // 数据处理逻辑
});

中间件压缩

const compression = require('compression');

app.use(compression({
  level: 6,              // 压缩级别(1-9)
  threshold: '1kb',      // 最小压缩大小
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));

中间件并行处理

const { parallelMiddlewares } = require('express-parallel-middleware');

const middleware1 = (req, res, next) => { /* ... */ };
const middleware2 = (req, res, next) => { /* ... */ };

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

中间件错误处理最佳实践

统一错误处理

// 业务逻辑中间件
app.get('/user/:id', async (req, res, next) => {
  try {
    const user = await getUserById(req.params.id);
    res.json(user);
  } catch (err) {
    next(err); // 传递到错误处理中间件
  }
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err);
  
  const statusCode = err.statusCode || 500;
  const message = statusCode === 500 ? '服务器错误' : err.message;
  
  res.status(statusCode).json({
    error: {
      message: message,
      code: statusCode
    }
  });
});

异步错误处理

// 包装异步中间件
const asyncHandler = fn => (req, res, next) => {
  Promise.resolve(fn(req, res, next))
    .catch(next);
};

// 使用示例
app.get('/async', asyncHandler(async (req, res) => {
  const data = await fetchData();
  res.json(data);
}));

404处理

// 放在所有路由之后
app.use((req, res, next) => {
  res.status(404).json({
    error: {
      message: '未找到资源',
      code: 404
    }
  });
});

中间件测试与调试

单元测试中间件

const request = require('supertest');
const express = require('express');

describe('认证中间件', () => {
  let app;
  
  beforeEach(() => {
    app = express();
    app.use(require('./authMiddleware'));
    app.get('/', (req, res) => res.send('OK'));
  });
  
  it('应该拒绝没有token的请求', async () => {
    const res = await request(app).get('/');
    expect(res.statusCode).toBe(401);
  });
});

中间件调试技巧

// 调试中间件执行顺序
app.use((req, res, next) => {
  console.log('中间件1 - 开始');
  next();
  console.log('中间件1 - 结束');
});

app.use((req, res, next) => {
  console.log('中间件2 - 开始');
  next();
  console.log('中间件2 - 结束');
});

// 使用debug模块
const debug = require('debug')('app:middleware');
app.use((req, res, next) => {
  debug('请求 %s %s', req.method, req.url);
  next();
});

自定义中间件开发

请求计时中间件

function responseTime() {
  return (req, res, next) => {
    const start = Date.now();
    
    res.on('finish', () => {
      const duration = Date.now() - start;
      console.log(`${req.method} ${req.url} - ${duration}ms`);
    });
    
    next();
  };
}

app.use(responseTime());

API版本控制中间件

function versionMiddleware(versions) {
  return (req, res, next) => {
    const version = req.headers['accept-version'] || 'v1';
    
    if (!versions[version]) {
      return res.status(400).json({
        error: `不支持的API版本: ${version}`
      });
    }
    
    req.version = version;
    req.versionHandler = versions[version];
    next();
  };
}

app.use(versionMiddleware({
  v1: require('./controllers/v1'),
  v2: require('./controllers/v2')
}));

请求验证中间件

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 Joi = require('joi');
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('/users', validate(userSchema), (req, res) => {
  // 处理有效请求
});

中间件安全实践

安全头部设置

const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com'],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", 'data:', 'cdn.example.com']
  }
}));

app.use(helmet.referrerPolicy({ policy: 'same-origin' }));

速率限制

const rateLimit = require('express-rate-limit');

const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100,                 // 每个IP限制100个请求
  message: '请求过多,请稍后再试'
});

app.use('/api/', apiLimiter);

CSRF保护

const csrf = require('csurf');
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.use(csrf({ cookie: true }));

// 在视图中使用CSRF令牌
app.use((req, res, next) => {
  res.locals.csrfToken = req.csrfToken();
  next();
});

中间件与微服务架构

API网关中间件

const { createProxyMiddleware } = require('http-proxy-middleware');

app.use('/users', createProxyMiddleware({
  target: 'http://user-service:3000',
  changeOrigin: true,
  pathRewrite: { '^/users': '' }
}));

app.use('/products', createProxyMiddleware({
  target: 'http://product-service:3000',
  changeOrigin: true,
  pathRewrite: { '^/products': '' }
}));

服务间认证中间件

function serviceAuth(allowedServices) {
  return (req, res, next) => {
    const serviceToken = req.headers['x-service-token'];
    
    if (!serviceToken || !allowedServices.includes(serviceToken)) {
      return res.status(403).json({
        error: '服务间认证失败'
      });
    }
    
    next();
  };
}

app.use('/internal', serviceAuth(['AUTH_SERVICE', 'PAYMENT_SERVICE']));

请求追踪中间件

const { v4: uuidv4 } = require('uuid');

app.use((req, res, next) => {
  req.requestId = uuidv4();
  res.setHeader('X-Request-ID', req.requestId);
  
  // 传递给下游服务
  if (req.headers['x-service-call']) {
    req.headers['x-request-id'] = req.requestId;
  }
  
  next();
});

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步