Skip to content

常用装饰器

Nest 中实现功能,大多是通过装饰器完成,因此学习常用的装饰器就显得尤为重要。

@Controller

一般在controller文件中用于声明路由及controller

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

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

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

@Injectable、@Inject

@Injectable用于声明provider,可以是任意的class

ts
@Injectable()
export class A {
  
}

Provider可以用构造器注入

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

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

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

也可以使用@Inject用于声明属性注入

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

@Controller()
export class AppController {
  @Inject(AppService)
  private appService: AppService;

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

属性注入要注意传入的token,可能是class 也可能是string

ts
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    {
      provide: 'token',
      useFactory() {
        return {
          number: 1,
        };
      },
    },
  ],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';

@Controller()
export class AppController {
  @Inject('token')
  private readonly appService: Record<string, any>;

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

@Module

@Module() 装饰器采用单个对象,其属性描述模块:

providers将由 Nest 注入器实例化并且至少可以在该模块中共享的提供程序
controllers此模块中定义的必须实例化的控制器集
imports导出此模块所需的提供程序的导入模块列表
exports这个模块提供的 providers 的子集应该在导入这个模块的其他模块中可用。你可以使用提供器本身或仅使用其令牌(provide 值)
ts
// app.module.ts
import { Module, Global } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AaaModule } from './aaa/aaa.module';

@Global()
@Module({
  imports: [AaaModule],
  controllers: [AppController],
  providers: [AppService],
  exports: [AppService],
})
export class AppModule {}

@Optional

声明可选对象,正常情况下,如果注入的依赖为空创建对象就会报错,但声明了可选的情况下没有对应的provider也可以正常创建对象。

ts
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get, Inject, Optional } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  @Inject(AppService)
  private readonly appService: AppService;

  @Optional()
  @Inject('token')
  private readonly token: Record<string, any>;

  @Get()
  getHello() {
    return {
      hi: this.appService.getHello(),
      token: this.token,
    };
  }
}

@Global

声明全局模块

执行nest g res aaa命令,生成一个aaa模块。

ts
// aaa.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Get('hhh')
  getHi() {
    return this.appService.getHello();
  }
}

// aaa.module.ts
import { Module } from '@nestjs/common';
import { AaaService } from './aaa.service';
import { AaaController } from './aaa.controller';

@Module({
  controllers: [AaaController],
  providers: [AaaService],
})
export class AaaModule {}


// app.module.ts
import { Module, Global } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AaaModule } from './aaa/aaa.module';

@Global()
@Module({
  imports: [AaaModule],
  controllers: [AppController],
  providers: [AppService],
  exports: [AppService],
})
export class AppModule {}

这个时候的aaa.module.ts里面是没有引入AppModule的(因为没有imports),其作用的是@Global

@UseFilters

注入拦截器

执行nest g f app生成Filter,在controller中使用@UseFilters注入AppFilter

ts
// app.filter.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Response } from 'express';

@Catch(HttpException)
export class AppFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const response: Response = host.switchToHttp().getResponse();
    response.status(exception.getStatus()).json({
      msg: exception.message,
    });
  }
}

// app.controller.ts
import {
  Controller,
  Body,
  Post,
  UseFilters,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { AppDto } from './app.dto';
import { AppFilter } from './app/app.filter';

@Controller()
export class AppController {
  @Post()
  @UseFilters(AppFilter)
  getHi(@Body() body: AppDto) {
    throw new HttpException('ccxcxz', HttpStatus.BAD_REQUEST);

    return {
      body,
    };
  }
}

@UseGuards

注入守卫

执行nest g gu app生成Guard,在controller中使用@UseGuards注入AppGuard

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

@Injectable()
export class AppGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('context', context);

    return true;
  }
}

// app.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AppGuard } from './app/app.guard';

@Controller()
export class AppController {
  @Get()
  @UseGuards(AppGuard)
  getGuards() {
    return {
      content: 11,
    };
  }
}

@UseInterceptors

注入拦截器

执行nest g interceptor app生成Interceptor,在controller中使用@UseInterceptors注入AppInterceptor

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

@Injectable()
export class AppInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('context', context);

    return next.handle();
  }
}

// app.controller.ts
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { AppInterceptor } from './app/app.interceptor';

@Controller()
export class AppController {
  @Get()
  @UseInterceptors(AppInterceptor)
  getInterceptors() {
    return {
      content: 11,
    };
  }
}

@UsePipes

注入管道,使得入参转换

