nestjs之策略模式的应用

这篇具有很好参考价值的文章主要介绍了nestjs之策略模式的应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

策略模式(Strategy Pattern)是一种软件设计模式,它定义了算法族,分别封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。这种模式涉及到三个角色:

  1. 上下文(Context):持有一个策略类的引用,用来与策略类交互。
  2. 策略接口(Strategy Interface):定义了每个策略或算法必须遵循的接口。
  3. 具体策略(Concrete Strategies):实现策略接口的类,提供具体的算法实现。

策略模式的优点

  • 分离算法:策略模式通过分离算法和上下文来提高内聚性和灵活性。
  • 易于扩展:可以定义新的策略而不影响到其他的代码。
  • 避免条件语句:策略模式避免了使用多重条件选择语句。

举例说明

假设我们正在开发一个导航系统,需要支持多种路线规划算法,例如最短路径、最少时间和避开高速。
当使用 TypeScript 来实现策略模式时,基本的模式和概念与 Java 类似,只是语法稍有不同。以下是使用 TypeScript 实现策略模式的示例:

首先,定义策略接口和具体策略类:

// 策略接口
interface RouteStrategy {
  buildRoute(pointA: string, pointB: string): string;
}

// 具体策略类 - 最短路径策略
class ShortestPathStrategy implements RouteStrategy {
  buildRoute(pointA: string, pointB: string): string {
    return `最短路径从 ${pointA}${pointB}`;
  }
}

// 具体策略类 - 最少时间策略
class MinTimeStrategy implements RouteStrategy {
  buildRoute(pointA: string, pointB: string): string {
    return `最少时间路径从 ${pointA}${pointB}`;
  }
}

// 具体策略类 - 避开高速策略
class AvoidHighwaysStrategy implements RouteStrategy {
  buildRoute(pointA: string, pointB: string): string {
    return `避开高速的路径从 ${pointA}${pointB}`;
  }
}

接下来,创建上下文类和使用策略模式:

// 上下文类
class NavigationContext {
  private strategy: RouteStrategy;

  setRouteStrategy(strategy: RouteStrategy): void {
    this.strategy = strategy;
  }

  buildRoute(pointA: string, pointB: string): string {
    return this.strategy.buildRoute(pointA, pointB);
  }
}

// 使用策略模式
const context = new NavigationContext();

// 选择最短路径策略
context.setRouteStrategy(new ShortestPathStrategy());
console.log(context.buildRoute("起点", "终点"));

// 切换到最少时间策略
context.setRouteStrategy(new MinTimeStrategy());
console.log(context.buildRoute("起点", "终点"));

// 切换到避开高速策略
context.setRouteStrategy(new AvoidHighwaysStrategy());
console.log(context.buildRoute("起点", "终点"));

在这个例子中,RouteStrategy 是策略接口,ShortestPathStrategyMinTimeStrategyAvoidHighwaysStrategy 是实现了不同路线规划算法的具体策略类。NavigationContext 是上下文,负责接受不同的策略并使用它们来构建路线。这样,当需要改变路线规划算法时,只需更换不同的策略类即可,无需修改 NavigationContext 的代码。

理解和运用这些设计模式可以帮助你更有效地使用 NestJS 构建可维护和可扩展的应用程序。

在 NestJS 中,策略模式主要用于以下几个领域:

Authentication

在 NestJS 中实现策略模式主要是通过 Guards 来完成的,特别是在处理授权(Authorization)时。我们可以通过定义不同的 Guards 来实现不同的授权策略,例如基于角色的授权(RBAC)或者是基于权限的授权。

示例:基于角色的访问控制(RBAC)

假设我们有一个简单的应用程序,它有两种用户角色:普通用户(User)和管理员(Admin)。我们想要实现的是,某些操作只能由管理员执行。

步骤 1:定义角色

首先,我们定义一个角色枚举(Enum)。

export enum Role {
  User = 'user',
  Admin = 'admin',
}
步骤 2:创建 Roles 装饰器

接下来,我们创建一个自定义装饰器来标记特定的路由需要特定的角色。

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

export const Roles = (...roles: Role[]) => SetMetadata('roles', roles);
步骤 3:实现 RolesGuard

然后,我们实现一个 RolesGuard,它将检查用户是否具有访问特定路由所需的角色。

