NestJS 原理与核心概念解析

556 阅读15分钟

MVC

在后端开发中,MVC(Model-View-Controller)架构是一种经典的设计模式。它将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller),每个部分都有其特定的职责。

  • 模型(Model) :负责与数据库交互,处理数据的存储、检索和更新。
  • 视图(View) :负责呈现数据,通常是用户界面。
  • 控制器(Controller) :负责接收用户输入,调用模型处理数据,并将结果传递给视图。

MVC架构的主要优点在于它将应用程序的不同部分分离,使代码更具模块化和可维护性。然而,随着应用程序的复杂性增加,单纯的MVC架构可能不足以应对所有需求。此时,引入一些更高级的设计模式和框架是必要的。

NestJS不仅支持MVC架构,还引入了许多现代开发理念,如依赖注入(Dependency Injection)、面向切面编程(AOP)等,使开发者能够更高效地管理和组织代码。

Controller、Provider和Module

NestJS中的目录结构如下:

在NestJS中,控制器(Controller)负责处理传入的请求并返回响应,Provider负责处理业务逻辑和数据操作,模块(Module)用于组织和管理应用程序的各个部分。

示例代码

// cats.controller.ts
import { Controller, Get } from '@nestjs/common';
import { CatsService } from './cats.service';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Get()
  findAll(): string[] {
    return this.catsService.findAll();
  }
}

// cats.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class CatsService {
  findAll(): string[] {
    return ['Tom', 'Jerry'];
  }
}

// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class AppModule {}

IOC/DI

NestJS最出名的就是IOC(Inverse of Control)机制了,就是不需要手动创建实例,框架会自动扫描需要加载的类,并创建它们的实例放到容器里,实例化时还会根据该类的构造器参数自动注入依赖。

关于IOC和DI可以参考:了不起的IOC

IOC翻译成中文是控制反转,控制的是实例化过程。反转是将实例化交给 IoC 容器,由 IoC 容器 控制对象的创建。

如上边的代码,其中 CatsController 依赖 CatsService 中的方法。当代码运行的时候会生成一个IOC容器,IOC容器负责实例化,当要实例化 CatsController 时,会分析出 CatsController 依赖了 CatsService 然后实例化 CatsService 实例,并将这个实例注入到 new CatsController()

一些前置知识

装饰器

装饰器(Decorator)用来增强 JavaScript 类(class)的功能。装饰器是一种在代码运行时动态添加功能的方式,它通常用于修改类或函数的行为。

在TypeScript中启用装饰器需要将experimentalDecorators 设置为 true

装饰器使用 @expression 的形式,其中 expression 必须是一个可以返回函数的 expression,该函数在运行时会被调用,并传递有关被装饰声明的信息。

装饰器本质上是一个函数,用于修改类或类成员的行为。装饰器可以应用于类、类的方法、访问器、属性和参数。

普通装饰器,代码如下:

function color(target: Function) {
  target.prototype.color = 'red';
}

@color
class D {
  color: string;
}
const d = new D();
console.log(d.color); // 'red'

装饰器工厂的形式可以增加参数,代码更加灵活:

function color(col: string) {
  return function (target: Function) {
    target.prototype.color = col;
  };
}

@color('red')
class D {
  color: string;
}
const d = new D();
console.log(d.color); // 'red'

装饰器的类型

  1. 类装饰器:用于修饰整个类。
  2. 方法装饰器:用于修饰类的方法。
  3. 访问器装饰器:用于修饰类的 getter 或 setter 方法。
  4. 属性装饰器:用于修饰类的属性。
  5. 参数装饰器:用于修饰方法的参数。

关于装饰器参考:装饰器

Reflect Metadata

Reflect API 提供了一些操作对象的属性、方法、构造器的API,如 Reflect.get(obj, 'a')Reflect.has(obj, 'a')Reflect.applyReflect.construct。这些API已经是ES标准了,而metadata API还在草案阶段,需要引入 reflect-metadata 包。

import 'reflect-metadata';

const car = {
  brand: 'BMW',
  price: 10,
};

// 给对象添加元数据
Reflect.defineMetadata('desc', 'cheap', car);
// 给对象中的属性添加元数据
Reflect.defineMetadata('desc', 'cheap', car, 'price');
// 检查是否存在元数据
Reflect.hasMetadata('desc', car);
// 检查是否存在元数据
Reflect.hasMetadata('desc', car, 'price');
// 读取元数据
Reflect.getMetadata('desc', car);
// 读取元数据
Reflect.getMetadata('desc', car, 'price');

