在装饰器中使用全局嵌套模块

Posted

技术标签:

【中文标题】在装饰器中使用全局嵌套模块【英文标题】:Use global nest module in decorator 【发布时间】:2020-06-20 00:45:01 【问题描述】:

我在嵌套中有一个全局记录器模块,它记录到云记录服务。我正在尝试创建一个添加日志记录功能的类方法装饰器。但是我正在努力如何在装饰器中注入全局嵌套模块的服务,因为我在文档中找到的所有依赖注入机制都依赖于基于类或类属性的注入。

export function logDecorator() 

  // I would like to inject a LoggerService that is a provider of a global logger module
  let logger = ???

  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => 
    //get original method
    const originalMethod = propertyDescriptor.value;

    //redefine descriptor value within own function block
    propertyDescriptor.value = function(...args: any[]) 
      logger.log(`$propertyKey method called with args.`);
      
      //attach original method implementation
      const result = originalMethod.apply(this, args);

      //log result of method
      logger.log(`$propertyKey method return value`);
    ;
  ;

更新:根据要求提供一个简单示例 基本示例是使用我的自定义记录器(在我的情况下记录到云服务)记录对服务方法的调用:

class MyService 
    @logDecorator()
    someMethod(name: string) 
        // calls to this method as well as method return values would be logged to CloudWatch
        return `Hello $name`
    

另一个扩展用例是捕获一些错误,然后记录它们。我有很多这种逻辑可以在我的所有服务中重复使用。

【问题讨论】:

你能举个例子来说明你想如何使用这个装饰器吗?我想我知道你想要做什么,但不是 100% 确定。 @JayMcDoniel:当然,我用一个简单的例子更新了问题 【参考方案1】:

好的,找到了解决方案。以防其他人偶然发现这一点。首先请记住装饰器是如何工作的——它们是基于类构造函数的,而不是基于实例的。

在我的例子中,我想让我的记录器服务注入到类实例中。所以解决方法是告诉装饰器中的Nest,将LoggerService注入到包含被装饰方法的类的实例中。

import  Inject  from '@nestjs/common';
import  LoggerService  from '../../logger/logger.service';

export function logErrorDecorator(bubble = true) 
  const injectLogger = Inject(LoggerService);

  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => 
    injectLogger(target, 'logger'); // this is the same as using constructor(private readonly logger: LoggerService) in a class

    //get original method
    const originalMethod = propertyDescriptor.value;

    //redefine descriptor value within own function block
    propertyDescriptor.value = async function(...args: any[]) 
      try 
        return await originalMethod.apply(this, args);
       catch (error) 
        const logger: LoggerService = this.logger;

        logger.setContext(target.constructor.name);
        logger.error(error.message, error.stack);

        // rethrow error, so it can bubble up
        if (bubble) 
          throw error;
        
      
    ;
  ;

这提供了在方法中捕获错误、在服务上下文中记录它们并重新抛出它们(以便您的控制器可以处理用户响应)或不重新抛出它们的可能性。就我而言,我还必须在这里实现一些与事务相关的逻辑。

export class FoobarService implements OnModuleInit 
  onModuleInit() 
    this.test();
  

  @logErrorDecorator()
  test() 
    throw new Error('Oh my');
  

【讨论】:

当我使用这种方法时,服务最终在描述值函数中未定义。就我而言,我正在使用的服务还依赖于其他两个服务,因此这可能会产生影响。这个解决方案可以用于注入其他服务的服务吗? @vinnymac Oi,您是否发现是什么导致您的服务未定义?好像我在这里遇到了同样的问题。尽管当我在服务类中使用这个装饰器时它工作得很好,但是当我在不同的类中使用它时,注入的服务会变成未定义的,在工厂中,准确地说。我试过让它可注射,但没有帮助。 嘿@Albert,你找到导致你的服务未定义的原因了吗? @hikvineh 嗨,是的,我有,但我不记得它到底是什么,但这只是一些小事,在我修复它之后,一切都按预期工作,所以是的,我确认答案中描述的解决方案有效!而且我注入到我的装饰器中的这个服务也注入了一堆其他的依赖项,所以如果你的服务有注入其他服务并不重要,一切都按预期工作。

以上是关于在装饰器中使用全局嵌套模块的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器

第十五篇:装饰器

Python 装饰器

如何使用来自@nestjs/mongoose 的@Prop 装饰器添加嵌套的对象数组

装饰器

Python中的闭包