import { CanActivate, ExecutionContext, Injectable } 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<Role[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return roles.some(role => user.roles?.includes(role));
  }
}

在这个 Guard 中,我们使用 Reflector 来获取与当前路由处理程序相关联的角色。然后,我们检查当前用户是否具有这些角色之一。

步骤 4:应用 RolesGuard

最后,我们需要在模块中注册这个 Guard 并应用到具体的路由上。

// 在模块中
@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}

// 在控制器中
@Controller('items')
export class ItemsController {
  @Get()
  @Roles(Role.Admin) // 只有管理员可以访问
  findAll() {
    // ...
  }
}

在这个例子中,RolesGuard 作为一个策略,用于控制对特定路由的访问。通过简单地更改 @Roles 装饰器中的参数,我们可以轻松地改变访问控制的策略,而无需修改其他业务逻辑。

Logging

在 NestJS 中实现日志策略模式通常涉及到自定义日志服务。这样,你可以根据需要切换或扩展不同的日志策略,例如输出日志到控制台、文件、远程服务器等。

示例:自定义日志服务

假设我们需要实现两种日志策略:一种是简单地将日志输出到控制台,另一种是将日志记录到文件中。

步骤 1:定义日志接口

首先,我们定义一个日志接口(LoggerService),它描述了日志服务应该实现的方法。

interface LoggerService {
  log(message: string): void;
  error(message: string, trace: string): void;
  warn(message: string): void;
  debug(message: string): void;
  verbose(message: string): void;
}
步骤 2:实现具体的日志策略

接下来,我们实现两个具体的日志策略,分别是 ConsoleLoggerFileLogger

@Injectable()
class ConsoleLogger implements LoggerService {
  log(message: string) { console.log(message); }
  // ...实现其他方法
}

@Injectable()
class FileLogger implements LoggerService {
  log(message: string) {
    // 将消息写入文件
  }
  // ...实现其他方法
}
步骤 3:动态选择日志策略

然后,我们可以根据需要在应用中动态选择使用哪个日志策略。

@Module({
  providers: [
    {
      provide: 'LoggerService',
      useClass: process.env.NODE_ENV === 'development' ? ConsoleLogger : FileLogger,
    },
  ],
})
export class AppModule {}

在这个模块中,我们根据环境变量来决定使用 ConsoleLogger 还是 FileLogger

步骤 4:使用日志服务

在应用的其他部分,我们可以注入并使用 LoggerService

@Controller('items')
export class ItemsController {
  constructor(@Inject('LoggerService') private logger: LoggerService) {}

  @Get()
  findAll() {
    this.logger.log('Fetching all items');
    // ...业务逻辑
  }
}

在这个控制器中,我们通过构造函数注入了 LoggerService。不论底层使用的是哪种日志策略,我们都可以通过相同的方式记录日志。

Exception Handling

在 NestJS 中,异常处理通常通过异常过滤器(Exception Filters)来实现,这可以被视为一种策略模式的应用。异常过滤器允许你定义不同的处理策略来处理不同类型的异常。以下是一个详细的例子,展示如何在 NestJS 中使用异常过滤器来实现异常处理的策略模式。

示例:自定义异常过滤器

假设我们的应用需要特定的处理方式来处理数据库异常和 HTTP 异常。

步骤 1:创建自定义异常过滤器

首先,我们创建两个异常过滤器,一个用于处理数据库异常,另一个用于处理 HTTP 异常。

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        message: exception.message,
      });
  }
}

@Catch(DatabaseException)
export class DatabaseExceptionFilter implements ExceptionFilter {
  catch(exception: DatabaseException, host: ArgumentsHost) {
    // 处理数据库异常的逻辑
  }
}
步骤 2:注册异常过滤器

接下来,我们需要在应用中注册这些异常过滤器。你可以全局注册或针对特定控制器或路由注册。

  • 全局注册:

    @Module({
      // ...
      providers: [
        {
          provide: APP_FILTER,
          useClass: HttpExceptionFilter,
        },
        {
          provide: APP_FILTER,
          useClass: DatabaseExceptionFilter,
        },
      ],
    })
    export class AppModule {}
    
  • 针对特定控制器的注册:

    @Controller('users')
    @UseFilters(new HttpExceptionFilter(), new DatabaseExceptionFilter())
    export class UsersController {
      // ...
    }
    
步骤 3:触发异常

