个人网站记录二 - Nest基础

346 阅读9分钟

路由

控制器配置路由

nestjs的路由是通过控制器配置的,控制器层面配置Cgi路径:控制器路由前缀[可选] + 方法路由前缀[可选]

import { Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';

// @Controller() 修改1
@Controller('app')
export class AppController {
  constructor(private readonly appService: AppService) {}
    
  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
  
  // --- 修改 2--- 
  @Get('name')
  getName(): String {
      return 'LW';
  }
  
  // --- 修改 3---
  @Post('age')
  getAge(): Number {
      return 18;
  }
}

如上,将AppController控制器添加路由前缀app(修改1), 添加一个了name路由前缀的get方法(修改2)和age路由前缀的post方法(修改3)。保存等重新编译后刷新浏览器发现localhost:3000提示404,而添加上app前缀(完整路由:localhost:3000/app)才正常显示出Hello World! image.png

修改2的路由(localhost:3000/app/name)
image.png

修改3的路由(localhost:3000/app/age)这里借助postman请求post cgi image.png

全局路由配置

nestjs还支持配置全局路由前缀
image.png
添加全局路由前缀后,所有的请求都需要添加上前缀。例如此处的localhost:3000/app 就变成了 localhost:3000/newapi/app

控制器方法中一般不写过多业务逻辑,而是将业务逻辑写在Service中,比如AppController中的getHello就直接调用AppService的getHello方法。更多的路由配置细节请查看中文文档,这里就不过多介绍了。

路由通配符

支持?+*正则表达式配置路由


@Get('regexp/note_*')
getReg(): String {
return '通配路由';
}
@Get('blog/:id')
getRegExp(@Param() params): String {
return `文章id: ${params.id}`;
}

静态资源

可以通过useStaticAssets指定静态资源目录,还可以指定虚拟目录。
比如设置public为静态资源目录, 并指定/static为虚拟目录

app.useStaticAssets(join(__dirname, '..', 'public'), {
  prefix: '/static',
});

public下有个index.html文件
image.png 然后可以通过路由访问了
image.png

模块

前面提到nestjs都是按模块划分的,现手动一步步创建一个用户模块。nestjs支持通过命令快速创建模块、控制器、服务器、资源等,后面会介绍,这步可自行选择跳过。这里我为了加深了解选择手动创建,后续还是会通过命令创建。

  1. 首先在src下新建user文件夹。
  2. src/user下创建user.module.ts。前面提到模块是通过module装饰器装饰的类,module装饰器引入自@nestjs/common,module装饰器提供四个可选参数控制器controllers提供者providers需要导入的模块imports导入的模块exports。 记得这些不用对照自己也能写敲出module文件的基本结构,当然也不用记,后面项目写多了自然就记住了。
import { Module } from '@nestjs/common';

@Module({
    controllers: [],
    providers: [],
    imports: [],
    exports: []
})
export class UserModule {}
  1. 在需要导入的模块中导入该user模块,这里需要在app.module.ts中@module装饰器imports中导入
// app.module.ts
...
import { UserModule } from './user/user.module';

@Module({
    imports: [UsersModule],
...
  1. 创建了模块文件,接下来就需要创建控制器文件了。在src/user下创建user.controller.ts,并在user.module.ts中引入该控制器,内容如下。
//user.module.ts
...
import { UserController } from "./user.controller";
...
    controllers: [UserController],
...


// user.controller.ts
import { Controller, Get, Param } from "@nestjs/common";
  
@Controller('user')
export class UserController {
    constructor() {}

    @Get(':id')
    getUser(@Param() params) {
        return {
            name: 'lw-' + params.id,
            age: 18
        }
    }
}
  1. 此时getUser方法就可以正常访问了
    image.png
  2. 这里控制器只是简单返回的了个对象,直接在控制器中返回也可以,但实际开发中逻辑功能因写在Service中。同样的在src/user下创建user.service.ts,修改后内容如下。
// user.module.ts
import { Module } from "@nestjs/common";
import { UserController } from "./user.controller";
import { UserService } from "./user.service";


@Module({
    controllers: [UserController],
    providers: [UserService],
    imports: [],
    exports: []
})
export class UsersModule {}


// user.controller.ts
import { Controller, Get, Param } from "@nestjs/common";
import { UserService } from "./user.service";

@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) {}

    @Get(':id')
    getUser(@Param() params) {
        return this.userService.getUser(params.id);
    }
}


// user.service.ts
import { Injectable } from "@nestjs/common";

@Injectable()
export class UserService {
    getUser(userId) {
        return {
            name: 'lw-' + userId,
            age: 18
        }
    }
}

cli快捷指令

使用nest generate|g [options] <schematic> [name] [path]可快速创建模块、控制器等。 这里创建一个Login模块为例。

  • 执行nest g mo login创建一个login模块文件,会自动在src下创建login文件夹,里面包含一个login.module.ts文件,并且在app.module.ts中自动引入LoginModule。 image.png

  • 执行nest g co login创建一个login控制器文件,自动在src/login下创建login.controller.ts文件,并自动在src/login/login.module.ts引入LoginController image.png

  • 执行nest g s login创建一个login服务文件,自动在src/login下创建login.service.ts文件,并自动在app.module.ts中引入LoginService image.png

简单使用几个指令就能创建一个模块,比手敲代码要方便的多,当然还有更方便的nest g res login就会创建整个Login功能模块(CRUD资源),并包含基础的dtoentity文件。nestjs提供了众多快捷的CLI,请查阅中文文档CLI用法

数据操作

前面介绍了基本的路由配置以及模块的创建,创建好模块后,既然是后端项目自然,接下来就是数据库操作了。

安装数据库(MySQL)

请自行百度😂

安装vscode数据库客户端插件Database Client

image.png 如图安装该插件

image.png 安装完后点击左侧的数据库图标,然后在右侧输入名称(随意,可不填)、密码(安装数据库时设置的密码),点击连接即可。如连接失败,检查一下数据库服务是否开启,以及账号连接配置是否正确。

使用TypeORM连接MySQL

安装依赖

yarn add @nestjs/typeorm typeorm mysql2

连接数据库
方式1

// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { Module } from '@nestjs/common';
...

@Module({
  imports: [
    ...
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'blog',
      entities: [],
      synchronize: true,
    })
  ],
  ...
})
export class AppModule {}

