# 控制器

初始化控制器

```bash
nest g controller [控制器名]
```

## 请求

一个请求包括请求方式，请求头，请求内容，请求参数，响应头，响应内容

```ts
// @Get()
// @Post()
// @Put()
// @Delete()
// @Patch()
// @Options()
// @Head()
// @All()
import {
  Controller,
  Get,
  Query,
  Post,
  Body,
  Put,
  Param,
  Delete,
} from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}
```

# 服务

服务负责数据存储和检索

nest g service [模块名]

```ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

// 在控制器中使用
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

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

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}
```

# 模块

每个应用程序至少有一个模块，即根模块

nest g module [模块名]

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

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

@Global
装饰@Global()器使模块具有全局范围

# 中间件

可以在函数或带有@Injectable()装饰器的类中实现自定义 Nest 中间件。类应该实现NestMiddleware接口，而函数没有任何特殊要求。让我们首先使用类方法实现一个简单的中间件功能。

```ts
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();
  }
}
```

注入中间件

```ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('cats');
  }
}
```

或者全局中间件

```ts
app.use(logger);
```

# 异常过滤器

```ts
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,
    });
  }
}
```

绑定过滤器

```ts
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}
```

全局过滤器

```ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();
```

扩展 的方法范围和控制器范围的过滤器BaseExceptionFilter不应使用 实例化new。相反，让框架自动实例化它们

```ts
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';

@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    super.catch(exception, host);
  }
}
```

# 管道

转换：将输入数据转换为所需的形式（例如，从字符串到整数）
验证：评估输入数据，如果有效，则简单地通过不变；否则，抛出异常

一个常用的类dto验证器

```ts
// 管道数据验证器
import {
  ArgumentMetadata,
  BadRequestException,
  Injectable,
  PipeTransform,
} from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, metadata: ArgumentMetadata) {
    const { metatype } = metadata;
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('参数校验失败');
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

// 注册全局管道
app.useGlobalPipes(new ValidationPipe());

// 创建具有校验的dto
import { IsNotEmpty, IsString } from 'class-validator';

export class CommonDto {
  @IsNotEmpty()
  @IsString()
  name: string;
}
```

# 卫兵

守卫是一个用@Injectable()装饰器注释的类，它实现了CanActivate接口

警卫的职责单一。它们根据运行时存在的某些条件（如权限、角色、ACL 等）确定路由处理程序是否处理给定的请求。这通常称为授权

卫兵在所有中间件之后、任何拦截器或管道之前执行

比如权限验证

```ts
// 定义卫兵
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 !!request;
  }
}

// 在控制器中使用卫兵
@UseGuards(new AuthGuard())

// 全局使用卫兵
app.useGlobalGuards(new RolesGuard());
```

## 权限元数据

```ts
// 定义接口角色
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

// 改进装饰器
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

// 在卫兵中进行鉴权
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 matchRoles(roles, user.roles);
  }
}
```

# 拦截器

@Injectable()拦截器是用装饰器注释并实现接口的类NestInterceptor

拦截器具有一组有用的功能，这些功能受到面向方面编程(AOP) 技术的启发

- 在方法执行之前/之后绑定额外的逻辑
- 转换函数返回的结果
- 转换函数抛出的异常
- 扩展基本功能行为
- 根据特定条件完全覆盖函数（例如，出于缓存目的）

# 缓存

```bash
yarn add @nestjs/cache-manager cache-manager
```

```ts
// 在指定模块中使用缓存
import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
import { AppController } from './app.controller';

@Module({
  imports: [CacheModule.register()],
  controllers: [AppController],
})
export class AppModule {}

// 缓存使用
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}

```

# 序列化

用于转换和清理要返回给客户端的数据的规则，例如，密码等敏感数据应始终从响应中排除。

```ts
import { Exclude } from 'class-transformer';

export class UserEntity {
  id: number;
  firstName: string;
  lastName: string;

  @Exclude()
  password: string;

  constructor(partial: Partial<UserEntity>) {
    Object.assign(this, partial);
  }
}
```

请注意，我们必须返回该类的一个实例。如果您返回纯 JavaScript 对象，例如 ，{ user: new UserEntity() }则该对象将无法正确序列化。

提供别名

```ts
@Expose()
get fullName(): string {
  return `${this.firstName} ${this.lastName}`;
}
```

# 任务调度

```bash
yarn add --save @nestjs/schedule
yarn add -D @types/cron
```

```ts
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

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

该.forRoot()调用初始化调度程序并注册应用程序中存在的任何声明性cron 作业、超时和间隔。当生命周期挂钩发生时，就会进行注册onApplicationBootstrap，确保所有模块都已加载并声明任何计划的作业。

示例

```ts
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('45 * * * * *')
  handleCron() {
    this.logger.debug('Called when the current second is 45');
  }
}
```

# 限流

# 日志

```ts
// 开启日志
const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn'],
});
await app.listen(3000);

@Controller()
export class AppController {
  private readonly logger = new Logger(AppController.name);

  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Post('/test')
  test(@Body() commonDto: CommonDto): string {
    console.log(commonDto.name);
    this.logger.warn('测试内容');
    return '测试';
  }
}
```

# cookie

cookie解析器

```bash
yarn add cookie-parser
yarn add -D @types/cookie-parser
```

中间件

```ts
import * as cookieParser from 'cookie-parser';
app.use(cookieParser());
```

获取cookie

```ts
@Get()
findAll(@Req() request: Request) {
  console.log(request.cookies); // or "request.cookies['cookieKey']"
  // or console.log(request.signedCookies);
}
```

设置cookie

```ts
@Get()
findAll(@Res({ passthrough: true }) response: Response) {
  response.cookie('key', 'value')
}
```

# 文件上传

```bash
yarn add -D @types/multer
```

```ts
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
  console.log(file);
}
```

## 文件验证

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

@Injectable()
export class FileSizeValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    const oneKb = 1000;
    return value.size < oneKb;
  }
}
```

# 流媒体文件

有时您可能希望将文件从 REST API 发送回客户端。要使用 Nest 执行此操作，通常您需要执行以下操作。

```ts
@Controller('file')
export class FileController {
  @Get()
  getFile(@Res() res: Response) {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    file.pipe(res);
  }
}
```

# 静态资源

```bash
yarn add hbs
```

使用了hbs（Handlebars）引擎

```ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('hbs');

  await app.listen(3000);
}
bootstrap();
```

模板渲染

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>App</title>
  </head>
  <body>
    {{ message }}
  </body>
</html>
```

渲染控制层

```ts
import { Get, Controller, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Render('index')
  root() {
    return { message: 'Hello world!' };
  }
}
```

# 安全

helmet

增加头安全

# 限流

```bash
yarn add @nestjs/throttler
```

```ts
@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60,
      limit: 10,
    }),
  ],
})
export class AppModule {}
```

定制

```ts
@SkipThrottle()
@Controller('users')
export class UsersController {}


@SkipThrottle()
@Controller('users')
export class UsersController {
  // Rate limiting is applied to this route.
  @SkipThrottle(false)
  dontSkip() {
    return "List users work with Rate limiting.";
  }
  // This route will skip rate limiting.
  doSkip() {
    return "List users work without Rate limiting.";
  }
}

@Throttle(3, 60)
@Get()
findAll() {
  return "List users works with custom rate limiting.";
}

```

保护策略

```ts
@Injectable()
export class ThrottleIpBodyGuard extends ThrottlerGuard {
  getTracker(req: Request) {
    return req.ip + req.body.username;
  }
}
```
