阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 中间件组合与复用技巧

中间件组合与复用技巧

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

中间件组合与复用技巧

Koa2中间件机制基于洋葱模型,通过组合不同的中间件函数实现请求处理流程的灵活控制。合理组合与复用中间件能显著提升代码可维护性,避免重复逻辑。下面从基础组合到高级模式逐步展开具体实践方案。

基础中间件组合方式

最简单的组合方式是直接使用app.use()链式调用:

const Koa = require('koa');
const app = new Koa();

app
  .use(async (ctx, next) => {
    console.log('Middleware 1 start');
    await next();
    console.log('Middleware 1 end');
  })
  .use(async (ctx, next) => {
    console.log('Middleware 2 start');
    await next();
    console.log('Middleware 2 end');
  });

这种基础组合会产生典型的洋葱圈执行顺序:

Middleware 1 start → Middleware 2 start → Middleware 2 end → Middleware 1 end

模块化中间件封装

将业务逻辑封装为独立模块是复用的基础模式。典型示例是错误处理中间件:

// error-handler.js
module.exports = function() {
  return async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      ctx.status = err.status || 500;
      ctx.body = { error: err.message };
      ctx.app.emit('error', err, ctx);
    }
  };
};

// app.js
const errorHandler = require('./error-handler');
app.use(errorHandler());

中间件工厂模式

通过工厂函数创建可配置的中间件实例:

function logger(format = ':method :url') {
  return async (ctx, next) => {
    const str = format
      .replace(':method', ctx.method)
      .replace(':url', ctx.url);
    console.log(str);
    await next();
  };
}

app.use(logger());
app.use(logger(':method :url :status'));

中间件合并技术

koa-compose包可将多个中间件合并为单一中间件:

const compose = require('koa-compose');

const middleware1 = async (ctx, next) => { /*...*/ };
const middleware2 = async (ctx, next) => { /*...*/ };

const combined = compose([middleware1, middleware2]);
app.use(combined);

条件中间件加载

根据运行环境动态加载中间件:

const conditionalMiddleware = (env) => {
  const devMiddleware = require('koa-dev-middleware');
  const prodMiddleware = require('koa-compress');
  
  return env === 'development' 
    ? devMiddleware()
    : prodMiddleware();
};

app.use(conditionalMiddleware(process.env.NODE_ENV));

中间件参数传递

通过上下文对象传递中间件间数据:

app.use(async (ctx, next) => {
  ctx.state.user = await getUser(ctx.headers.authorization);
  await next();
});

app.use(async ctx => {
  // 可以获取上游中间件设置的数据
  console.log(ctx.state.user);
});

中间件短路控制

通过next()控制流程中断:

// 权限检查中间件
app.use(async (ctx, next) => {
  if (!ctx.state.user) {
    ctx.status = 401;
    return; // 中断后续中间件执行
  }
  await next();
});

中间件组合模式实践

认证+授权组合示例

const auth = compose([
  // JWT解析
  async (ctx, next) => {
    const token = ctx.get('Authorization');
    ctx.state.user = jwt.verify(token, SECRET);
    await next();
  },
  // 权限检查
  async (ctx, next) => {
    if (!ctx.state.user.roles.includes('admin')) {
      throw new Error('Forbidden');
    }
    await next();
  }
]);

app.use(auth);

请求预处理链

const preprocess = compose([
  bodyParser(),
  cors(),
  helmet(),
  requestId()
]);

app.use(preprocess);

中间件性能优化

避免在中间件中执行重复计算:

// 反模式
app.use(async (ctx, next) => {
  const data = await expensiveOperation(); // 每次请求都执行
  ctx.state.data = data;
  await next();
});

// 优化方案
let cachedData;
app.use(async (ctx, next) => {
  if (!cachedData) {
    cachedData = await expensiveOperation(); // 仅首次执行
  }
  ctx.state.data = cachedData;
  await next();
});

中间件错误处理策略

分层错误处理方案:

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    // 业务错误
    if (err.isBusinessError) {
      ctx.status = 400;
      ctx.body = { code: err.code };
      return;
    }
    // 系统错误
    ctx.status = 500;
    ctx.app.emit('error', err, ctx);
  }
});

中间件测试方案

使用supertest测试中间件组合:

const request = require('supertest');
const app = require('../app');

describe('middleware stack', () => {
  it('should process auth middleware', async () => {
    const res = await request(app)
      .get('/')
      .set('Authorization', 'Bearer token');
    expect(res.status).toEqual(200);
  });
});

动态中间件编排

运行时动态调整中间件顺序:

function createApp(middlewares) {
  const app = new Koa();
  middlewares.forEach(mw => app.use(mw));
  return app;
}

const standardStack = [helmet(), bodyParser()];
const devStack = [...standardStack, morgan('dev')];
const prodStack = [...standardStack, compress()];

中间件元编程

通过装饰器增强中间件功能:

function measureTime(target) {
  return async function timedMiddleware(ctx, next) {
    const start = Date.now();
    await target(ctx, next);
    const duration = Date.now() - start;
    ctx.set('X-Response-Time', `${duration}ms`);
  };
}

app.use(measureTime(async (ctx) => {
  ctx.body = await fetchData();
}));

中间件与TypeScript

强类型中间件定义示例:

interface KoaContextWithUser extends Koa.Context {
  state: {
    user?: {
      id: string;
      name: string;
    };
  };
}

const authMiddleware: Koa.Middleware<KoaContextWithUser> = async (ctx, next) => {
  const user = await getUser(ctx.headers.authorization);
  ctx.state.user = user;
  await next();
};

中间件版本控制

实现API版本兼容的中间件:

function versionedMiddleware(versions) {
  return async (ctx, next) => {
    const version = ctx.get('X-API-Version') || 'v1';
    const middleware = versions[version] || versions.default;
    return middleware(ctx, next);
  };
}

app.use(versionedMiddleware({
  v1: legacyMiddleware,
  v2: modernMiddleware,
  default: modernMiddleware
}));

中间件调试技巧

使用调试命名空间定位问题:

const debug = require('debug')('app:middleware');

app.use(async (ctx, next) => {
  debug('Request start %s %s', ctx.method, ctx.url);
  const start = Date.now();
  await next();
  const duration = Date.now() - start;
  debug('Request finished in %dms', duration);
});

中间件与事件系统结合

通过事件机制扩展中间件功能:

app.use(async (ctx, next) => {
  ctx.app.emit('request_start', ctx);
  try {
    await next();
    ctx.app.emit('request_success', ctx);
  } catch (err) {
    ctx.app.emit('request_error', err, ctx);
    throw err;
  }
});

app.on('request_error', (err, ctx) => {
  console.error(`Request failed: ${ctx.url}`, err);
});

中间件执行顺序可视化

生成中间件堆栈示意图:

function inspectMiddleware(app) {
  return app.middleware.map((mw, i) => ({
    id: i,
    name: mw._name || mw.name || 'anonymous',
    toString: () => `${i}.${mw.name}`
  }));
}

console.log(inspectMiddleware(app));

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

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

前端川

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