在应用的任何地方抛出异常,对应的过滤器将会捕获并处理它。

@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string): string {
    throw new HttpException('User not found', HttpStatus.NOT_FOUND);
  }
}

在这个例子中,当 findOne 方法抛出 HttpException 时,HttpExceptionFilter 会被触发,并按照其逻辑处理异常。

Request Processing

在 NestJS 中,请求处理(Request Processing)通常涉及拦截器(Interceptors),这些拦截器可以被视为一种策略模式的实现。拦截器允许你在请求处理流程中插入自定义逻辑,比如日志记录、响应转换、错误处理等。接下来我将通过一个详细的例子来说明如何在 NestJS 中使用拦截器实现请求处理的策略。

示例:响应转换拦截器

假设我们需要一个拦截器来统一格式化所有 API 响应。这个拦截器将拦截出站响应,并将其转换为一个标准的格式。

步骤 1:创建拦截器

首先,我们创建一个名为 TransformInterceptor 的拦截器。

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
    return next.handle().pipe(
      map(data => ({
        data,
        timestamp: new Date().toISOString(),
        path: context.switchToHttp().getRequest().url,
      }))
    );
  }
}

interface Response<T> {
  data: T;
  timestamp: string;
  path: string;
}

在这个拦截器中,我们通过 rxjsmap 操作符来转换处理函数返回的数据。我们把数据包装成一个对象,其中包含数据、时间戳和请求路径。

步骤 2:注册拦截器

接下来,我们需要在应用中注册这个拦截器。有两种方式可以注册拦截器:全局注册和针对特定路由的注册。

  • 全局注册:

    @Module({
      // ...
      providers: [
        {
          provide: APP_INTERCEPTOR,
          useClass: TransformInterceptor,
        },
      ],
    })
    export class AppModule {}
    
  • 针对特定路由的注册:

    在特定控制器或者处理函数上使用拦截器:

    @Controller('items')
    @UseInterceptors(TransformInterceptor)
    export class ItemsController {
      // ...
    }
    
步骤 3:使用拦截器

一旦拦截器被注册,它就会自动应用于你的请求处理流程。在上面的例子中,任何通过 ItemsController 的响应都会被 TransformInterceptor 拦截并格式化。

Validation and Transformation

从源码层面详细分析 NestJS 中的管道(Pipes)是一个涉及到多个文件和类的复杂过程,但我会尽量简化并解释关键部分。

管道的基本原理

在 NestJS 中,管道(Pipes)是负责处理输入数据的中间件,它们在控制器处理函数执行之前运行。管道可以执行数据转换或数据验证。当你在控制器的参数前使用管道,NestJS 会在将请求传递给处理函数之前执行这些管道。

核心类:ValidationPipe

ValidationPipe 为例,这是一个内置的管道,通常用于 DTO(Data Transfer Object)验证。我们将从它的源码开始分析。

  1. ValidationPipe的定义

    ValidationPipe 是一个实现了 PipeTransform 接口的类。PipeTransform 接口要求实现一个名为 transform 的方法。

    export class ValidationPipe implements PipeTransform<any> {
      async transform(value, metadata: ArgumentMetadata) {
        // ...验证逻辑
      }
    }
    
  2. 使用 Class Validator 进行验证

    ValidationPipetransform 方法使用 class-validator 库来验证输入数据。如果数据不符合 DTO 定义的规则,它会抛出异常。

    import { validate } from 'class-validator';
    
    async transform(value, { metatype }): Promise<any> {
      if (!metatype || !this.toValidate(metatype)) {
        return value;
      }
      const object = plainToClass(metatype, value);
      const errors = await validate(object);
      if (errors.length > 0) {
        throw new BadRequestException('Validation failed');
      }
      return value;
    }
    
  3. 调用管道

    当请求到达控制器时,NestJS 会根据控制器方法的装饰器(如 @Body)来确定需要应用哪个管道。

    @Body(new ValidationPipe()) 的情况下,NestJS 会创建一个 ValidationPipe 实例,并调用它的 transform 方法,将传入的请求体作为参数。

  4. 异常处理

    如果验证失败,ValidationPipe 会抛出一个 BadRequestException。NestJS 捕获这个异常,并根据异常类型生成相应的 HTTP 响应。文章来源地址https://www.toymoban.com/news/detail-824281.html

