Nest.js 系列—-常用的装饰器以及自定义装饰器

前言

在前面的文章中,我们已经了解了 Nest.js 的基本使用,以及一些常用的模块,接下来我们来了解一下 Nest.js 中的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是有非常大的好处的。通过对自定义装饰器的学习可以知道装饰器的实现原理。

常用的装饰器

@Module

Nest 中是以模块的形式来组织代码的,@Module 装饰器用来定义一个模块,它接收一个对象,告诉 Nest 如何构建模块。

ts 复制代码
@Module({
  imports: [],
  controllers: [],
  providers: [],
  exports: []
})
export class AppModule {}
  • imports:导入模块列表,这些模块导出了本模块中的提供者,这样它们就可以在本模块中使用。
  • controllers:控制器列表,控制器的作用是处理传入的请求,并返回响应。
  • providers:提供者列表,提供者的作用是创建可以被模块中的其他提供者使用的对象。
  • exports:导出提供者列表,导出提供者的作用是创建可以被其他模块使用的模块。

@Controller

@Controller 装饰器用来定义一个控制器,它接收一个字符串参数,用来定义控制器的路由前缀。

ts 复制代码
@Controller('user')
export class CatsController {}

上面的代码定义了一个控制器,它的路由前缀是 user,那么它的路由就是/user

@Injectable

@Injectable 装饰器用来定义一个提供者,它接收一个元数据对象,告诉 Nest 如何创建提供者。

ts 复制代码
@Injectable()
export class CatsService {}

上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。使用的方式一般是在构造函数中注入,如下所示:

ts 复制代码
@Injectable()
export class CatsService {
  constructor(private catsRepository: CatsRepository) {}
}

然后在路由处理器中使用,如下所示:

ts 复制代码
@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}
}

当然也可以使用属性注入的方式,如下所示:

ts 复制代码
@Controller('cats')
export class CatsController {
  @Inject(CatsService)
  private catsService: CatsService
}

当然属性注入也有可能是 class 也有可能是 string,所以需要通过 useFactoryuseValue 来声明提供者

ts 复制代码
{
    // 提供者的keyong
  provide: 'water',
  useFactory: () => {
    return 'water';
  },
};
ts 复制代码
@Controller('cats')
export class CatsController {
  // 属性注入需要提供key
  @Inject('water')
  private catsService: CatsService
}

@Inject

@Inject 装饰器用来注入一个提供者,它接收一个字符串参数,用来指定要注入的提供者。

ts 复制代码
@Injectable()
export class CatsService {
  constructor(@Inject('CATS_REPOSITORY') private catsRepository: CatsRepository) {}
}

上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。

@Optional

@Optional 装饰器用来注入一个可选的提供者,它接收一个字符串参数,用来指定要注入的提供者。

ts 复制代码
@Injectable()
export class CatsService {
  constructor(@Optional() private configService: ConfigService) {}
}

上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。通过声明为可选的,如果没有这个提供者,也不会报错。

@Global

@Global 装饰器用来定义一个全局模块。

ts 复制代码
@Global()
@Module({
  imports: [],
  controllers: [],
  providers: [],
  exports: []
})
export class AppModule {}

@Catch

@Catch 装饰器用来定义一个过滤器,它接收一个异常类作为参数。

ts 复制代码
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse()
    const request = ctx.getRequest()

    response.status(exception.getStatus()).json({
      statusCode: exception.getStatus(),
      timestamp: new Date().toISOString(),
      path: request.url
    })
  }
}

@Get

@Get 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats

@Post

