likes
comments
collection
share

NestJS SetMetadata 装饰器的实现和用法

作者站长头像
站长
· 阅读数 36

前言

SetMetadata 是 Nest.js 内置的一个装饰器方法,它用于为路由方法添加元数据。

本文涉及到两个知识点,装饰器和反射元数据。装饰器本质就是一个函数,可以用来修饰类,成员属性和方法等。反射元数据提供者了一些用来操作元数据的方法,现在主要使用社区实现的模块 reflect-metadata。之前也有文章对做过介绍,如果还不了解,可以先看一下。

SetMetadata

SetMetadata 是 Nest 内置的一个装饰器,作用是给控制器类和类的成员方法设置元数据。

先来看下 SetMetadata 的源码:

// 自定义装饰器的类型
export type CustomDecorator<TKey = string> = MethodDecorator &
  ClassDecorator & {
    KEY: TKey;
};
// 给类和成员方法设置元数据,内部使用了 reflect-metadata 提供的 Reflect.defineMetadata 方法
export const SetMetadata = <K = string, V = any>(
  metadataKey: K,
  metadataValue: V,
): CustomDecorator<K> => {
  const decoratorFactory = (target: object, key?: any, descriptor?: any) => {
    if (descriptor) { 
      Reflect.defineMetadata(metadataKey, metadataValue, descriptor.value);
      return descriptor;
    }
    Reflect.defineMetadata(metadataKey, metadataValue, target);
    return target;
  };
  decoratorFactory.KEY = metadataKey;
  return decoratorFactory;
};

阅读源码可知,SetMetadata 的主要作用就是指定的 key 给类和函数注入元数据的装饰器。它接收两个参数,分别是描述元数据的 key 和元数据值。然后创建一个装饰器工厂函数 decoratorFactory

前文讲装饰器时说过,装饰器可以用在类和方法上,两种装饰器所接收的参数是不同的。类装饰器只接收类一个参数,方法装饰器可以接收类/类的原型对象,方法名和方法描述符三个参数。

decoratorFactory 方法内部会根据是否有方法描述符来判断此时是类装饰器还是方法装饰器。如果是方法装饰器,直接将元数据注入到方法本身。如果是类装饰器,则将元数据定义到类身上。设置元数据利用的是 reflect-metadata 包提供的 Reflect.defineMetadata 方法。

最后,将元数据标识 key 绑定到装饰器的 KEY 属性。这样,反射器就能根据 KEY 来访问到使用 SetMetadata 注入的元数据。

SetMetadata 的用法

按照文档中守卫部分的介绍,可以使用 @SetMetadata() 装饰器来为路由处理方法设置元数据。比如:

@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

根据上面源码的介绍,可以这里为 create 方法设置了元数据,key 为 roles,值是一个数组 ['admin']

之后在守卫中,就可以利用 Reflector 根据 key 读取出用户的角色信息,比如在下面这个 RolesGuard 守卫中:

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private reflector: Reflector) {}
    
  canActivate(context: ExecutionContext): Promise<boolean> {
    // 使用 Reflector 获取路由方法的 role 元数据
    const roles = this.reflector.getAllAndMerge('roles', [context.getHandler(), context.getClass()]);
    console.log(roles)
    
    return true
  }
}

总结

本文介绍了 NestJS 中内置的 SetMetadata 装饰器的实现和用法,它是对 reflect-metadata 提供的方法的封装,主要用来为路由方法添加元数据。使用 SetMetadata 设置的元数据,可以使用 Reflector 反射器读取元数据。