javascript 在Nest.js中,如何在装饰器中获取服务示例?

5uzkadbs  于 9个月前  发布在  Java
关注(0)|答案(5)|浏览(104)

CustomDecorator中,如何访问Nest.js中定义的服务示例?

export const CustomDecorator = (): MethodDecorator => {
  return (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
    ) => {

    // Here, is possibile to access a Nest.js service (i.e. TestService) instance?

    return descriptor;
  }
};

字符串

kxeu7u2r

kxeu7u2r1#

迟到了,但因为我有一个类似的问题(在装饰器中使用全局嵌套模块),并偶然发现了这个问题。

import { Inject } from '@nestjs/common';
export function yourDecorator() {
  const injectYourService = Inject(YourServiceClass);

  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    // this is equivalent to have a constructor like constructor(yourservice: YourServiceClass)
    // note that this will injected to the instance, while your decorator runs for the class constructor
    injectYourService(target, 'yourservice');

    // do something in you decorator

    // we use a ref here so we can type it
    const yourservice: YourServiceClass = this.yourservice;
    yourservice.someMethod(someParam);
  };
}

字符串

agxfikkp

agxfikkp2#

我们有几点:

  • decorated instance创建之前执行的属性装饰器。
  • Decorator希望使用some instance,由decorated instance的Injector解决。

作为一种简单的方法-使用decorated instance注入的some instance

@Injectable()
export class CatsService {
  constructor(public myService: MyService){}

  @CustomDecorator()
  foo(){}
}

export const CustomDecorator = (): MethodDecorator => {
  return (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ) => {

    const originalMethod = descriptor.value;

    descriptor.value = function () {
      const serviceInstance = this;
      console.log(serviceInstance.myService);

    }

    return descriptor;
  }
};

字符串
PS我认为它是somehow可能使用示例的注入器,以获得任何所需的示例(如角)。

hpxqektj

hpxqektj3#

我遇到了这个问题,花了一天的时间试图找出一个好的答案。这可能不适合每一个用例,但我能够复制Nest的核心包中的一个常见模式来满足我的需求。
我想创建自己的装饰器来注解控制器方法来处理事件(例如,@Subscribe('some.topic.key') async handler() { ... }))。
为了实现这一点,我的装饰器使用@nestjs/common中的SetMetadata来注册一些我需要的元数据(它所应用的方法名、它所属的类、对该方法的引用)。

export const Subscribe = (topic: string) => {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    SetMetadata<string, RabbitSubscriberMetadataConfiguration>(
      RABBITMQ_SUBSCRIBER,
      {
        topic,
        target: target.constructor.name,
        methodName: propertyKey,
        callback: descriptor.value,
      },
    )(target, propertyKey, descriptor);
  };
};

字符串
从那里,我可以创建自己的模块,它连接到Nest的生命周期钩子中,以找到我用装饰器装饰的所有方法,并对其应用一些逻辑,例如:

@Module({
  imports: [RabbitmqChannelProvider],
  providers: [RabbitmqService, MetadataScanner, RabbitmqSubscriberExplorer],
  exports: [RabbitmqService],
})
export class RabbitmqModule implements OnModuleInit {
  constructor(
    private readonly explorer: RabbitmqSubscriberExplorer,
    private readonly rabbitmqService: RabbitmqService,
  ) {}

  async onModuleInit() {
    // find everything marked with @Subscribe
    const subscribers = this.explorer.explore();
    // set up subscriptions
    for (const subscriber of subscribers) {
      await this.rabbitmqService.subscribe(
        subscriber.topic,
        subscriber.callback,
      );
    }
  }
}


explorer服务使用@nestjs/core中的一些实用程序来内省容器,并处理查找所有带元数据的修饰函数的问题。

@Injectable()
export class RabbitmqSubscriberExplorer {
  constructor(
    private readonly modulesContainer: ModulesContainer,
    private readonly metadataScanner: MetadataScanner,
  ) {}

  public explore(): RabbitSubscriberMetadataConfiguration[] {
    // find all the controllers
    const modules = [...this.modulesContainer.values()];
    const controllersMap = modules
      .filter(({ controllers }) => controllers.size > 0)
      .map(({ controllers }) => controllers);

    // munge the instance wrappers into a nice format
    const instanceWrappers: InstanceWrapper<Controller>[] = [];
    controllersMap.forEach(map => {
      const mapKeys = [...map.keys()];
      instanceWrappers.push(
        ...mapKeys.map(key => {
          return map.get(key);
        }),
      );
    });

    // find the handlers marked with @Subscribe
    return instanceWrappers
      .map(({ instance }) => {
        const instancePrototype = Object.getPrototypeOf(instance);
        return this.metadataScanner.scanFromPrototype(
          instance,
          instancePrototype,
          method =>
            this.exploreMethodMetadata(instance, instancePrototype, method),
        );
      })
      .reduce((prev, curr) => {
        return prev.concat(curr);
      });
  }

  public exploreMethodMetadata(
    instance: object,
    instancePrototype: Controller,
    methodKey: string,
  ): RabbitSubscriberMetadataConfiguration | null {
    const targetCallback = instancePrototype[methodKey];
    const handler = Reflect.getMetadata(RABBITMQ_SUBSCRIBER, targetCallback);
    if (handler == null) {
      return null;
    }
    return handler;
  }
}


我并不认为这是处理这个问题的最佳方法,但它对我来说效果很好。使用这段代码需要自担风险,它应该能让你开始:-)。我改编了这里的代码:https://github.com/nestjs/nest/blob/5.1.0-stable/packages/microservices/listener-metadata-explorer.ts

r7xajy2e

r7xajy2e4#

我从Buggy's answerflorian norbert bepunkt's answer中得到了灵感,我创建了一个装饰器,它只在装饰器中注入服务,如果它在目标中不可用。

function CustomDecorator(): MethodDecorator {
  return (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      const servicesContainer = {};

      let myServiceInstance = (this as any).myService;

      if (!myServiceInstance) {
        const injectMyService = Inject(MyService);        
        injectMyService(servicesContainer, 'myService');
        myServiceInstance = (servicesContainer as any).myService;
      }

      
      myServiceInstance.someMethod();

      originalMethod.call(this, ...args);
    };
  };
}

字符串

mspsb9vt

mspsb9vt5#

我试图在ParamDecorator中使用我的配置服务,所以我通过创建一个新的示例来访问我的服务:

export const MyParamDecorator = createParamDecorator((data, req) => {

  // ...
  const configService = new ConfigService(`${process.env.NODE_ENV || 'default'}.env`);
  const myConfigValue = configService.getMyValue();
  // ...
});

字符串

相关问题