@Post 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Post()
  create(): string {
    return 'This action adds a new cat'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 POST 请求,路由路径是/cats

@Put

@Put 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Put()
  update(): string {
    return 'This action updates a cat'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 PUT 请求,路由路径是/cats

@Delete

@Delete 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Delete()
  remove(): string {
    return 'This action removes a cat'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 DELETE 请求,路由路径是/cats

@Param

@Param 装饰器用来获取路由参数,它接收一个字符串参数,用来指定路由参数的名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get(':id')
  findOne(@Param() params): string {
    console.log(params.id)
    return `This action returns a #${params.id} cat`
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats/:id,其中:id 是一个路由参数,它的值可以通过@Param 装饰器获取。

@Body

@Body 装饰器用来获取请求体,它接收一个字符串参数,用来指定请求体的属性名称。

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

上面的代码定义了一个路由处理器,它的作用是处理 POST 请求,路由路径是/cats,它通过@Body 装饰器获取请求体。

@Req

@Req 装饰器用来获取请求对象,它接收一个字符串参数,用来指定请求对象的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Req() request: Request): string {
    return 'This action returns all cats'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Req 装饰器获取请求对象。

@Res

@Res 装饰器用来获取响应对象,它接收一个字符串参数,用来指定响应对象的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Res() response: Response): string {
    return 'This action returns all cats'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Res 装饰器获取响应对象。

@Headers

@Headers 装饰器用来获取请求头,它接收一个字符串参数,用来指定请求头的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Headers() headers: Headers): string {
    console.log(headers)
    return 'This action returns all cats'
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Headers 装饰器获取请求头。

@Header

@Header 装饰器用来获取请求头,它接收一个字符串参数,用来指定请求头的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Header('authorization') authorization: string): string {
    console.log(authorization)
    return 'This action returns all cats'
  }
}

@UseGuards

@UseGuards 装饰器用来给路由添加守卫,它接收一个守卫类作为参数。

ts 复制代码
@Controller('cats')
@UseGuards(CatsGuard)
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseGuards 装饰器给路由添加了一个守卫。

@UseInterceptors

@UseInterceptors 装饰器用来给路由添加拦截器,它接收一个拦截器类作为参数。

ts 复制代码
@Controller('cats')
@UseInterceptors(CatsInterceptor)
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseInterceptors 装饰器给路由添加了一个拦截器。

@SetMetadata

@SetMetadata 装饰器用来给路由添加元数据,它接收两个参数,第一个参数是一个字符串,用来指定元数据的名称,第二个参数是一个任意类型的值,用来指定元数据的值。一般的使用方式如下:

ts 复制代码
@Controller('cats')
@SetMetadata('roles', ['admin'])
export class CatsController {}

@Session

@Session 装饰器用来获取会话对象,它接收一个字符串参数,用来指定会话对象的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Session() session: Record<string, any>) {
    session.views = (session.views || 0) + 1
    return `Views: ${session.views}`
  }
}

@HostParm

@HostParm 装饰器用来获取主机参数,它接收一个字符串参数,用来指定主机参数的名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@HostParam('account') account: string) {
    return account
  }
}

@Next

@Next 装饰器用来获取下一个处理器,它接收一个字符串参数,用来指定下一个处理器的属性名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Next() next: Function) {
    next()
  }
}

@HttpCode

@HttpCode 装饰器用来指定响应的状态码,它接收一个数字参数,用来指定响应的状态码。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  @HttpCode(204)
  findAll() {
    return 'This action returns all cats'
  }
}

@UsePipes

@UsePipes 装饰器用来给路由添加管道,它接收一个管道类作为参数。

ts 复制代码
@Controller('cats')
@UsePipes(CatsPipe)
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UsePipes 装饰器给路由添加了一个管道。

@UseFilters

@UseFilters 装饰器用来给路由添加过滤器,它接收一个过滤器类作为参数。

ts 复制代码
@Controller('cats')
@UseFilters(CatsFilter)
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseFilters 装饰器给路由添加了一个过滤器。

@Redirect

@Redirect 装饰器用来重定向路由,它接收一个字符串参数,用来指定重定向的路径。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get('docs')
  @Redirect('https://docs.nestjs.com', 302)
  getDocs(@Query('version') version) {
    if (version && version === '5') {
      return { url: 'https://docs.nestjs.com/v5/' }
    }
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats/docs,它通过@Redirect 装饰器重定向到docs.nestjs.com。

@Render

@Render 装饰器用来渲染模板,它接收一个字符串参数,用来指定模板名称。

ts 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  @Render('index')
  root() {
    return { message: 'Hello world!' }
  }
}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Render 装饰器渲染 index 模板。