Reflect Metadata 的主要 API:

  • Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?):定义元数据。
  • Reflect.getMetadata(metadataKey, target, propertyKey?):获取元数据。
  • Reflect.hasMetadata(metadataKey, target, propertyKey?):检查是否存在元数据。
  • Reflect.deleteMetadata(metadataKey, target, propertyKey?):删除元数据。
  • Reflect.getOwnMetadata(metadataKey, target, propertyKey?):获取自有元数据(不包括继承的元数据)。
  • Reflect.getMetadataKeys(target, propertyKey?):获取所有元数据键。
  • Reflect.getOwnMetadataKeys(target, propertyKey?):获取自有元数据键。

它也支持装饰器的方式使用:

@Reflect.metadata('key', 111)
class C {
  @Reflect.metadata('metadataKey', 'metadataValue')
  method() {}
}

Reflect.metadata 装饰器用装饰器工厂再封装一层:

function Type(type) {
  return Reflect.metadata('design:type', type);
}
function ParamTypes(...types) {
  return Reflect.metadata('design:paramtypes', types);
}
function ReturnType(type) {
  return Reflect.metadata('design:returntype', type);
}

// 为class添加类型元数据
@ParamTypes(String, Number)
class User {
  constructor(text, i) {}

  @Type(String)
  get name() {
    return 'text';
  }

  @Type(Function)
  @ParamTypes(Number, Number)
  @ReturnType(Number)
  add(x, y) {
    return x + y;
  }
}

然后我们就可以通过 Reflect metadata 的API获取这个类和对象的元数据了:

let obj = new User('a', 1);
let paramTypes = Reflect.getMetadata('design:paramtypes', obj, 'add');
obj.add();
// 获取add 方法的参数类型 [Number, Number]

Nest的实现原理就是使用 Reflect Metadata API 通过装饰器给 Class 添加一些元数据,然后初始化的时候取出这些元数据,进行依赖的分析,然后创建对应的实例对象。

但是,我们在代码中并没有添加 MetaData,这是依靠强大的 TypeScriptTypeScript 有一个编译选项叫做 emitDecoratorMetadata,开启它编译时自动添加一些 metadata 数据。我们开启 emitDecoratorMetadata,定义一个 Class

// TypeScript代码
class Example {
  constructor(public name: string) {}
}

编译后的 JavaScript 代码如下:

// 编译后的JavaScript代码
var Example = /** @class */ (function () {
  function Example(name) {
    this.name = name;
  }
  Example = __decorate(
    [
      __param(0, String),
      __metadata('design:paramtypes', [String]),
    ],
    Example
  );
  return Example;
})();

TypeScript 自动注入了一些 metadata,分别是:属性类,参数类型,返回值类型。NestJS通过利用装饰器和 Reflect Metadata API,从而在运行时能够自动解析依赖关系并注入所需的依赖。

基本概念

Controllers

Controller 是用于处理传入的 HTTP 请求并返回响应的类。它们是 NestJS 框架的核心部分之一,负责处理路由、请求和响应。

Controllers

Providers

在 NestJS 中,Provider 是用于处理业务逻辑的核心概念。它们通过依赖注入(Dependency Injection)机制在整个应用中提供共享的功能。

Providers

Modules

在 NestJS 中,Module 是用于组织应用程序结构的基本单位。每个 Module 封装了一组相关的功能,并且可以导入和导出其他 Module

Modules

AOP

Nest 还提供了 AOP(Aspect Oriented Programming)的能力,也就是面向切面编程的能力。面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。这有助于增强模块化,使得代码更易于理解、维护和扩展。

NestJS是一个基于Node.js的现代、高性能的框架,它采用了多种编程范式,包括AOP。在NestJS中,AOP主要通过以下几个概念实现:

中间件(Middleware)

类似于 Express 或 Koa 中的中间件,中间件函数可以访问请求对象(req)、响应对象(res)以及应用的 next 函数,next 函数是一个当被调用时将控制权交给下一个中间件的函数。

创建中间件

在 NestJS 中创建中间件有两种方式:使用类和使用函数。

  1. 使用类创建中间件

使用类创建中间件时,需要实现 NestMiddleware 接口,并定义 use 方法。

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`Request...`);
    next();
  }
}
  1. 使用函数创建中间件

也可以直接使用函数来创建中间件。

import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
}

如何使用中间件

中间件可以在模块中配置,以应用于特定的路由、控制器,或者全局范围内。

  1. 在模块中配置中间件

要在模块中配置中间件,需要实现 NestModule 接口,并定义 configure 方法。

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { LoggerMiddleware } from './logger.middleware';

@Module({
  controllers: [CatsController],
})
export class CatsModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes(CatsController);
  }
}
  1. 应用于特定路由

可以将中间件应用于特定的路由。

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { LoggerMiddleware } from './logger.middleware';

@Module({
  controllers: [CatsController],
})
export class CatsModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('cats');
  }
}
  1. 全局范围内使用中间件

要在全局范围内使用中间件,可以在主模块中配置。

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';

@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('*');
  }
}

NestJS中的守卫(Guard)

