【问题标题】:How to integrate dependency injection with custom decorators如何将依赖注入与自定义装饰器集成
【发布时间】:2019-06-14 10:24:36
【问题描述】:

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

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

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

  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;
  };
};

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

【问题讨论】:

    标签: dependency-injection nestjs


    【解决方案1】:

    您应该考虑使用 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;
        },
      );
    

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

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

    【讨论】:

    • 我很难让拦截器工作,即使NestJS docs 上的简单日志拦截器也不适用于类中的任何方法。它似乎只适用于我的 @Controller 注释方法?您是否愿意为此添加一个测试用例?
    • @jesse-carter 您是从哪里了解到mixin 函数的?我没有在文档中看到它 (docs.nestjs.com)
    猜你喜欢
    • 1970-01-01
    • 2022-08-13
    • 2016-04-04
    • 2016-10-20
    • 2016-02-08
    • 1970-01-01
    • 2013-02-06
    • 1970-01-01
    • 2016-11-29
    相关资源
    最近更新 更多