三、自定义装饰器

自定义方法装饰器

在上面的内容中,我们已经了解了 Nest.js 中常用的装饰器,接下来我们来看一下如何自定义装饰器。可以通过@SetMetadata 装饰器来自定义装饰器,它接收两个参数,第一个参数是一个字符串,用来指定元数据的名称,第二个参数是一个任意类型的值,用来指定元数据的值。

ts 复制代码
import { SetMetadata } from '@nestjs/common'

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

上面的代码定义了一个装饰器,它的作用是给路由添加元数据,元数据的名称是 roles,元数据的值是一个字符串数组。这样就可以通过@Roles 装饰器给路由添加元数据了。

ts 复制代码
@Controller('cats')
@Roles('admin')
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Roles 装饰器给路由添加了一个元数据。

当用到的装饰器过多的时候,可以通过自定义装饰器的方式把用到的装饰器组合起来,这样就可以减少代码量,主要使用 applyDecorators 方法,它接收一个装饰器数组作为参数。

ts 复制代码
import { applyDecorators } from '@nestjs/common'
import { Roles } from './roles.decorator'
import { UseGuards } from '@nestjs/common'
import { AuthGuard } from './auth.guard'

export function Auth(...roles: string[]) {
  return applyDecorators(Roles(...roles), UseGuards(AuthGuard))
}

上面的代码定义了一个装饰器,它的作用是给路由添加元数据,元数据的名称是 roles,元数据的值是一个字符串数组。这样就可以通过@Roles 装饰器给路由添加元数据了。

ts 复制代码
@Controller('cats')
@Auth('admin')
export class CatsController {}

上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Auth 装饰器给路由添加了一个元数据。这样就可以减少代码量了。

自定义参数装饰器

也可以自定义参数装饰器,用来获取路由参数。

ts 复制代码
import { createParamDecorator, ExecutionContext } from '@nestjs/common'

export const User = createParamDecorator((data, req: ExecutionContext) => {
  return req.user
})

此处 data 就是传入的参数,而 ExecutionContext 是一个上下文对象,它包含了请求对象、响应对象、路由处理器等信息。

自定义类装饰器

也可以自定义类装饰器,用来给类添加元数据。

ts 复制代码
import { Controller } from '@nestjs/common'

export const water = () => Controller('water')

类装饰器也可以用 applyDecorators 方法来组合。

四、总结

本文我们了解了 Nest.js 中常用的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是非常有必要的。当知道如何自定义装饰器后,一是可以知道装饰器的实现原理,二是可以减少代码量,通过自定义装饰器把用到的装饰器组合起来,这样就可以减少代码量了。希望本文对大家的学习有所帮助。若有不足之处,欢迎指正。

相关推荐
患得患失9494 分钟前
【NestJS】NestJS三件套:校验、转换与文档生成,对比Django DRF
django·sqlite·nestjs
吃饺子不吃馅5 分钟前
【八股汇总,背就完事】这一次再也不怕webpack面试了
前端·面试·webpack
Amos_Web12 分钟前
Rust实战教程--文件管理命令行工具
前端·rust·全栈
li理21 分钟前
鸿蒙相机开发入门篇(官方实践版)
前端
webxin66622 分钟前
页面动画和延迟加载动画的实现
前端·javascript
逛逛GitHub34 分钟前
这个牛逼的股票市场平台,在 GitHub 上开源了。
前端·github
Max81235 分钟前
Agno Agent 服务端文件上传处理机制
后端
调试人生的显微镜42 分钟前
苹果 App 怎么上架?从开发到发布的完整流程与使用 开心上架 跨平台上传
后端
细节控菜鸡1 小时前
【排查实录】Web 页面能打开,服务器能通接口,客户端却访问失败?原因全在这!
运维·服务器·前端
顾漂亮1 小时前
Spring AOP 实战案例+避坑指南
java·后端·spring