# 框架-NestJS

# 简介

NestJS (opens new window) 是一个用于构建高效、可扩展的 Node.js 服务器端应用的框架。它使用渐进式 JavaScript,构建并完全支持 TypeScript(但仍然允许开发者使用纯 JavaScript 进行编码)并结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数式反应式编程)的元素。

相关地址

# 安装

# 安装nestjs
npm install -g @nestjs/cli

# 创建项目
nest new project-name

# 项目结构与概念

# 初始项目结构

项目安装后,会生成一个项目目录,目录结构如下:

项目结构

项目中主要有srctest两个目录,src目录是项目的主要目录,test目录是测试目录。下面我们详细看看src目录的结构:

  • app.controller.spec.ts:控制器测试文件,用于测试控制器。
  • app.controller.ts:控制器文件,用于处理HTTP请求。
  • app.module.ts:模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
  • app.service.ts:服务文件,用于处理业务逻辑。
  • main.ts:主文件,用于启动应用程序。

# 常见概念

  • module:模块,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
  • controller:控制器,用于处理HTTP请求。
  • service:服务,用于处理业务逻辑。
  • entity:实体,用于处理实体。
  • dto:数据传输对象,用于处理数据传输。
  • vo:视图对象,用于处理视图。
  • exception:异常,用于处理异常。
  • interface:接口,用于处理接口。
  • middleware:中间件,用于处理中间件。
  • pipe:管道是用 @Injectable() 装饰器注释的类,它实现了 PipeTransform 接口。
  • guard:守卫是用 @Injectable() 装饰器注释的类,它实现了 CanActivate 接口。
  • interceptor:拦截器是用 @Injectable() 装饰器注释的类,它实现了 NestInterceptor 接口。
  • Providers: 提供器,用于处理提供器。

# 管道

NestJS框架中,管道是一种用@Injectable()装饰器注释的类,它实现了PipeTransform接口。管道主要负责在数据到达控制器处理方法之前对其进行转换或验证。

管道的主要作用

  • 数据转换:将输入数据转换为所需的形式(例如,将字符串转换为整数)
  • 数据验证:验证输入数据是否满足特定条件,如果不满足则抛出异常

管道的基本实现

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

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // 在这里进行数据转换或验证
    return value; // 返回转换后的值
  }
} 

NestJS内置管道

NestJS提供了几个内置的管道:

  • ValidationPipe(options?: ValidationPipeOptions):用于验证和转换请求数据
  • ParseIntPipe(options?: ParseIntPipeOptions):将字符串参数转换为整数
  • ParseBoolPipe(options?: ParseBoolPipeOptions):将字符串参数转换为布尔值
  • ParseArrayPipe(options?: ParseArrayPipeOptions):将字符串参数转换为数组
  • ParseUUIDPipe(options?: ParseUUIDPipeOptions):验证参数是否为有效的UUID
  • DefaultValuePipe(defaultValue: any, options?: DefaultValuePipeOptions):如果传入值为undefined,则使用默认值

管道的使用方式

  • 方法级别:仅适用于特定的控制器方法
  • 控制器级别:适用于控制器的所有方法
  • 全局级别:适用于整个应用程序的所有控制器和方法

方法级别

// 在控制器方法中使用
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

// 在控制器类中使用
@UsePipes(new ValidationPipe()) // 使用ValidationPipe管道 验证参数
@Controller('users')
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

// 在全局级别使用
@Module({
  imports: [
    // ...
  ],
  providers: [ValidationPipe],
})
export class AppModule {}

# 守卫

NestJS框架中,守卫是一种用@Injectable()装饰器注释的类,它实现了CanActivate接口。守卫的主要责任是判断请求是否应该被当前路由处理程序处理,通常用于实现权限控制、认证和授权。

守卫的核心功能

  • 认证:验证用户身份
  • 授权:检查用户是否有权限执行特定操作
  • 访问控制:基于各种条件限制对路由的访问

实际应用场景

  • JWT认证守卫:验证JWT令牌的有效性
  • 角色授权守卫:检查用户是否具有执行操作的角色
  • API密钥守卫:验证API请求中的密钥
  • 限流守卫:限制API请求频率
  • IP白名单守卫:只允许特定IP地址访问

守卫的实现

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();
    // 执行认证逻辑,返回true表示允许访问,false表示拒绝访问
    return validateRequest(request);
  }
}

守卫的使用方式

守卫可以在三个不同级别应用:

  • 全局级别:适用于整个应用程序中的所有控制器和路由
  • 控制器级别:适用于控制器中的所有路由
  • 方法级别:仅适用于特定的路由处理程序
// 方法级别绑定示例
@Get('profile')
@UseGuards(AuthGuard)
getProfile(@Request() req) {
  return req.user;
}

// 控制器级别绑定示例
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {
  // ...
}

// 全局级别绑定示例
// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());

# 实际开发下推荐的目录结构