ts
// app.controller.ts
import { Controller, Get, ParseIntPipe, UsePipes, Query } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @UsePipes(ParseIntPipe)
  UsePipes(@Query('a') a: string) {
    console.log('a', typeof a);

    return typeof a;
  }
}

@SetMetadata

为路由处理方法设置元数据

ts
// app.gurad.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';

@Injectable()
export class AppGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    // 使用 Reflector 获取路由方法的 role 元数据
    const roles = this.reflector.getAllAndMerge('role', [
      context.getHandler(),
      context.getClass(),
    ]);
    console.log('roles', roles);

    return true;
  }
}


// app.controller.ts
import { Controller, Get, UseGuards, SetMetadata } from '@nestjs/common';
import { AppGuard } from './app/app.guard';

@Controller()
export class AppController {
  @Get()
  @UseGuards(AppGuard)
  @SetMetadata('role', ['user'])
  getGuards() {
    return {
      content: 11,
    };
  }
}

@Ip

获取请求ip

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

@Controller()
export class AppController {
  @Get()
  getIp(@Ip() ip: string) {
    return {
      ip,
    };
  }
}

@Session

获取session对象

执行npm install express-session安装express中间件

获取不到ts类型提示,则需加执行一个命令npm install @types/express-session

接下来在main.ts中引入并且启用

ts
// main.ts
import { NestFactory } from '@nestjs/core';
import * as session from 'express-session';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(
    session({
      secret: 'test',
      cookie: {
        maxAge: 10000,
      },
    }),
  );
  await app.listen(3000);
}
bootstrap();

// app.controller.ts
import { Controller, Get, Session } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  getSession(@Session() session: Record<string, any>) {
    session.visits = session.visits ? session.visits + 1 : 1;
    return {
      visits: session.visits,
    };
  }
}

这时候每次发起请求时返回的visits都会加1

@Headers

获取请求头信息

ts
// app.controller.ts
import { Controller, Get, Headers } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  getHeaders(@Headers() headers: Record<string, any>) {
    return {
      headers,
    };
  }
}

@Redirect

重定向至指定url

ts
// app.controller.ts
import { Controller, Get, Headers, Redirect } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Redirect('http://www.baidu.com')
  getHeaders(@Headers() headers: Record<string, any>) {
    return {
      headers,
    };
  }
}

@Catch

捕获异常

ts
// app.filter.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Response } from 'express';

@Catch(HttpException)
export class AppFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const response: Response = host.switchToHttp().getResponse();
    response.status(exception.getStatus()).json({
      msg: exception.message,
    });
  }
}

// app.controller.ts
import {
  Controller,
  Body,
  Post,
  UseFilters,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { AppDto } from './app.dto';
import { AppFilter } from './app/app.filter';

@Controller()
export class AppController {
  @Post()
  @UseFilters(AppFilter)
  getHi(@Body() body: AppDto) {
    throw new HttpException('ccxcxz', HttpStatus.BAD_REQUEST);

    return {
      body,
    };
  }
}

@HostParam

获取域名参数

注意:需要配合@Controller中的host字段使用,否则无法获取

将域名设置为127.:host.0.1,访问127.0.0.1:3000@HostParam会获取到:host所对应的值为0

ts
import { Controller, Get, Headers, HostParam } from '@nestjs/common';

@Controller({ host: '127.:host.0.1', path: 'aaa' })
export class AppController {
  @Get()
  getHeaders(
    @Headers() headers: Record<string, any>,
    @HostParam() host: Record<string, any>,
  ) {
    return {
      headers,
      ...host,
    };
  }
}

请求方法

@Get

设置路由为get方法

ts
import { Controller, Get, Post, Delete, Options, Put } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Get('hhh')
  getHi() {
    return this.appService.getHello();
  }
}

@Post

设置路由为post方法

ts
import { Controller, Get, Post, Delete, Options, Put } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Post('hhh')
  getHi1() {
    return this.appService.getHello();
  }
}

@Delete

设置路由为delete方法

ts
import { Controller, Get, Post, Delete, Options, Put } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Delete('hhh')
  getHi2() {
    return this.appService.getHello();
  }
}

@Put

设置路由为put方法

ts
import { Controller, Get, Post, Delete, Options, Put } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Put('hhh')
  getHi4() {
    return this.appService.getHello();
  }
}

@Options

设置路由为options方法

ts
import { Controller, Get, Post, Delete, Options, Put } from '@nestjs/common';
import { AppService } from 'src/app.service';

