可以在TypeScript中动态添加装饰器吗?

sqxo8psd  于 2022-12-19  发布在  TypeScript
关注(0)|答案(2)|浏览(131)

我正在NestJS中开发一个个人项目,用于学习目的。目前我正在将Swagger集成到其中。因此,假设我想展示某个路由可能给予UnauthorizedException作为响应。我自然必须这样做

@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@Get()
findAll() {
  return this.usersService.findAll();
}

但是,我想把这个装饰器添加到所有非公共的路由中,所以,在一个拦截器中,我会得到当前的路由处理器和isPublic元数据,从这些中,我想决定是否把装饰器添加到处理器引用中,但是如果可能的话,我该怎么做呢?
现在,我想象拦截器是这样的。请不要考虑拦截器的名字,它不是最终的。

@Injectable()
export class UnauthSwaggerInterceptor implements NestInterceptor {
  constructor(private readonly reflector: Reflector) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const handler = context.getHandler();

    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!isPublic) {
      //  Apply Swagger decorator to handler
    }

    return next.handle();
  }
}

因此,如图所示,我将首先获取对当前处理程序的引用,该处理程序的类型为Function
然后,使用reflector从路由中获取isPublic元数据。
然后,验证路由是否是公共的,以便应用装饰器。我使用公共装饰器将路由标记为公共的。

export const IS_PUBLIC = 'isPublic';

export const Public = () => SetMetadata(IS_PUBLIC, true);

所以,我想把前面提到的Unauthorized response的decorator附加到这个特定的非公共处理器上。有可能这样做吗?因为它不是函数声明,它是一个将在运行时获得的引用。在肯定的情况下,那么正确的操作过程是什么?
我不知道是否有一种方法可以直接将装饰器应用到函数中。虽然装饰器只能应用到类的上下文中,但我想只将它应用到控制器方法中。据我所知,Nest的Swagger插件基本上就是这样做的,它根据特定的规则动态地添加装饰器。如果可能的话,这也是我想实现的。

hmtdttj4

hmtdttj41#

在运行时尝试更新装饰器是没有意义的。装饰器的存在是为了在服务器启动时,可以在运行时执行或读取某些操作。对于swagger装饰器,这些都是在服务器启动时通过SwaggerModulecreateDocument方法读取的。即使您在请求时更改它们,您不会修改当前的swagger,并且它在服务器重启后也不会持续。

2exbekwf

2exbekwf2#

装饰器只是一个被装饰者调用的函数(尽管这里我们有TS手册中所称的装饰器工厂):

function Decorator(s: string) {
  return (target: any) => {
    console.log("Decorating", target);
    target.foo = s;
    return target;
  };
}

// Decorator syntax
@Decorator("wibba wubba")
class SomeClass {}

// Call syntax
const OtherClass = Decorator("webbo wobbo")(class OtherClass {});

console.log((SomeClass as any).foo);
console.log((OtherClass as any).foo);

这个打印出来

"wibba wubba" 
"webbo wobbo"

正如你所想象的。
哦,

@Injectable()
class Foo {}

约等于

const Foo = Injectable()(class Foo{});

你可以想象用一个变量代替Injectable()保存一个函数。

相关问题