Appearance
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 的依赖注入系统集成
- 可以组合使用多个装饰器
- 支持参数化装饰器
- 提供了丰富的内置装饰器