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 是非常有必要的。当知道如何自定义装饰器后,一是可以知道装饰器的实现原理,二是可以减少代码量,通过自定义装饰器把用到的装饰器组合起来,这样就可以减少代码量了。希望本文对大家的学习有所帮助。若有不足之处,欢迎指正。

相关推荐
小白小白从不日白12 分钟前
react hooks--useCallback
前端·react.js·前端框架
恩婧20 分钟前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
mez_Blog21 分钟前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript
珊珊而川30 分钟前
【浏览器面试真题】sessionStorage和localStorage
前端·javascript·面试
森叶40 分钟前
Electron 安装包 asar 解压定位问题实战
前端·javascript·electron
drebander43 分钟前
ubuntu 安装 chrome 及 版本匹配的 chromedriver
前端·chrome
微尘81 小时前
C语言存储类型 auto,register,static,extern
服务器·c语言·开发语言·c++·后端
软件技术NINI1 小时前
html知识点框架
前端·html
计算机学姐1 小时前
基于PHP的电脑线上销售系统
开发语言·vscode·后端·mysql·编辑器·php·phpstorm
深情废杨杨1 小时前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js