Ecosystem
ArtusX
Plugins
koa

@artusx/plugin-koa

https://github.com/koajs/koa (opens in a new tab)

插件基于 koa 封装,默认集成 koa-router,并添加 controller / middleware / http method 等注解能力,完全兼容 koa 生态。

概览

在框架各生命周期执行如下操作:

  • willReady

    • 加载 middleware,注册 ArtusX 中间件
    • 加载 controller,注册路由以及函数中间件
  • didReady

    • 加载 koa-router,挂载插件中注册的路由
    • 启动 http 服务:koa.listen(port: number)

当服务接收到请求后,先执行应用自定义声明周期 willReady 中,用户拓展的 koa 中间件(参考进阶章节);请求到达 koa-router,执行 ArtusX,并将结果交给 controller,由具体的 handler 完成最终处理。

配置

配置插件

keys 通过 KOA_KEYS 环境变量传入。

iTerm
const key_keys = process.env.KOA_KEYS?.split(',') ?? ['artusx'];

port 默认配置为 7001,可通过 config 修改。

config.default.ts
export default {
  koa: {
    port: 7001,
  },
  artusx: {
    // 类中间件支持顺序加载,不配置则按 loader 加载顺序依次执行。
    // middlewares: [],
  },
};

启用插件

core 插件已默认集成,无需单独配置;如需单独使用,可通过如下方式开启插件。

plugin.ts
export default {
  koa: {
    enable: true,
    package: '@artusx/plugin-koa',
  },
};

内置注解

@Middleware

类中间件

ℹ️

类中间件为全局中间件,可通过 config.artusx.middlewares 控制执行顺序,否则根据加载顺序执行。

middleware/limitRate.ts
import { ArtusInjectEnum, Inject } from '@artus/core';
import { ArtusXContext, ArtusXNext, Middleware } from '@artusx/plugin-koa';
 
import { RateLimiterMemory } from 'rate-limiter-flexible';
 
const rateLimiterOptions = {
  points: 6,
  duration: 1,
};
 
@Middleware({
  enable: true,
})
export default class LimitRateMiddleware {
  @Inject(ArtusInjectEnum.Config)
  config: Record<string, string | number>;
 
  private rateLimiter: RateLimiterMemory;
 
  constructor() {
    this.rateLimiter = new RateLimiterMemory(rateLimiterOptions);
  }
 
  async use(ctx: ArtusXContext, next: ArtusXNext): Promise<void> {
    try {
      const rateLimiterRes = await this.rateLimiter.consume(ctx.ip);
      ctx.set('Retry-After', `${rateLimiterRes.msBeforeNext / 1000}`);
      ctx.set('X-RateLimit-Limit', `${rateLimiterOptions.points}`);
      ctx.set('X-RateLimit-Remaining', `${rateLimiterRes.remainingPoints}`);
      ctx.set('X-RateLimit-Reset', `${new Date(Date.now() + rateLimiterRes.msBeforeNext)}`);
    } catch (rejRes) {
      ctx.status = 429;
      ctx.body = 'Too Many Requests';
      return;
    }
    await next();
  }
}

函数中间件

middleware/traceTime.ts
import { ArtusXContext, ArtusXNext } from '@artusx/plugin-koa';
 
export default async function traceTime(_ctx: ArtusXContext, next: ArtusXNext): Promise<void> {
  console.time('trace');
  await next();
  console.timeEnd('trace');
}

@Controller

此外插件提供了 web服务的常用注解:

  • @GET: (path: string)
  • @PUT: (path: string)
  • @POST: (path: string)
  • @HEAD: (path: string)
  • @PATCH: (path: string)
  • @DELETE: (path: string)
  • @OPTIONS: (path: string)
  • @MW: (middlewares: ArtusXHandler[])
controller/home.ts
import { Controller, GET, POST, MW } from '@artusx/plugin-koa';
import type { ArtusXContext } from '@artusx/plugin-koa';
import traceTime from '../middleware/traceTime';
 
@Controller()
export default class HomeController {
  @MW([traceTime])
  @GET('/can-be-get')
  @POST('/post')
  async home(ctx: ArtusXContext) {
    ctx.body = 'Hello World';
  }
}

进阶使用

自定义 lifecycle.ts

如内置 koa 无法满足需求,可通过自定义 LifecycleHook ,在应用启动前,对 koa 进行拓展。

lifecycle.ts
import {
  Inject,
  ArtusInjectEnum,
  ArtusApplication,
  ApplicationLifecycle,
  LifecycleHook,
  LifecycleHookUnit,
} from '@artus/core';
 
import { KoaApplication } from '@artusx/core';
 
@LifecycleHookUnit()
export default class CustomLifecycle implements ApplicationLifecycle {
  @Inject(ArtusInjectEnum.Application)
  private readonly app: ArtusApplication;
 
  get koa(): KoaApplication {
    return this.app.container.get(KoaApplication);
  }
 
  @LifecycleHook()
  async didLoad() {}
 
  @LifecycleHook()
  public async willReady() {
    // extend koa
    this.koa.use(async (_ctx, next) => {
      // do something
      await next();
    });
  }
}