守卫(Guard)是 NestJS 框架中用于处理权限控制的一个重要概念。守卫是一种用于确定当前请求是否有权限继续执行的函数。它们在路由处理程序之前执行,可以对请求进行验证和授权操作。守卫在 NestJS 中有着广泛的应用场景,如身份验证、角色授权等。

如何定义守卫

在 NestJS 中创建守卫需要实现 CanActivate 接口,并定义 canActivate 方法。

下面是一个简单的身份验证守卫示例:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  validateRequest(request: any): boolean {
    // 在这里添加你的验证逻辑
    return true; // 如果验证通过,返回true,否则返回false
  }
}

如何使用守卫

守卫可以在方法范围内、控制器范围内或全局范围内使用。

  1. 在方法范围内使用守卫

要在特定方法上使用守卫,可以使用 @UseGuards 装饰器。

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
export class CatsController {
  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    return 'This action returns all cats';
  }
}
  1. 在控制器范围内使用守卫

要在整个控制器上使用守卫,可以将 @UseGuards 装饰器应用于控制器类。

import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}
  1. 在全局范围内使用守卫

要在全局范围内使用守卫,可以在主模块中配置。

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}

守卫的应用场景

  • 身份验证:检查请求是否包含有效的身份验证信息,如JWT令牌。
  • 角色授权:检查用户是否具有执行特定操作的权限。
  • 访问控制:限制特定IP或用户组的访问权限。

NestJS中的管道(Pipe)

管道(Pipe)是 NestJS 框架中用于转换和验证数据的一个重要概念。管道在请求到达控制器处理程序之前执行,可以对请求数据进行转换、验证和过滤操作。管道在 NestJS 中有着广泛的应用场景,如数据验证、数据转换等。

如何定义管道

在 NestJS 中创建管道需要实现 PipeTransform 接口,并定义 transform 方法。

下面是一个简单的数据验证管道示例:

import { Injectable, PipeTransform, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    if (!this.isValid(value)) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  isValid(value: any): boolean {
    // 在这里添加你的验证逻辑
    return true; // 如果验证通过,返回true,否则返回false
  }
}

如何使用管道

管道可以在方法参数、控制器范围内或全局范围内使用。

  1. 在方法参数上使用管道

要在特定方法参数上使用管道,可以使用 @UsePipes 装饰器或直接在参数装饰器中使用管道。

import { Controller, Get, Query, UsePipes } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';

@Controller('cats')
export class CatsController {
  @Get()
  @UsePipes(ValidationPipe)
  findAll(@Query('age') age: number) {
    return `This action returns all cats of age ${age}`;
  }
}

或者直接在参数装饰器中使用管道:

import { Controller, Get, Query } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Query('age', ValidationPipe) age: number) {
    return `This action returns all cats of age ${age}`;
  }
}
  1. 在控制器范围内使用管道

要在整个控制器上使用管道,可以将 @UsePipes 装饰器应用于控制器类。

import { Controller, UsePipes } from '@nestjs/common';
import { ValidationPipe } from './validation.pipe';

@Controller('cats')
@UsePipes(ValidationPipe)
export class CatsController {
  @Get()
  findAll(@Query('age') age: number) {
    return `This action returns all cats of age ${age}`;
  }
}
  1. 在全局范围内使用管道

要在全局范围内使用管道,可以在主模块中配置。

import { Module, ValidationPipe } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe,
    },
  ],
})
export class AppModule {}

管道的应用场景

  1. 数据验证:验证请求数据的合法性,如检查必填字段、数据格式等。
  2. 数据转换:将请求数据转换为所需的格式,如将字符串转换为整数。
  3. 数据过滤:过滤掉不需要的数据或字段。

示例:数据转换管道

下面是一个数据转换管道的示例,它将请求中的字符串转换为整数。

import { Injectable, PipeTransform, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ParseIntPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}

NestJS中的拦截器(Interceptor)

拦截器(Interceptor)是 NestJS 框架中用于处理请求和响应的一个重要概念。拦截器在请求到达处理程序之前和响应返回客户端之前执行,可以对请求和响应进行处理、修改等操作。拦截器在 NestJS 中有着广泛的应用场景,如日志记录、响应映射、异常处理等。

如何定义拦截器

在 NestJS 中创建拦截器需要实现 NestInterceptor 接口,并定义 intercept 方法。

下面是一个简单的日志记录拦截器示例:

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

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    const now = Date.now();
    return next.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

如何使用拦截器

拦截器可以在方法范围内、控制器范围内或全局范围内使用。

  1. 在方法范围内使用拦截器

要在特定方法上使用拦截器,可以使用 @UseInterceptors 装饰器。

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';

@Controller('cats')
export class CatsController {
  @Get()
  @UseInterceptors(LoggingInterceptor)
  findAll() {
    return 'This action returns all cats';
  }
}
  1. 在控制器范围内使用拦截器