上面通过将数据库配置放到TypeOrmModule.forRoot里,还可以将配置放到文件上。
方式2
在src同级目录下创建ormconfig.json文件

//ormconfig.json
{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "123456",
  "database": "blog",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true
}

然后app.module.ts文件中,TypeOrmModule.forRoot就可以不带参数了。

// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { Module } from '@nestjs/common';
...

@Module({
  imports: [
    ...
    TypeOrmModule.forRoot()
  ],
  ...
})
export class AppModule {}

注意,ormconfig.json 文件由typeorm库载入,因此,任何上述参数之外的属性都不会被应用(例如由forRoot()方法内部支持的属性–例如autoLoadEntitiesretryDelay())

CURD

通过TypeORM可以将实体映射到数据库表,就不用手动执行建库操作。下面简单的创建一个实体来熟悉下大致流程。 创建src/user/entities/user.entity.ts文件。

import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";

@Entity('users')
export class UsersEntity {
  // 主键
  @PrimaryGeneratedColumn()
  id: number;
   
  // 字符串,对应varchar(20)
  @Column({ length: 20 }) 
  username: string;
    
  // 字符串,没有长度限制,默认varchar(255)
  @Column()
  nickname: string;
   
  // 同上,设置select: false, 查询的时候就不会返回。
  // 实际开发过程中密码应加密
  @Column({ select: false })
  password: string;
    
  // 数字,对应int
  @Column()
  age: number;
}

