SSF0SSF0
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
  • Node.js

    • 在 Docker 容器中运行 Node.js Koa 应用的网络配置与常见问题
    • 在 Node.js 后端实现邮箱验证码功能
    • Redis 与 JWT 结合的 Token 验证方案
    • Node.js 后端三层架构中的错误处理最佳实践
    • 在 Koa 中批量设置路由 JWT 校验的最佳实践
    • 前端开发者的 Redis 入门指南
    • AI工具系统架构设计
    • Koa 项目中间件设计技巧与最佳实践
  • Go

    • Go1
    • Go2
  • Python

    • Python 项目 API 代理转发:解决跨平台外网访问难题
  • MySql

    • 数据库连接

Koa 项目中间件设计技巧与最佳实践

1. 应用级中间件 vs 路由级中间件

类型差异

在 Koa 项目中,应用级中间件和路由级中间件接收的 Context 类型是不同的:

// 应用级中间件 - 标准 Koa Context
import { Context, Next } from "koa";
export const appMiddleware = async (ctx: Context, next: Next) => {
  // ctx 是标准的 koa Context
};

// 路由级中间件 - 增强的 Router Context
import { IMiddleware } from "koa-router";
export const routeMiddleware: IMiddleware = async (ctx, next) => {
  // ctx 包含路由信息:params, router, _matchedRoute 等
  console.log("路由参数:", ctx.params);
  console.log("匹配的路由:", ctx._matchedRoute);
};

适用场景

  • 应用级中间件:全局功能(日志、错误处理、全局速率限制)
  • 路由级中间件:特定路由的精细控制(权限检查、参数验证)

2. 中间件工厂模式设计

为什么使用工厂模式?

工厂模式允许同一个中间件根据不同参数做出不同处理,提高代码复用性:

// 路由级中间件工厂模式 - 灵活配置
export const authMiddleware = (
  options: {
    required?: boolean;
    roles?: string[];
  } = {}
) => {
  return async (ctx: RouterContext, next: Next) => {
    const { required = true, roles = [] } = options;

    // 根据参数做不同处理
    if (required && !token) {
      throw new AuthenticationError("缺少令牌");
    }

    if (!required && !token) {
      return await next(); // 可选认证,继续执行
    }

    if (roles.length > 0) {
      // 检查角色权限
      const hasPermission = await checkUserRoles(userId, roles);
      if (!hasPermission) {
        throw new AuthorizationError("权限不足");
      }
    }

    await next();
  };
};

使用示例

// 路由配置中的灵活使用
const routes = [
  {
    path: "/public",
    method: "GET",
    controller: "PublicController",
    action: "getData",
    middleware: [authMiddleware({ required: false })], // 可选认证
  },
  {
    path: "/admin",
    method: "GET",
    controller: "AdminController",
    action: "dashboard",
    middleware: [authMiddleware({ roles: ["admin"] })], // 需要管理员权限
  },
  {
    path: "/user",
    method: "GET",
    controller: "UserController",
    action: "profile",
    middleware: [authMiddleware()], // 必须认证(默认配置)
  },
];

// 路由注册器
class RouteRegistry {
  private router: Router;

  constructor() {
    this.router = new Router();
  }

  // 注册路由配置
  registerRoutes(routeConfigs: RouteConfig[]): Router {
    routeConfigs.forEach((route) => {
      const handlers = [
        ...route.middleware, // 应用中间件
        async (ctx: RouterContext, next: Next) => {
          // 控制器处理逻辑
          const controller = getController(route.controller);
          await controller[route.action](ctx, next);
        },
      ];

      // 动态注册路由
      this.router[route.method.toLowerCase()](route.path, ...handlers);
    });

    return this.router;
  }
}

// 使用示例
const app = new Koa();
const registry = new RouteRegistry();
const router = registry.registerRoutes(routes);

app.use(router.routes());
app.use(router.allowedMethods());

3. 预设中间件模式

为了平衡灵活性和便利性,可以提供常用的预设版本:

// 工厂函数
export const authMiddleware = (options: AuthOptions = {}) => {
  /* ... */
};

// 预设版本 - 常用配置
export const requireAuth: IMiddleware = authMiddleware();
export const optionalAuth: IMiddleware = authMiddleware({ required: false });
export const adminOnly: IMiddleware = authMiddleware({ roles: ["admin"] });
export const userOrAdmin: IMiddleware = authMiddleware({
  roles: ["user", "admin"],
});

// 使用时更简洁
middleware: [requireAuth]; // 而不是 authMiddleware()

4. 中间件类型兼容性处理

问题场景

当项目中定义路由级中间件类型时,会遇到类型不兼容问题:

// 错误:类型不匹配
interface RouteConfig {
  middleware: IMiddleware[]; // 期望路由中间件
}

const routes = [
  {
    middleware: [rateLimitMiddleware()], // 实际是应用级中间件
  },
];

解决方案

方案 1:分层设计

// 应用级中间件
export const appRateLimitMiddleware = (options) => {
  return async (ctx: Context, next: Next) => {
    /* ... */
  };
};

// 路由级中间件
export const routeRateLimitMiddleware = (options): IMiddleware => {
  return async (ctx: RouterContext, next: Next) => {
    // 可以利用路由信息做更精确的限制
    const key = `rate_limit:${ctx.ip}:${ctx._matchedRoute}`;
    // ...
  };
};

5. 中间件职责分离

全局中间件 vs 路由中间件

正确的职责分离能避免类型冲突:

// ❌ 错误:同一个中间件既用于全局又用于路由
app.use(rateLimitMiddleware({ max: 100 })); // 应用级使用
routes: [{ middleware: [rateLimitMiddleware({ max: 10 })] }]; // 路由级使用

// ✅ 正确:职责分离
// 全局速率限制
app.use(globalRateLimitMiddleware({ max: 100 }));

// 特定路由的精细限制(如果需要)
routes: [{ middleware: [routeSpecificMiddleware] }];

6. 最佳实践建议

1. 命名约定

// 应用级中间件
export const appAuthMiddleware = () => {
  /* ... */
};
export const globalRateLimitMiddleware = () => {
  /* ... */
};

// 路由级中间件
export const routeAuthMiddleware: IMiddleware = () => {
  /* ... */
};
export const paramValidationMiddleware: IMiddleware = () => {
  /* ... */
};

2. 文件组织

middleware/
  ├── app/           # 应用级中间件
  │   ├── auth.ts
  │   ├── rate-limit.ts
  │   └── logging.ts
  ├── route/         # 路由级中间件
  │   ├── validation.ts
  │   ├── permissions.ts
  │   └── cache.ts
  └── index.ts       # 统一导出

3. 类型安全

// 定义明确的中间件接口
export interface AppMiddlewareFactory<T = any> {
  (options?: T): Middleware;
}

export interface RouteMiddlewareFactory<T = any> {
  (options?: T): IMiddleware;
}

// 实现时明确类型
export const authMiddleware: AppMiddlewareFactory<AuthOptions> = (options) => {
  return async (ctx: Context, next: Next) => {
    /* ... */
  };
};

7. 总结

  • 工厂模式提供了中间件的灵活配置能力

  • 预设模式在保持灵活性的同时提供了便利性

  • 正确的类型处理避免了开发时的类型错误

  • 职责分离让应用架构更清晰,避免类型冲突

通过这些设计技巧,可以构建出既灵活又类型安全的 Koa 中间件系统。

最后更新时间:
贡献者: 何风顺
上一页
AI工具系统架构设计