要在整个控制器上使用拦截器,可以将 @UseInterceptors 装饰器应用于控制器类。

import { Controller, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';

@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {
  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}
  1. 在全局范围内使用拦截器

要在全局范围内使用拦截器,可以在主模块中配置。

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './logging.interceptor';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
})
export class AppModule {}

拦截器的应用场景

  1. 日志记录:记录每个请求的详细信息,如时间戳、请求路径、请求方法等。
  2. 响应映射:将响应数据转换为所需的格式。
  3. 异常处理:在响应返回之前捕获和处理异常。
  4. 缓存:在请求处理之前检查缓存,以减少不必要的处理。

示例:响应映射拦截器

下面是一个响应映射拦截器的示例,它将响应数据包装在一个统一的格式中。

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

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(data => ({ data })),
    );
  }
}

要使用这个拦截器,可以在方法或控制器上应用:

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { TransformInterceptor } from './transform.interceptor';

@Controller('cats')
@UseInterceptors(TransformInterceptor)
export class CatsController {
  @Get()
  findAll() {
    return ['cat1', 'cat2', 'cat3'];
  }
}

异常过滤器

异常过滤器是 NestJS 中用于处理异常的机制。它们可以捕获应用程序中的异常,并对其进行处理,以便向客户端返回适当的响应。异常过滤器在 NestJS 中有着广泛的应用场景,如统一错误处理、自定义错误响应等。

如何定义异常过滤器

在 NestJS 中创建异常过滤器需要实现 ExceptionFilter 接口,并定义 catch 方法。

下面是一个简单的异常过滤器示例:

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

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

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

如何使用异常过滤器

异常过滤器可以在方法范围内、控制器范围内或全局范围内使用。

  1. 在方法范围内使用异常过滤器

要在特定方法上使用异常过滤器,可以使用 @UseFilters 装饰器。

import { Controller, Get, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';

@Controller('cats')
export class CatsController {
  @Get()
  @UseFilters(HttpExceptionFilter)
  findAll() {
    throw new HttpException('Forbidden', 403);
  }
}
  1. 在控制器范围内使用异常过滤器

要在整个控制器上使用异常过滤器,可以将 @UseFilters 装饰器应用于控制器类。

import { Controller, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';

@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
  @Get()
  findAll() {
    throw new HttpException('Forbidden', 403);
  }
}
  1. 在全局范围内使用异常过滤器

要在全局范围内使用异常过滤器,可以在主模块中配置。

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from './http-exception.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

异常过滤器的应用场景

  1. 统一错误处理:捕获应用程序中的所有异常,并返回统一的错误响应格式。
  2. 自定义错误响应:根据异常类型或内容,返回自定义的错误响应。
  3. 日志记录:记录异常信息,方便调试和分析。

示例:全局异常过滤器

下面是一个全局异常过滤器的示例,它捕获所有未处理的异常,并返回统一的错误响应。

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception instanceof HttpException
      ? exception.getStatus()
      : HttpStatus.INTERNAL_SERVER_ERROR;

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception instanceof HttpException ? exception.message : 'Internal server error',
    });
  }
}

要使用这个全局异常过滤器,可以在主模块中配置:

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionsFilter } from './all-exceptions.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: AllExceptionsFilter,
    },
  ],
})
export class AppModule {}

整体执行流程

NestJS 的整体执行流程可以分为以下几个主要步骤:

  1. 请求进入应用:当客户端发送 HTTP 请求时,请求首先进入 NestJS 应用。

  2. 中间件处理:请求经过应用配置的中间件。中间件可以对请求进行预处理,例如日志记录、身份验证等。

  3. 守卫检查:在进入控制器处理程序之前,NestJS 会执行守卫(Guard)来检查请求是否有权限继续执行。如果守卫返回 false,请求将被拒绝。

  4. 拦截器处理:请求经过拦截器(Interceptor)。拦截器可以对请求和响应进行处理,例如日志记录、响应映射等。

  5. 管道处理:请求经过管道(Pipe)。管道可以对请求数据进行转换和验证,例如数据验证、数据转换等。

  6. 控制器处理:请求最终到达控制器处理程序。控制器处理程序处理请求,并调用相应的服务(Provider)来执行业务逻辑。

  7. 响应处理:控制器处理程序将结果返回给客户端。在返回响应之前,响应会再次经过拦截器和异常过滤器进行处理。

  8. 异常处理:如果在处理请求的过程中发生异常,异常过滤器会捕获异常,并返回适当的错误响应。

通过上述内容,我们可以看到 NestJS 是如何通过中间件、守卫、拦截器、管道和异常过滤器等机制来实现请求的完整处理流程的。这些机制使得 NestJS 能够提供强大的功能和灵活性,帮助开发者更高效地构建和维护应用程序。