这个实体文件就对于一个表的描述,并且数据库配置中开启了synchronize: true(生产环境建议关闭),连接数据库后,就会根据该实体文件自动创建users表。

开发中遇到过个问题,数据库为mysql,在开启synchronize后,提示数据表已存在的错误,后来发现实体定义的命名不对,需要小写(例如@Entity('小写'))。

// user.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersEntity } from './entities/user.entity';
//...
@Module({
  //...
  imports: [TypeOrmModule.forFeature([UsersEntity])],
})

在需要使用该实体的模块中通过TypeOrmModule.forFeature导入该实体。

// app.module.ts
import { UsersEntity } from './user/entities/user.entity';
// ...
@Module({
  imports: [
    
    TypeOrmModule.forRoot({
      // ...
      entities: [UsersEntity],
    })
  ],
  // ...
})

另外还需AppModule数据库连接选项加入该实体。而且后面每加一个实体,都需要在这里导入。当然,可以开启autoLoadEntities自动加载实体,这样每个通过forFeature方法注册的实体都将自动添加到配置对象的entities数组中。

// app.module.ts
//import { UsersEntity } from './user/entities/user.entity';
// ...
@Module({
  imports: [
    //...
    TypeOrmModule.forRoot({
      // ...
      //entities: [UsersEntity],
      autoLoadEntities: true, // 开启自动加载实体
    })
  ],
  // ...
})

然后后查看数据库会发现已经创建一个Users表 image.png

再也不用像以前需要手敲sql语句了🤓,接下来试试插入和查询操作。调整如下

// user.controller.ts
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
import { UserService } from "./user.service";

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':username')
  getUser(@Param() params) {
    return this.userService.getUser(params.username);
  }

  @Post('create')
  createUser(@Body() user) {
    return this.userService.createUser(user);
  }
}

// ------------------------

// user.service.ts
import { HttpException, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UsersEntity } from "./entities/user.entity";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>
  ) { }

  async getUser(username: string): Promise<UsersEntity> {
    let user = await this.usersRepository.findOne({ where: { username } });
    return user;
  }

  async createUser(user: Partial<UsersEntity>): Promise<UsersEntity> {
    console.log(user);
    let { username } = user;
    if(!username) {
      throw new HttpException(`${username}不能为空!`,401) 
    }
    let usr = await this.usersRepository.findOne({ where: { username } });
    if(usr) {
      throw new HttpException(`${username}已存在!`,401);
    }
    return await this.usersRepository.save(user);
  }
}

接着使用postman来试试接口

  • 创建 image.png 成功,插入了一条数据 image.png

  • 查询 image.png 也毛得问题,成功返回了大帅哥的信息。

这只是简单示例了一下数据的操作,实际数据库操作要比这复杂的多(比如多表连表查询,几千行sql语句你值得拥有[虽说没遇到过,之前公司老系统手敲sql很容易就上千行了])、规范(比如密码字符长度、密码加密等)等

数据库配置优化

前面例子把数据库的连接信息都写到app.module.ts中了,而且没有区分开发和生产的配置,改造一下。

  • 在package.json中scripts中加入变量标识

先安装cross-env打平环境变量设置差异

yarn add cross-env -D
// package.json
...
"scripts": {
    "start": "nest start", // 加入NODE_ENV环境变量
    "start:dev": "cross-env NODE_ENV=dev nest start --watch", // 加入NODE_ENV环境变量
}
  • 在src同级创建.env.dev(开发配置)和.env.prod(生产配置)
// .env.dev
DB_HOST=localhost  
DB_PORT=3306
DB_USER=root
DB_PASSWORD=123456
DB_DATABASE=blog

!------------------

// .env.prod
DB_HOST=localhost  
DB_PORT=3306
DB_USER=root
DB_PASSWORD=123456
DB_DATABASE=blog
  • 创建src/config/db.config.ts