src/
├── app.controller.ts # 控制器文件,用于处理HTTP请求。
├── app.module.ts # 模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
├── app.service.ts # 服务文件,用于处理业务逻辑。
├── common/
│   ├── decorators/ # 装饰器文件,用于装饰控制器、服务等。
│   ├── filters/ # 过滤器文件,用于过滤HTTP请求。
│   ├── guards/ # 守卫文件,用于保护控制器、服务等。
│   ├── interceptors/ # 拦截器文件,用于拦截HTTP请求。
│   ├── pipes/ # 管道文件,用于处理HTTP请求。
│   └── utils/ # 工具文件,用于处理业务逻辑。
├── config/
│   ├── configuration.ts # 配置文件,用于配置应用程序。
│   ├── configuration.module.ts # 配置模块文件,用于组织和管理配置。
│   └── configuration.service.ts # 配置服务文件,用于处理配置。
├── modules/
│   ├── module1/ # 模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
│   │   ├── module1.controller.ts # 控制器文件,用于处理HTTP请求。
│   │   ├── module1.module.ts # 模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
│   │   ├── module1.service.ts # 服务文件,用于处理业务逻辑。
│   │   ├── entities/ # 实体文件,用于处理实体。
│   │   ├── └── create-module1.entity.ts # 创建模块1的实体文件。
│   │   └── dto/ # 数据传输对象文件,用于处理数据传输。
│   │       └── create-module1.dto.ts # 创建模块1的数据传输对象文件。
│   └── module2/ # 模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
│       ├── module2.controller.ts # 控制器文件,用于处理HTTP请求。
│       ├── module2.module.ts # 模块文件,用于组织和管理模块。例如将控制器、服务、管道、过滤器等组织在一起。
│       ├── module2.service.ts # 服务文件,用于处理业务逻辑。
│       ├── entities/ # 实体文件,用于处理实体。
│       ├── └── create-module2.entity.ts # 创建模块2的实体文件。
│       └── dto/ # 数据传输对象文件,用于处理数据传输。
│           └── create-module2.dto.ts # 创建模块2的数据传输对象文件。
├── main.ts # 主文件,用于启动应用程序。
└── shared/ # 共享文件,用于处理共享的逻辑。
    ├── constants/ # 常量文件,用于处理常量。
    ├── decorators/ # 装饰器文件,用于装饰控制器、服务等。
    ├── dtos/ # 数据传输对象文件,用于处理数据传输。
    │   ├── create-module1.vo.ts # 创建模块1的视图对象文件。
    ├── exceptions/ # 异常文件,用于处理异常。
    ├── interfaces/ # 接口文件,用于处理接口。
    ├── middlewares/ # 中间件文件,用于处理中间件。
    ├── pipes/ # 管道文件,用于处理管道。
    └── services/ # 服务文件,用于处理业务逻辑。

# 常用命令

nestjs 的命令行工具(CLI)提供了许多有用的命令,以下是一些常用的命令:

  • nest new <project-name>:创建一个新的 NestJS 项目。
  • nest g resource <resource-name>:生成一个新的资源。
  • nest g controller <controller-name>:生成一个新的控制器。
  • nest g service <service-name>:生成一个新的服务。
  • nest g module <module-name>:生成一个新的模块。
  • nest g pipe <pipe-name>:生成一个新的管道。
  • nest g guard <guard-name>:生成一个新的守卫。
  • nest g interceptor <interceptor-name>:生成一个新的拦截器。
  • nest start:启动应用程序。
  • nest build:构建应用程序。
  • nest serve:启动开发服务器。
  • nest test:运行测试。
  • nest lint:运行 lint 检查。
  • nest format:格式化代码。
  • nest add <package-name>:添加一个包。
  • nest remove <package-name>:移除一个包。
  • nest update <package-name>:更新一个包。

# 功能

# 接入swagger

nestjs 接入 swagger 需要安装 @nestjs/swagger@types/swagger-ui-express 包。

npm install @nestjs/swagger @types/swagger-ui-express

swagger.ts文件中添加以下代码:

// swagger.ts
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { INestApplication } from '@nestjs/common';

export function setupSwagger(app: INestApplication<any>) {
  const config = new DocumentBuilder()
    .setTitle('NestJS API') // 设置标题
    .setDescription('API description') // 设置描述
    .setBasePath('api') // 设置基础路径
    .setVersion('1.0') // 设置版本
    // 添加JWT Bearer认证配置
    .addBearerAuth(
      {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        name: 'JWT',
        description: '输入JWT token',
        in: 'header',
      },
      'JWT-auth', // 这个名称需要在控制器的@ApiBearerAuth()中使用
    )
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);
}

main.ts文件中添加以下代码:

//main.ts
import { setupSwagger } from './swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  setupSwagger(app); // 添加swagger
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

# 常用的Swagger装饰器

装饰器 描述
@ApiTags 为控制器或方法添加标签
@ApiOperation 为控制器方法添加操作描述
@ApiParam 描述路径参数、请求参数或响应参数
@ApiBody 指定请求体的DTO类型
@ApiResponse 描述API的响应
@ApiBearerAuth 指定请求需要携带Bearer Token
@ApiProperty 为DTO类型的属性添加元数据
@ApiQuery 描述查询参数
@ApiHeader 描述请求头信息
@ApiExcludeEndpoint 标记一个控制器方法不在Swagger UI中显示

# 日志

# 集成Winston