Skip to content
On this page

NestJS 装饰器 (Decorators)

装饰器是 NestJS 框架的核心特性,它们提供了一种声明式的方式来添加元数据到类、方法、属性和参数上。装饰器是 TypeScript 的一个实验性功能,NestJS 广泛使用装饰器来实现依赖注入、路由映射、参数注入等功能。

基础概念

装饰器是一种特殊类型的声明,它可以附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression 形式,其中 expression 必须计算为一个函数,该函数在运行时被调用,并接收装饰声明的相关信息作为参数。

控制器装饰器

@Controller()

@Controller() 装饰器用于定义控制器类:

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

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

// 带前缀的控制器
@Controller('api/v1/cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

// 带主机约束的控制器
@Controller({ 
  path: 'cats', 
  host: 'admin.example.com' 
})
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

路由方法装饰器

@Get(), @Post(), @Put(), @Delete(), @Patch()

这些装饰器用于定义 HTTP 请求方法:

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'Get all cats';
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `Get cat with id: ${id}`;
  }

  @Post()
  create(@Body() createCatDto: CreateCatDto): string {
    return 'Create a new cat';
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto): string {
    return `Update cat with id: ${id}`;
  }

  @Delete(':id')
  remove(@Param('id') id: string): string {
    return `Remove cat with id: ${id}`;
  }

  @Patch(':id')
  partialUpdate(@Param('id') id: string, @Body() partialUpdateDto: PartialUpdateDto): string {
    return `Partially update cat with id: ${id}`;
  }
}

@All()

@All() 装饰器处理所有 HTTP 方法:

@Controller('cats')
export class CatsController {
  @All('*')
  handleAll(): string {
    return 'This will handle all HTTP methods';
  }
}

参数装饰器

@Request() / @Req()

注入原始 HTTP 请求对象:

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

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Request() req: any) {
    return req.headers;
  }
}

@Response() / @Res()

注入原始 HTTP 响应对象:

import { Controller, Get, Response } from '@nestjs/common';
import { Response as ExpressResponse } from 'express';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Response() res: ExpressResponse) {
    res.status(200).json({
      message: 'Success',
    });
  }
}

@Param()

提取路由参数:

@Controller('cats')
export class CatsController {
  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `Get cat with id: ${id}`;
  }

  @Get(':id/:name')
  findOneWithParams(@Param() params: any): string {
    return `Get cat with id: ${params.id} and name: ${params.name}`;
  }

  @Get('user/:userId/cat/:catId')
  findCat(
    @Param('userId') userId: string,
    @Param('catId') catId: string,
  ): string {
    return `Get cat ${catId} for user ${userId}`;
  }
}

@Query()

提取查询参数:

@Controller('cats')
export class CatsController {
  @Get()
  findAll(
    @Query('page') page: number,
    @Query('limit') limit: number,
  ): string {
    return `Get cats, page: ${page}, limit: ${limit}`;
  }

  @Get('search')
  search(@Query() query: any): string {
    return `Search cats with params: ${JSON.stringify(query)}`;
  }

  @Get('filter')
  filter(
    @Query('status') status: string,
    @Query('type') type: string,
  ): string {
    return `Filter cats by status: ${status}, type: ${type}`;
  }
}

@Body()

提取请求体数据:

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto): string {
    return `Create cat with name: ${createCatDto.name}`;
  }

  @Post('batch')
  createBatch(@Body() createCatDtos: CreateCatDto[]): string {
    return `Create ${createCatDtos.length} cats`;
  }

  @Put(':id')
  update(
    @Param('id') id: string,
    @Body('name') name: string,
    @Body('age') age: number,
  ): string {
    return `Update cat ${id} with name: ${name}, age: ${age}`;
  }
}

@Headers()

提取请求头信息:

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Headers() headers: any): string {
    return `Authorization: ${headers.authorization}`;
  }

  @Get('auth')
  authenticate(@Headers('authorization') auth: string): string {
    return `Auth header: ${auth}`;
  }
}

@Session()

提取会话信息:

@Controller('cats')
export class CatsController {
  @Get('session')
  getSession(@Session() session: any): string {
    return `Session ID: ${session.id}`;
  }
}

@Ip()

获取客户端 IP 地址:

@Controller('cats')
export class CatsController {
  @Get('ip')
  getIp(@Ip() ip: string): string {
    return `Client IP: ${ip}`;
  }
}

属性装饰器

@Get(), @Set() 访问器装饰器

虽然不是 NestJS 特有的,但可以使用 TypeScript 的访问器装饰器:

class Cat {
  private _name: string;

  @Get()
  get name(): string {
    return this._name;
  }

  @Set()
  set name(value: string) {
    this._name = value;
  }
}

依赖注入装饰器

@Injectable()

@Injectable() 装饰器标记一个类可以被 NestJS 的依赖注入系统管理:

import { Injectable } from '@nestjs/common';

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

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

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

@Inject()

@Inject() 装饰器用于注入特定的依赖项:

import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class CatsService {
  constructor(@Inject('DATABASE_CONNECTION') private dbConnection: any) {}
}

模块装饰器

@Module()

@Module() 装饰器定义一个模块:

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

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

守卫和拦截器装饰器

@UseGuards()

@UseGuards() 装饰器用于应用守卫:

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