// src/config/db.config.ts
import * as fs from 'fs';
import * as path from 'path';

// 这里只简单区分开发和生产,不是开发就当作生产
export const isProd = process.env.NODE_ENV !== 'dev';

function getEnvFilePath() {
  const localEnv = path.resolve('.env.dev');
  const prodEnv = path.resolve('.env.prod');

  if (!fs.existsSync(localEnv) && !fs.existsSync(prodEnv)) {
    throw new Error(`缺少配置文件 ${isProd ? prodEnv : localEnv}`);
  }

  const filePath = isProd && fs.existsSync(prodEnv) ? prodEnv : localEnv;
  return filePath;
}

export const dbEnvFilePath = getEnvFilePath()
  • 使用@nestjs/config(需先安装yarn add @nestjs/config)提供的配置模块功能,将app.module.ts做如下修改
// app.module.ts
import { ConfigModule, ConfigService } from '@nestjs/config';
import { dbEnvFilePath, isProd } from './config/db.config';
...

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: [dbEnvFilePath]
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'mysql',
        autoLoadEntities: true,
        synchronize: !isProd,
        host: configService.get('DB_HOST'),
        port: configService.get<number>('DB_PORT'), 
        username: configService.get('DB_USER'),
        password: configService.get('DB_PASSWORD'),
        database: configService.get('DB_DATABASE'),
      })
    }),
  ],

})
export class AppModule {}

这样便对数据库的配置做了个最简单的优化

异常过滤器

虽然可以正常curd了,但请求还是脆弱粗糙。当我们的请求缺少参数将会发生! image.png 报500错误了,这个是因为请求参数缺少导致在保存到数据库时出现了异常后被nestjs处理的结果。和前面手动抛出的异常(throw new HttpException(`用户${username}不存在`,401);)类似,这些不管是手动抛出的异常还是程序异常,整个应用程序中的所有抛出的异常都将由Nestjs内置的异常层处理。 image.png

自定义(异常)过滤器

  • 创建一个捕获Http异常的过滤器src/common/filter/http-exception.filter.ts,编码如下
// common/filter/http-exception.filter.ts
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

// 这里扑获Http异常,不注入参数将扑获所有异常
@Catch(HttpException)
export class HttpExceptionFilter<T> implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    // host.switchToWs(); // 获取WebSockets上下文
    const rtx = host.switchToHttp(); // 获取http请求上下文
    const response = rtx.getResponse<Response>(); // 获取上下文的response对象
    const request = rtx.getRequest<Request>(); // 获取request对象
    const status = exception.getStatus(); // 获取状态码

    // 获取异常信息
    const excepResp = exception.getResponse();

    response
      .status(status)
      .json({
        data: {},
        msg: excepResp || `${status >= 500 ? 'Service Error' : 'Client Error'}`,
        ret: -1,
        path: request.url
      });
  }
}
  • 绑定过滤器
// main.ts
import { HttpExceptionFilter } from './common/filter/http-exception.filter';
...
async function bootstrap() {
  ...
  app.useGlobalFilters(new HttpExceptionFilter())
  ...
}

绑定后再试下一下,发现我们抛出的异常返回格式已经变了。 image.png

虽然添加了一个Http过滤器,但面前缺少参数导致报的500错误并没有解决,这样因为那不属于Http异常,可以通过创建一个可以拦截所有异常的过滤器(@Catch不传参)。绑定拦截器的范围也有多种:方法范围,控制器范围或全局范围。更多信息请看文档

拦截器

前面通过HttpException异常过滤器,将Http异常请求做了统一的返回格式。那是不是可以让正常的请求也有个统一的返回格式呢,这时就可以使用拦截器了。
拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。

拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:

  • 在函数执行之前/之后绑定额外的逻辑
  • 转换从函数返回的结果
  • 转换从函数抛出的异常
  • 扩展基本函数行为
  • 根据所选条件完全重写函数 (例如, 缓存目的)

