Nest.js中的设计模式——装饰器

本文介绍了Nest.js框架如何利用装饰器模式来组织代码,详细讲解了装饰器在JavaScript和TypeScript中的应用,包括参数装饰器、方法装饰器等。还探讨了如何使用Nest提供的createParamDecorator、applyDecorators以及SetMetadata和Reflector等工具来创建和操作元数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

Nest是围绕一种称为装饰器的语言特性构建的。

装饰器风格的实现

Nest在面向对象设计中用到了装饰器模式去组织代码。

装饰器模式是一种动态地往一个类别中添加新的行为的设计模式

在写项目的时候,可以说处处都是围绕着装饰器模式去进行定义的。

例如将一个类定义为一个controller,并将方法暴露为Get请求的方法是像下面这样处理:

import { Controller, Get } from '@nestjs/common';

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

而能使用装饰器,其实还是因为js提供了支持。具体细节可以看看Typescript的装饰器文档

装饰器是JavaScript 的第 2 阶段提案,可作为 TypeScript 的实验性功能使用

装饰器提供了一种为类声明和成员添加注释和元编程语法的方法。因此,很多装饰器,其实就是应用reflect-metadata包,去定义各种元数据。

Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。

因为这些js提供的语言特性,我们在使用时,就需要在tsconfig.json中配置compileOptions的参数:

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}

大多数场景下,装饰器其实都是在对元数据进行定义或者操作。例如,下面是Nest中对Http请示装饰器的逻辑代码:

const defaultMetadata = {
  [PATH_METADATA]: '/',
  [METHOD_METADATA]: RequestMethod.GET,
};

export const RequestMapping = (
  metadata: RequestMappingMetadata = defaultMetadata,
): MethodDecorator => {
  const pathMetadata = metadata[PATH_METADATA];
  const path = pathMetadata && pathMetadata.length ? pathMetadata : '/';
  const requestMethod = metadata[METHOD_METADATA] || RequestMethod.GET;

  return (
    target: object,
    key: string | symbol,
    descriptor: TypedPropertyDescriptor<any>,
  ) => {
    Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);  // 注意这里! 元数据定义
    Reflect.defineMetadata(METHOD_METADATA, requestMethod, descriptor.value); // 注意这里! 元数据定义
    return descriptor;
  };
};

之前刚了解js中使用装饰器风格的时候,自己也写过一个简答的用装饰器风格封装koa的项目:deco-brick,可以结合代码理解下这里所说的。

Typescript中的decorator

Typescript提供了五种decorator:

  • Class Decorators(类装饰器)
  • Method Decorators(类方法装饰器)
  • Parameter Decorators(参数装饰器)
  • Accessor Decorators(访问器装饰器)
  • Property Decorators(类属性装饰器)

具体还是参考Typescript Decorator文档会讲得比较清楚

但是在Nest中,只用到Class Decorators(类装饰器)、Method Decorators(类方法装饰器)、Parameter Decorators(参数装饰器)三种

export declare function applyDecorators(...decorators: Array<ClassDecorator | MethodDecorator | PropertyDecorator>): <TFunction extends Function, Y>(target: object | TFunction, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;

使用Nest提供的关于decorator的方法

创建参数装饰器——createParamDecorator

createParamDecorator是创建一个参数装饰器,提供这个方法主要的原因是我们在开发过程中,需要针对执行上下文去做一下操作,这个方法会将执行上下文传递到装饰器中。

如下:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest(); // 将执行上下文转换成express的request
    return request.user;
  },
);

组合装饰器——applyDecorators

为了逻辑代码的切分和管理,会在开发中写了多个装饰器,Nest则提供了方法去组合多个装饰器,以达到重新排练组合去复用装饰器。

import { applyDecorators } from '@nestjs/common';

export function Auth(...roles: Role[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' }),
  );
}

自定义MetaData——SetMetadata和Reflector

Nest也提供了自己去设置和获取MetaData的方法暴露出来,个人用得比较多的就是在自己编写模块的使用会用到。

下面代码是一个方法装饰器的例子

import { SetMetadata } from '@nestjs/common';

// 为一个方法设置一个监听事件名
export const OnProcessEvent = (
  eventName: string,
  eventStatus: ProcessEvent,
): MethodDecorator => SetMetadata(MY_PROCESS_EVENT, { eventName, eventStatus });

而获取设置得MetaData,怎是使用@nestjs/core中的Reflector

import { Reflector } from '@nestjs/core';

// 根据对象的方法获取配置的监听事件名
getEventMetadata(target: Type<any> | Function): {
    eventName: string;
    eventStatus: ProcessEvent;
} {
    return this.reflector.get(MY_PROCESS_EVENT, target);
}

这里的例子可以参考我的示例代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值