@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
  @Get()
  findAll(): string {
    return 'Protected route';
  }
}

@UseInterceptors()

@UseInterceptors() 装饰器用于应用拦截器:

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

@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {
  @Get()
  findAll(): string {
    return 'Route with logging';
  }
}

@UsePipes()

@UsePipes() 装饰器用于应用管道:

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

@Controller('cats')
export class CatsController {
  @Post()
  @UsePipes(ValidationPipe)
  create(@Body() createCatDto: CreateCatDto): string {
    return 'Create cat with validation';
  }
}

@UseFilters()

@UseFilters() 装饰器用于应用异常过滤器:

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

@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
  @Get()
  findAll(): string {
    return 'Route with exception handling';
  }
}

管道装饰器

@UsePipes()

@UsePipes() 装饰器用于应用管道:

import { Controller, UsePipes, Post, Body, ValidationPipe } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Post()
  @UsePipes(new ValidationPipe())
  create(@Body() createCatDto: CreateCatDto): string {
    return 'Create cat with validation';
  }

  @Post('multiple')
  @UsePipes(ValidationPipe, CustomPipe)
  createMultiple(@Body() createCatDto: CreateCatDto): string {
    return 'Create cat with multiple pipes';
  }
}

异常处理装饰器

@UseFilters()

@UseFilters() 装饰器用于应用异常过滤器:

import { Controller, UseFilters, Get, NotFoundException } from '@nestjs/common';
import { AllExceptionsFilter } from './all-exceptions.filter';

@Controller('cats')
@UseFilters(AllExceptionsFilter)
export class CatsController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    if (id === '0') {
      throw new NotFoundException('Cat not found');
    }
    return `Cat ${id}`;
  }
}

元数据装饰器

@SetMetadata()

@SetMetadata() 装饰器用于设置自定义元数据:

import { SetMetadata } from '@nestjs/common';

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

// 在控制器中使用
@Controller('cats')
export class CatsController {
  @Get()
  @Roles('admin', 'user')
  findAll(): string {
    return 'This route requires admin or user role';
  }
}

自定义装饰器

创建自定义装饰器:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

// 使用自定义装饰器
@Controller('cats')
export class CatsController {
  @Get('profile')
  getProfile(@User() user: any) {
    return user;
  }
}

带参数的自定义装饰器

export const GetUser = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return data ? request.user?.[data] : request.user;
  },
);

// 使用
@Get('profile')
getProfile(@GetUser('email') email: string) {
  return email;
}

基于请求头的自定义装饰器

export const Token = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.headers.authorization?.replace('Bearer ', '');
  },
);

// 使用
@Get('secure')
secureRoute(@Token() token: string) {
  return `Token: ${token}`;
}

装饰器组合

创建复合装饰器:

import { applyDecorators, UseGuards, UseInterceptors } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
import { LoggingInterceptor } from './logging.interceptor';

export function AuthAndLog() {
  return applyDecorators(
    UseGuards(AuthGuard),
    UseInterceptors(LoggingInterceptor),
  );
}

// 使用
@Controller('cats')
export class CatsController {
  @Get()
  @AuthAndLog()
  findAll(): string {
    return 'Protected and logged route';
  }
}

装饰器工厂

创建装饰器工厂:

import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
import { RolesGuard } from './roles.guard';

export function AuthRequired(roles: string[] = []) {
  return applyDecorators(
    UseGuards(RolesGuard),
    SetMetadata('roles', roles),
  );
}

// 使用
@Controller('cats')
export class CatsController {
  @Get()
  @AuthRequired(['admin', 'user'])
  findAll(): string {
    return 'Route with required roles';
  }
}

装饰器最佳实践

1. 装饰器命名约定

// 好的命名约定
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes);

// 自定义参数装饰器
export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

2. 装饰器复用

// 创建可复用的装饰器
export const Public = () => SetMetadata('isPublic', true);

// 在控制器中使用
@Controller('cats')
export class CatsController {
  @Get('public')
  @Public()
  publicRoute(): string {
    return 'This is a public route';
  }
}

3. 装饰器验证

export const ValidateUser = createParamDecorator(
  (data: { required?: boolean } = {}, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    if (data.required && !user) {
      throw new UnauthorizedException('User is required');
    }

    return user;
  },
);

// 使用
@Get('profile')
getProfile(@ValidateUser({ required: true }) user: any) {
  return user;
}

装饰器与依赖注入

装饰器与依赖注入系统集成:

@Injectable()
export class CustomDecoratorService {
  getUserFromToken(token: string) {
    // 从token中获取用户信息
    return { id: 1, name: 'John' };
  }
}

export const CurrentUser = createParamDecorator(
  async (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const service = request.app.get(CustomDecoratorService); // 获取服务
    return service.getUserFromToken(request.headers.authorization);
  },
);

总结

NestJS 装饰器是框架的核心特性,它们提供了一种声明式的方式来配置应用程序的行为。通过使用装饰器,你可以轻松地定义路由、注入依赖、处理参数、应用中间件等。

装饰器的主要特点:

  • 提供声明式编程方式
  • 与 TypeScript 的装饰器语法兼容
  • 支持自定义装饰器创建
  • 与 NestJS 的依赖注入系统集成
  • 可以组合使用多个装饰器
  • 支持参数化装饰器
  • 提供了丰富的内置装饰器