@Controller('aaa')
export class AaaController {
  constructor(private readonly appService: AppService) {}

  @Options('hhh')
  getHi3() {
    return this.appService.getHello();
  }
}

请求对象

处理程序通常需要访问客户端请求的详细信息。Nest 提供对底层平台 请求对象 的访问(默认为 Express)。我们可以通过将 @Req() 装饰器添加到处理程序的签名来指示 Nest 注入它来访问请求对象。

typescript
// cats.controller.ts
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return 'This action returns all cats';
  }
}

提示为了利用 express 类型(如上面的 request: Request 参数示例),请安装 @types/express 软件包。

请求对象表示 HTTP 请求,并具有请求查询字符串、参数、HTTP 标头和正文的属性(阅读更多 此处)。在大多数情况下,没有必要手动获取这些属性。我们可以使用开箱即用的专用装饰器,例如 @Body()@Query()。下面是提供的装饰器列表和它们代表的普通平台特定对象。

@Request(), @Req()req
@Response(), @Res()*****res
@Next()next
@Session()req.session
@Param(key?: string)req.params / req.params[key]
@Body(key?: string)req.body / req.body[key]
@Query(key?: string)req.query / req.query[key]
@Headers(name?: string)req.headers / req.headers[name]

@Req、@Request

两个装饰器是同一个对象,都是注入整个请求对象

ts
// app.controller.ts
import { Controller, Get, Inject, Optional, Req } from '@nestjs/common';
import { Request } from 'express';
import { AppService } from './app.service';

@Controller()
export class AppController {
  @Inject(AppService)
  private readonly appService: AppService;

  @Optional()
  @Inject('token')
  private readonly token: Record<string, any>;

  @Get()
  getHello(@Req() request: Request) {
    console.log('request', request);

    return {
      hi: this.appService.getHello(),
      token: this.token,
    };
  }
}

@Res、@Response

两个装饰器是同一个对象,都是注入整个响应对象

注意:如果注入了响应对象后,需要手动响应,不想手动响应的话需要在参数中加上{passthrough: true},否则服务器则会一直没有响应。

ts
// app.controller.ts
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';

@Controller()
export class AppController {

  @Get('hi')
  getHi(@Res() response: Response) {
    // 手动响应
    response.end('hi');
  }
  
  @Get('hello')
  getHi(@Res({passthrough: true}) response: Response) {
    console.log('response', response);
    // 无需手动响应
    return 'hello'
  }
}

@Query

获取url后的参数,如localhost:3000/?a=1,获取a为1,适用于@Get@Post

ts
// app.controller.ts
import { Controller, Get, Inject, Query } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  @Inject(AppService)
  private readonly appService: AppService;

  @Get()
  getHello(@Query('a') a: string) {
    console.log('a', typeof a);

    return {
      hi: this.appService.getHello(),
      a,
    };
  }
  
  @Post()
  getHi(@Query('b') b: string) {
    console.log('b', typeof b);

    return {
      b,
    };
  }
}

@Param

获取路径中的参数,如localhost:3000/hi/1111,获取/hi/1111中的1111

注意:需要在路由参数中插入:参数名才能获取到

ts
import { Controller, Get, Post, Param } from '@nestjs/common';

@Controller()
export class AppController {
  @Get('/hello/:d')
  getHello(@Param('d') d: string) {
    return {
      d,
    };
  }

  @Post('/hi/:c')
  getHi(@Param('c') c: string) {
    return {
      c,
    };
  }
}

@Body

如果是Post请求,可以使用@Body获取body的参数(application/x-www-form-urlencoded类型)

注意:记得新增dto

ts
// app.dto.ts
export class AppDto {
  a: string;
  b: string;
}

// app.controller.ts
import { Controller, Body, Post } from '@nestjs/common';
import { AppDto } from './app.dto';

@Controller()
export class AppController {
  @Post()
  getHi(@Body() body: AppDto) {
    return {
      body,
    };
  }
}

@Next

转发处理函数

注意:类似于@Response,如果注入了@Next对象后,需要手动响应,否则服务器则会一直没有响应。

ts
import { Controller, Get, Next } from '@nestjs/common';
import { NextFunction } from 'express';

@Controller()
export class AppController {
  @Get()
  useNext(@Next() next: NextFunction) {
    next();
    return {
      content: 11,
    };
  }

  @Get()
  useNext1() {
    return {
      content: 222,
    };
  }
}

相同路由的处理函数调用@Next后会执行下一个函数。