创建一个拦截器文件src/common/interceptor/transform.interceptor.ts。编码如下

// transform.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { map, Observable } from 'rxjs';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data)=>{
        return {
          data,
          msg: '请求成功!',
          ret: 0,
        }
      })
    );
  }
}

和异常过滤器一样,在main.ts中注册一下

// main.ts
import { TransformInterceptor } from './common/transform.interceptor';
...
async function bootstrap() {
  ...
  app.useGlobalInterceptors(new TransformInterceptor());
  ...
}

效果 image.png

通过过滤器拦截器,不管请求失败还是成功,都是统一格式的返回。

管道

管道是具有 @Injectable() 装饰器的类。管道应实现 PipeTransform 接口。
image.png

管道有两个类型:

  • 转换:管道将输入数据转换为所需的数据输出
  • 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常;

在这两种情况下, 管道 参数(arguments) 会由 控制器(controllers)的路由处理程序 进行处理. Nest 会在调用这个方法之前插入一个管道,管道会先拦截方法的调用参数,进行转换或是验证处理,然后用转换好或是验证好的参数调用原方法。
也就是说,可以在控制器方法调用前,通过插入管道的方式对控制器方法的参数做一些。理,然后再将处理过的参数传给控制器方法。

上面的例子,使用实体能够应对简单数据库操作,但没有对请求数据进行校验、也没有对返回数据做任何处理。这显然很不合理,比如获取用户时password最好是不返回的,而且password应加密存储。

在Nest中可以使用管道搭配数据传输对象 DTO(Data Transfer Object)实现对数据的校验功能。

数据校验

先创建src/user/dto/create-user.dto.ts文件

export class CreateUserDto {
  readonly username: string;
  readonly nickname: string;
  readonly password: string;
  readonly age: number;
}

然后给UserController的createUser方法引入该Dto

// user.controller.ts
...
@Post('create')
createUser(@Body() user: CreateUserDto) {
    return this.userService.createUser(user);
}
...

nestjs内置了一些管道,接下来就用其中一个内置管道ValidationPipe结合class-validator对参数进行校验。
先安装依赖yarn add class-validator class-transformer
然后在create-user.dto.ts使用

// create-user.dto.ts
import { IsNotEmpty, IsNumber } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty({ message: '名称不能为空' })
  readonly username: string;

  @IsNotEmpty({ message: '别名不能为空' })
  readonly nickname: string;

  @IsNotEmpty({ message: '密码不能为空' })
  readonly password: string;
  
  @IsNumber()
  readonly age: number;
}

最后还需要main.ts中注册管道ValidationPipe

app.useGlobalPipes(new ValidationPipe());

试下效果 image.png

利用管道,轻松的解决了数据校验问题,就不用为写一堆if了。更多内容请查看中文文档管道

接口文档

写接口文档往往需要花费大量时间,费时费力还容易被吐槽文档垃圾。在Nestjs中提供了专门的模块来使用Swagger,方便我们写出合格的文档(少费时力,同事还满意)。只要注解到位,就能精确表达接口和字段含义。

使用前,先安装下依赖

yarn add @nestjs/swagger swagger-ui-express

接下来就是在main.ts配置下文档信息了

// main.ts
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
...
async function bootstrap() {
  ...
  // 设置 swagger 文档
  const config = new DocumentBuilder()
    .setTitle('接口文档')
    .setDescription('nestjs 接口文档')
    .setVersion('1.0')
    .addBearerAuth()
    .build();
  const document = SwaggerModule.createDocument(app,config);
  SwaggerModule.setup('docs',app, document);
  ...
}

然后直接打开http://localhost:3000/docs/#/就能看Swagger生成的文档了。

image.png 虽然现在看路由信息,但没有接口描述信息。就这样交给前端怕不是会被打...
只要加上控制器描述(@ApiTags())、接口描述(@ApiOperation())、字段描述(@ApiProperty())。

