乐闻世界logo
搜索文章和话题
NestJS 如何在装饰器中获取 Service 实例?

NestJS 如何在装饰器中获取 Service 实例?

乐闻的头像
乐闻

2024年12月08日 01:55· 阅读 51

前言

在现代Web开发中,NestJS以其模块化和可扩展性受到了广大开发者的青睐。装饰器作为NestJS核心特性之一,能够以简洁的语法增强应用的功能。然而,在实际开发中,我们常常需要在装饰器中访问服务实例,以实现更复杂的业务逻辑。

那么,如何在NestJS装饰器中高效、安全地获取服务实例呢?本文将通过详细的步骤和示例,实现这一目标,以提升代码的可维护性和可扩展性。

准备工作

在深入探讨之前,确保你已经了解以下概念:

  1. NestJS 基础:包括模块、控制器、服务等。
  2. 装饰器:理解什么是装饰器以及如何在 TypeScript 中使用它们。

实现步骤

1. 创建一个自定义装饰器

首先,我们需要创建一个简单的自定义装饰器。装饰器其实就是一个函数,可以在类、方法或属性上使用。

TypeScript
import { SetMetadata } from '@nestjs/common'; export const CustomDecorator = (...args: any[]) => SetMetadata('custom', args);

2. 使用 Reflector 获取元数据

Reflector 是 NestJS 提供的一个工具类,用于获取装饰器的元数据。首先,创建一个拦截器或 Guard 来处理装饰器。

TypeScript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class CustomGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const customData = this.reflector.get<string[]>('custom', context.getHandler()); console.log('Custom Data:', customData); // 逻辑处理... return true; } }

3. 在装饰器中获取 Service 实例

要在装饰器中获取 Service 实例,我们可以使用依赖注入(Dependency Injection)。我们将需要的服务先注入到一个全局的模块中。

TypeScript
import { Module, Global } from '@nestjs/common'; import { CustomService } from './custom.service'; @Global() @Module({ providers: [CustomService], exports: [CustomService], }) export class CustomModule {}

在任何需要使用该装饰器的地方导入 CustomModule

4. 创建装饰器工厂函数

我们需要一个工厂函数来创建装饰器,并在其中获取 Service 实例。

TypeScript
import { Inject, Injectable, createParamDecorator, ExecutionContext } from '@nestjs/common'; import { CustomService } from './custom.service'; @Injectable() export class CustomDecoratorFactory { constructor(private readonly customService: CustomService) {} createCustomDecorator() { return createParamDecorator( (data: unknown, ctx: ExecutionContext) => { console.log(this.customService.getSomeData()); return this.customService.getSomeData(); }, )(); } }

5. 使用装饰器工厂

在你的控制器中使用这个装饰器工厂。

TypeScript
import { Controller, Get } from '@nestjs/common'; import { CustomDecoratorFactory } from './custom-decorator.factory'; @Controller('example') export class ExampleController { constructor(private readonly customDecoratorFactory: CustomDecoratorFactory) {} @Get() @this.customDecoratorFactory.createCustomDecorator() findAll() { return 'This action returns all items'; } }

优化手段

1. 改进装饰器工厂函数

在之前的实现中,我们已经通过装饰器工厂函数成功获取了 Service 实例。现在我们可以对装饰器工厂函数进行一些改进,使其更加灵活和可配置。

TypeScript
import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export function CustomDecorator(data?: any): ParameterDecorator { return createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); const customService = request.customService; // 从请求中获取服务实例 console.log(customService.getSomeData()); return customService.getSomeData(); }, )(data); }

2. 在请求中注入 Service 实例

为了确保我们的 Service 实例可以在请求中使用,我们可以创建一个中间件将 Service 实例注入到请求中。

TypeScript
import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { CustomService } from './custom.service'; @Injectable() export class CustomMiddleware implements NestMiddleware { constructor(private readonly customService: CustomService) {} use(req: Request, res: Response, next: NextFunction) { req.customService = this.customService; next(); } }

然后在你的模块中应用这个中间件:

TypeScript
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common'; import { CustomMiddleware } from './custom.middleware'; import { ExampleController } from './example.controller'; import { CustomService } from './custom.service'; @Module({ controllers: [ExampleController], providers: [CustomService], }) export class ExampleModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(CustomMiddleware) .forRoutes(ExampleController); } }

3. 在控制器中使用改进后的装饰器

现在,我们可以在控制器中使用改进后的自定义装饰器:

TypeScript
import { Controller, Get } from '@nestjs/common'; import { CustomDecorator } from './custom.decorator'; @Controller('example') export class ExampleController { @Get() @CustomDecorator() findAll(@CustomDecorator() customData: any) { return `This action returns all items with custom data: ${customData}`; } }

高级使用场景

1. 基于角色的访问控制

在实际项目中,我们可能需要基于用户角色进行访问控制。可以使用自定义装饰器来实现这一功能。

TypeScript
import { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

然后创建一个守卫来处理角色验证:

TypeScript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const roles = this.reflector.get<string[]>('roles', context.getHandler()); if (!roles) { return true; } const request = context.switchToHttp().getRequest(); const user = request.user; return roles.some(role => user.roles?.includes(role)); } }

在模块中注册守卫:

TypeScript
import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { RolesGuard } from './roles.guard'; import { ExampleController } from './example.controller'; @Module({ controllers: [ExampleController], providers: [ { provide: APP_GUARD, useClass: RolesGuard, }, ], }) export class ExampleModule {}

在控制器中使用 Roles 装饰器:

TypeScript
import { Controller, Get } from '@nestjs/common'; import { Roles } from './roles.decorator'; @Controller('example') export class ExampleController { @Get() @Roles('admin') findAll() { return 'This action returns all items for admins'; } }

2. 日志记录

我们可以使用装饰器来简化日志记录的工作:

TypeScript
import { createParamDecorator, ExecutionContext, Injectable } from '@nestjs/common'; import { CustomService } from './custom.service'; @Injectable() export class LoggingDecoratorFactory { constructor(private readonly customService: CustomService) {} createLoggingDecorator() { return createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); console.log(`[${new Date().toISOString()}] Request URL: ${request.url}`); return this.customService.getSomeData(); }, )(); } }

在控制器中使用日志装饰器:

TypeScript
import { Controller, Get } from '@nestjs/common'; import { LoggingDecoratorFactory } from './logging-decorator.factory'; @Controller('') export class ExampleController { constructor(private loggingDecoratorFactory: LoggingDecoratorFactory) @Get() @this.loggingDecoratorFactory.createLoggingDecorator() findAll() { return 'This action returns all items'; } }

总结

通过本文的详细讲解,我们深入探讨了如何在NestJS的装饰器中获取服务实例,并进一步探索了基于角色的访问控制和日志记录等高级应用场景。这不仅增强了装饰器的功能,也提升了应用的灵活性和可维护性。希望通过本文的讲解,你能更好地掌握NestJS装饰器的使用技巧,并在实际项目中充分发挥其强大功能。

标签: