如何将依赖注入与自定义装饰器集成

Kar*_*lam 2 dependency-injection nestjs

我正在尝试创建一个需要依赖注入的装饰器。例如:

@Injectable()
class UserService{
  @TimeoutAndCache(1000)
  async getUser(id:string):Promise<User>{
     // Make a call to db to get all Users
  }
}

Run Code Online (Sandbox Code Playgroud)

@TimeoutAndCache 返回一个新的承诺,它执行以下操作:

  1. 如果调用时间超过 1000 毫秒,则返回一个拒绝,当调用完成时,它会存储到 redis(以便下次可以获取)。
  2. 如果调用时间少于 1000 毫秒,只需返回结果
export const TimeoutAndCache = function timeoutCache(ts: number, namespace) {
  return function log(
    target: object,
    propertyKey: string,
    descriptor: TypedPropertyDescriptor<any>,
  ) {
    const originalMethod = descriptor.value; // save a reference to the original method
    descriptor.value = function(...args: any[]) {
      // pre
      let timedOut = false;
      // run and store result
      const result: Promise<object> = originalMethod.apply(this, args);
      const task = new Promise((resolve, reject) => {
        const timer = setTimeout(() => {
          if (!timedOut) {
            timedOut = true;
            console.log('timed out before finishing');
            reject('timedout');
          }
        }, ts);
        result.then(res => {
          if (timedOut) {
            // store in cache
            console.log('store in cache');
          } else {
            clearTimeout(timer);
            // return the result
            resolve(res);
          }
        });
      });
      return task;
    };
    return descriptor;
  };
};
Run Code Online (Sandbox Code Playgroud)

我需要注入一个 RedisService 来保存评估结果。我可以将 Redis Service 注入 UserService 的一种方法,但看起来有点丑陋。

Jes*_*ter 5

您应该考虑使用一个Interceptor而不是自定义装饰器,因为它们在 Nest 管道中运行较早,并且默认情况下支持依赖项注入。

但是,因为您既要传递值(用于缓存超时)又要解析依赖项,所以您必须使用该mixin模式。

import {
  ExecutionContext,
  Injectable,
  mixin,
  NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { TestService } from './test/test.service';

@Injectable()
export abstract class CacheInterceptor implements NestInterceptor {
  protected abstract readonly cacheDuration: number;

  constructor(private readonly testService: TestService) {}

  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {
    // Whatever your logic needs to be

    return call$;
  }
}

export const makeCacheInterceptor = (cacheDuration: number) =>
  mixin(
    // tslint:disable-next-line:max-classes-per-file
    class extends CacheInterceptor {
      protected readonly cacheDuration = cacheDuration;
    },
  );
Run Code Online (Sandbox Code Playgroud)

然后,您就可以以类似的方式将拦截器应用于您的处理程序:

@Injectable()
class UserService{
  @UseInterceptors(makeCacheInterceptor(1000))
  async getUser(id:string):Promise<User>{
     // Make a call to db to get all Users
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我很难让拦截器工作,即使是 [NestJS docs](https://docs.nestjs.com/interceptors) 上的简单日志拦截器也不适用于类中的任何方法。它似乎只适用于我的 @Controller 注释方法?你愿意为此添加一个测试用例吗? (3认同)
  • @jesse-carter 你从哪里了解到 `mixin` 函数的?我在文档中没有看到它(https://docs.nestjs.com) (2认同)