Nest.js学习(1)装饰器

486 阅读2分钟

在学习了一些设计模式以后,突然觉得很多知识都能够建立在这个基础体系下。也因此想再理解一下富有盛名的nestjs,正好有个项目感觉express写起来有点吃力了,计划将它升级成nest。

控制器

控制器负责处理传入的 请求 和向客户端返回 响应

控制器的目的是接收应用的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。

为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,并使 Nest 能够创建路由映射(将请求绑定到相应的控制器)。

首先说一下装饰器。装饰器是实现装饰者模式的一种方式,类似于我们在react中使用的HOC。其作用是对对象进行增强。装饰器不仅可以用于装饰函数,还可以用于装饰参数。

// nest
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
}

我们来看看nest这段代码中几个装饰器。

  • Controller:类装饰器,在类声明之前被声明,应用于类构造函数,可以监视、增强、替换类的定义;

    function Controller(prefixOrOptions) {
       const defaultPath = '/';
        ...
        return (target) => {
            Reflect.defineMetadata(constants_1.PATH_METADATA, path, target);
            Reflect.defineMetadata(constants_1.HOST_METADATA, host, target);
            Reflect.defineMetadata(constants_1.SCOPE_OPTIONS_METADATA, scopeOptions, target);
        };
    }
    

    以上这段代码节选了controller的源码,其中用到了 Reflect Metadata ,这是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。

    function classDecorator(): ClassDecorator {
      return target => {
        // 在类上定义元数据,key 为 `classMetaData`,value 为 `a`
        Reflect.defineMetadata('classMetaData', 'a', target);
      };
    }
    
    function methodDecorator(): MethodDecorator {
      return (target, key, descriptor) => {
        // 在类的原型属性 'someMethod' 上定义元数据,key 为 `methodMetaData`,value 为 `b`
        Reflect.defineMetadata('methodMetaData', 'b', target, key);
      };
    }
    
    @classDecorator()
    class SomeClass {
      @methodDecorator()
      someMethod() {}
    }
    
    Reflect.getMetadata('classMetaData', SomeClass); // 'a'
    Reflect.getMetadata('methodMetaData', new SomeClass(), 'someMethod'); // 'b'
    
  • Get:方法装饰器

    先大概看一下源码

    const createMappingDecorator = (method) => (path) => {
        return exports.RequestMapping({
            [constants_1.PATH_METADATA]: path,
            [constants_1.METHOD_METADATA]: method,
        });
    };
    const RequestMapping = (metadata = defaultMetadata) => {
       ...
        // 返回一个有三个参数的闭包函数
        return (target, key, descriptor) => {
            Reflect.defineMetadata(constants_1.PATH_METADATA, path, descriptor.value);
            Reflect.defineMetadata(constants_1.METHOD_METADATA, requestMethod, descriptor.value);
            return descriptor;
        };
    };
    
    exports.Get = createMappingDecorator(request_method_enum_1.RequestMethod.GET);
    
    

    方法装饰器会在运行时传入3个参数:

    1. 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象;
    2. 成员的名字;
    3. 成员的属性描述符;
  • Req:方法参数装饰器

    参数装饰器表达式会在运行时被调用,可以为类的原型增加一些元素数据,传入3个参数:

    1. 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象;
    2. 方法名称,如果装饰的是构造函数的参数,则值为undefined
    3. 参数在函数参数列表中的索引;