// user.controller.ts
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger/dist";
import { CreateUserDto } from "./dto/create-user.dto";
import { UserService } from "./user.service";

@ApiTags('用户模块')
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @ApiOperation({ summary: '查询用户' })
  @Get(':username')
  getUser(@Param() params) {
    return this.userService.getUser(params.username);
  }

  @ApiOperation({ summary: '新建用户' })
  @Post('create')
  createUser(@Body() user: CreateUserDto) {
    return this.userService.createUser(user);
  }
}

// =================================================

// create-user.dto.ts
import { ApiProperty } from '@nestjs/swagger/dist/decorators';
import { IsNotEmpty, IsNumber } from 'class-validator';

export class CreateUserDto {
  @ApiProperty({ description: '名称' })
  @IsNotEmpty({ message: '名称不能为空' })
  readonly username: string;

  @ApiProperty({ description: '别名' })
  @IsNotEmpty({ message: '别名不能为空' })
  readonly nickname: string;

  @ApiProperty({ description: '密码' })
  @IsNotEmpty({ message: '密码不能为空' })
  readonly password: string;
  
  @ApiProperty({ description: '年龄' })
  @IsNumber()
  readonly age: number;
}

image.png

image.png

中间件

Nest官方文档对中间件介绍的很明了了,建议直接阅读官方文档。中间件是在路由处理程序 之前 调用的函数,Nest 中间件实际上等价于 express 中间件。 image.png

中间件可以是函数中间件或继承了NestMiddleware接口的类中间件,可在模块中注册中间件也可在全局注册

  • 定义方式1:实现NestMiddleware接口的 类中间件
    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();
      }
    }
    
  • 定义方式2:函数中间件
    export function logger(req, res, next) {
     console.log(`Request...`);
     next();
    };
    
  • 注册方式1:模块中注册
    // ...
    export class AppModule implements NestModule {
       configure(consumer: MiddlewareConsumer) {
         consumer
           .apply(LoggerMiddleware)
           // .apply(logger) 
           .forRoutes(中间件消费者);
       }
     }
    
  • 注册方式2:全局注册
    app.use(logger)
    // app.use(LoggerMiddleware)
    

守卫

守卫是一个使用 @Injectable() 装饰器的类。 守卫应该实现 CanActivate 接口。 image.png

守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。

可以通过中间件来判断某个路由是否有权限访问(即授权),同样也可以通过守卫来判断路由的权限。不同的是,中间件调用 next() 函数后会执行哪个处理程序,而守卫可以访问 ExecutionContext 实例,因此确切地知道接下来要执行什么。

注册守卫

守卫应实现CanActivate接口,有个canActivate方法同步或异步返回Boolean值。

  • 返回true:表示通过则会继续该请求
  • 返回false:表示不通过会忽略当前请求,并抛出一个 HttpException 异常

例如注册一个直接返回true的守卫,即所有请求都通过,实际开发可根据具体权限逻辑返回Boolean值

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

@Injectable()
export class TestGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

绑定守卫

需绑定才能使用守卫,与管道和异常过滤器一样,守卫可以是控制范围的、方法范围的或全局范围的。

  • 控制器范围

    @Controller()
    @UseGuards(TestGuard)
    export class AppController {
       // ...
    }
    
  • 方法范围

    @Controller()
    export class AppController {
      // ...
      @Get()
      @UseGuards(TestGuard)
      getHello(): string {
        console.log('infos', this.infos);
        return this.appService.getHello();
      }
    }
    
  • 全局范围

    app.useGlobalGuards(new TestGuard());
    

因守卫直接返回的true,请求被正常处理。
image.png

但返回false时,请抛出一个 HttpException 异常。
image.png

利用守卫,还可以根据角色权限限制接口的请求。更多详情内容可查看中文官网守卫

结尾

目前只是简单介绍了下nestjs基础内容,写的不好的地方还望海涵,如有不对的地方欢迎指出[抱拳]