到了这里,关于nestjs之策略模式的应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【前端设计模式】之策略模式

    设计模式是在软件开发中经过验证的解决问题的方法。它们是从经验中总结出来的,可以帮助我们更好地组织和管理代码,提高代码的可维护性、可扩展性和可重用性。无论是前端还是后端开发,设计模式都扮演着重要的角色。在本专栏中,我们将探索一些常见的前端设计模

    2024年02月04日
    浏览(42)
  • 【面试题】如何理解 前端设计模式-测策略模式?

    前端面试题库 ( 面试必备)              推荐:★★★★★ 地址:前端面试题库 【国庆头像】- 国庆爱国 程序员头像!总有一款适合你! 策略(Strategy)模式的定义:该模式定义了一 系列算法 ,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用

    2024年02月07日
    浏览(68)
  • 【前端知识】JavaScript——设计模式(工厂模式、构造函数模式、原型模式)

    工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。 优点:可以解决创建多个类似对象的问题 缺点:没有解决对象标识问题(即新创建的对象是什么类型) 示例: 构造函数模式与工厂模式相比,没有显式地创建对象,其属性和方

    2024年02月15日
    浏览(49)
  • 【设计模式-03】Strategy策略模式及应用场景

    Java 官方文档 Overview (Java SE 18 JDK 18) module index https://docs.oracle.com/en/java/javase/18/docs/api/index.html Java中使用到的策略模式 Comparator、comparable Comparator (Java SE 18 JDK 18) declaration: module: java.base, package: java.util, interface: Comparator https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/Compar

    2024年01月22日
    浏览(45)
  • 【设计模式-02】Strategy策略模式及应用场景

    Java 官方文档 Overview (Java SE 18 JDK 18) module index https://docs.oracle.com/en/java/javase/18/docs/api/index.html Java中使用到的策略模式 Comparator、comparable Comparator (Java SE 18 JDK 18) declaration: module: java.base, package: java.util, interface: Comparator https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/Compar

    2024年01月16日
    浏览(45)
  • 策略模式在AIBOT项目中的实际应用

    原文链接 https://www.jylt.cc/#/detail?activityIndex=2id=8d1912358fa1c1d8db1b44e2d1042b70 AIBOT 你想 我来做 AIBOT https://chat.jylt.top/ 定义 策略模式(Strategy Pattern:Define a family of algorithms,encapsulate each one,and make them interchangeable.)中文解释为:定义一组算法,然后将这些算法封装起来,以便它们之间

    2024年01月21日
    浏览(55)
  • 【JavaScript】手撕前端面试题:寄生组合式继承 | 发布订阅模式 | 观察者模式

    🧑‍💼个人简介:大三学生,一个不甘平庸的平凡人🍬 🖥️ NodeJS专栏:Node.js从入门到精通 🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述) 🖥️ TypeScript知识总结:TypeScript从入门到精通(十万字超详细知识点总结) 👉

    2023年04月08日
    浏览(97)
  • [JavaScript进阶] 路由跳转原理 之 Hash 模式

    首先讲讲路由跳转的原理, 其实没有什么神秘的, 以变量类比: 在上文的代码中, 在监听到点击事件的时候, 会改变变量的值. 那么, 如果不再监听点击事件, 而是 监听页面路径改变 ; container 也不是一个变量而是一个HTML元素, 当监听回调触发时, 修改的是这个 container 元素内部的

    2024年02月05日
    浏览(51)
  • javascript设计模式-应用示例

    有些时候为了适应没的场景,有些代码没必要每次调用时都进行一次环境判断,所以可以memoizing技术动态改写运行中代码的实现。 发起多个请求,程序会自动缓存,并通过setTimeOut重复调用。 这个例子充分应用了通道方法,利用此模式应该可以做到AOP的功能。

    2024年01月23日
    浏览(43)
  • 前端理解的HTTP缓存(作用、缓存策略、缓存控制机制、应用)

    目录 一、HTTP缓存有什么作用? 二、 浏览器的缓存策略有哪些? 1、强缓存(Expires、Cache-control) 2、协商缓存(Last-Modified、ETag) 3、缓存过程是什么? 三、浏览器缓存控制机制有哪些? 1、使用HTML Meta 标签 2、使用HTTP头信息控制缓存 四、哪些请求不能被缓存? 五、部署时

    2024年02月15日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包