Nest.js 系列—-常用的装饰器以及自定义装饰器
前言
在前面的文章中,我们已经了解了 Nest.js 的基本使用,以及一些常用的模块,接下来我们来了解一下 Nest.js 中的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是有非常大的好处的。通过对自定义装饰器的学习可以知道装饰器的实现原理。
常用的装饰器
@Module
在 nest 中是以模块的形式来组织代码的,@Module 装饰器用来定义一个模块,它接收一个对象,告诉 Nest 如何构建模块。
@Module({
imports: [],
controllers: [],
providers: [],
exports: []
})
export class AppModule {}
- imports:导入模块列表,这些模块导出了本模块中的提供者,这样它们就可以在本模块中使用。
- controllers:控制器列表,控制器的作用是处理传入的请求,并返回响应。
- providers:提供者列表,提供者的作用是创建可以被模块中的其他提供者使用的对象。
- exports:导出提供者列表,导出提供者的作用是创建可以被其他模块使用的模块。
@Controller
@Controller 装饰器用来定义一个控制器,它接收一个字符串参数,用来定义控制器的路由前缀。
@Controller('user')
export class CatsController {}
上面的代码定义了一个控制器,它的路由前缀是 cats,那么它的路由就是/user。
@Injectable
@Injectable 装饰器用来定义一个提供者,它接收一个元数据对象,告诉 Nest 如何创建提供者。
@Injectable()
export class CatsService {}
上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。使用的方式一般是在构造函数中注入,如下所示:
@Injectable()
export class CatsService {
constructor(private catsRepository: CatsRepository) {}
}
然后在路由处理器中使用,如下所示:
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
}
当然也可以使用属性注入的方式,如下所示:
@Controller('cats')
export class CatsController {
@Inject(CatsService)
private catsService: CatsService
}
当然属性注入也有可能是 class 也有可能是 string,所以需要通过 useFactory、useValue 来声明提供者
{
// 提供者的keyong
provide: 'water',
useFactory: () => {
return 'water';
},
};
@Controller('cats')
export class CatsController {
// 属性注入需要提供key
@Inject('water')
private catsService: CatsService
}
@Inject
@Inject 装饰器用来注入一个提供者,它接收一个字符串参数,用来指定要注入的提供者。
@Injectable()
export class CatsService {
constructor(@Inject('CATS_REPOSITORY') private catsRepository: CatsRepository) {}
}
上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。
@Optional
@Optional 装饰器用来注入一个可选的提供者,它接收一个字符串参数,用来指定要注入的提供者。
@Injectable()
export class CatsService {
constructor(@Optional() private configService: ConfigService) {}
}
上面的代码定义了一个提供者,它的作用是创建一个 CatsService 的实例,这个实例可以被其他模块中的提供者使用。通过声明为可选的,如果没有这个提供者,也不会报错。
@Global
@Global 装饰器用来定义一个全局模块。
@Global()
@Module({
imports: [],
controllers: [],
providers: [],
exports: []
})
export class AppModule {}
@Catch
@Catch 装饰器用来定义一个过滤器,它接收一个异常类作为参数。
@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 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats。
@Post
@Post 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。
@Controller('cats')
export class CatsController {
@Post()
create(): string {
return 'This action adds a new cat'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 POST 请求,路由路径是/cats。
@Put
@Put 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。
@Controller('cats')
export class CatsController {
@Put()
update(): string {
return 'This action updates a cat'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 PUT 请求,路由路径是/cats。
@Delete
@Delete 装饰器用来定义一个路由处理器,它接收一个字符串参数,用来指定路由的路径。
@Controller('cats')
export class CatsController {
@Delete()
remove(): string {
return 'This action removes a cat'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 DELETE 请求,路由路径是/cats。
@Param
@Param 装饰器用来获取路由参数,它接收一个字符串参数,用来指定路由参数的名称。
@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 装饰器用来获取请求体,它接收一个字符串参数,用来指定请求体的属性名称。
@Controller('cats')
export class CatsController {
@Post()
create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 POST 请求,路由路径是/cats,它通过@Body 装饰器获取请求体。
@Req
@Req 装饰器用来获取请求对象,它接收一个字符串参数,用来指定请求对象的属性名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
return 'This action returns all cats'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Req 装饰器获取请求对象。
@Res
@Res 装饰器用来获取响应对象,它接收一个字符串参数,用来指定响应对象的属性名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@Res() response: Response): string {
return 'This action returns all cats'
}
}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Res 装饰器获取响应对象。
@Headers
@Headers 装饰器用来获取请求头,它接收一个字符串参数,用来指定请求头的属性名称。
@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 装饰器用来获取请求头,它接收一个字符串参数,用来指定请求头的属性名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@Header('authorization') authorization: string): string {
console.log(authorization)
return 'This action returns all cats'
}
}
@UseGuards
@UseGuards 装饰器用来给路由添加守卫,它接收一个守卫类作为参数。
@Controller('cats')
@UseGuards(CatsGuard)
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseGuards 装饰器给路由添加了一个守卫。
@UseInterceptors
@UseInterceptors 装饰器用来给路由添加拦截器,它接收一个拦截器类作为参数。
@Controller('cats')
@UseInterceptors(CatsInterceptor)
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseInterceptors 装饰器给路由添加了一个拦截器。
@SetMetadata
@SetMetadata 装饰器用来给路由添加元数据,它接收两个参数,第一个参数是一个字符串,用来指定元数据的名称,第二个参数是一个任意类型的值,用来指定元数据的值。一般的使用方式如下:
@Controller('cats')
@SetMetadata('roles', ['admin'])
export class CatsController {}
@Session
@Session 装饰器用来获取会话对象,它接收一个字符串参数,用来指定会话对象的属性名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@Session() session: Record<string, any>) {
session.views = (session.views || 0) + 1
return `Views: ${session.views}`
}
}
@HostParm
@HostParm 装饰器用来获取主机参数,它接收一个字符串参数,用来指定主机参数的名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@HostParam('account') account: string) {
return account
}
}
@Next
@Next 装饰器用来获取下一个处理器,它接收一个字符串参数,用来指定下一个处理器的属性名称。
@Controller('cats')
export class CatsController {
@Get()
findAll(@Next() next: Function) {
next()
}
}
@HttpCode
@HttpCode 装饰器用来指定响应的状态码,它接收一个数字参数,用来指定响应的状态码。
@Controller('cats')
export class CatsController {
@Get()
@HttpCode(204)
findAll() {
return 'This action returns all cats'
}
}
@UsePipes
@UsePipes 装饰器用来给路由添加管道,它接收一个管道类作为参数。
@Controller('cats')
@UsePipes(CatsPipe)
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UsePipes 装饰器给路由添加了一个管道。
@UseFilters
@UseFilters 装饰器用来给路由添加过滤器,它接收一个过滤器类作为参数。
@Controller('cats')
@UseFilters(CatsFilter)
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@UseFilters 装饰器给路由添加了一个过滤器。
@Redirect
@Redirect 装饰器用来重定向路由,它接收一个字符串参数,用来指定重定向的路径。
@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 装饰器重定向到https://docs.nestjs.com。
@Render
@Render 装饰器用来渲染模板,它接收一个字符串参数,用来指定模板名称。
@Controller('cats')
export class CatsController {
@Get()
@Render('index')
root() {
return { message: 'Hello world!' }
}
}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Render 装饰器渲染 index 模板。
三、自定义装饰器
自定义方法装饰器
在上面的内容中,我们已经了解了 Nest.js 中常用的装饰器,接下来我们来看一下如何自定义装饰器。可以通过@SetMetadata 装饰器来自定义装饰器,它接收两个参数,第一个参数是一个字符串,用来指定元数据的名称,第二个参数是一个任意类型的值,用来指定元数据的值。
import { SetMetadata } from '@nestjs/common'
export const Roles = (...roles: string[]) => SetMetadata('roles', roles)
上面的代码定义了一个装饰器,它的作用是给路由添加元数据,元数据的名称是 roles,元数据的值是一个字符串数组。这样就可以通过@Roles 装饰器给路由添加元数据了。
@Controller('cats')
@Roles('admin')
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Roles 装饰器给路由添加了一个元数据。
当用到的装饰器过多的时候,可以通过自定义装饰器的方式把用到的装饰器组合起来,这样就可以减少代码量,主要使用 applyDecorators 方法,它接收一个装饰器数组作为参数。
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 装饰器给路由添加元数据了。
@Controller('cats')
@Auth('admin')
export class CatsController {}
上面的代码定义了一个路由处理器,它的作用是处理 GET 请求,路由路径是/cats,它通过@Auth 装饰器给路由添加了一个元数据。这样就可以减少代码量了。
自定义参数装饰器
也可以自定义参数装饰器,用来获取路由参数。
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
export const User = createParamDecorator((data, req: ExecutionContext) => {
return req.user
})
此处 data 就是传入的参数,而 ExecutionContext 是一个上下文对象,它包含了请求对象、响应对象、路由处理器等信息。
自定义类装饰器
也可以自定义类装饰器,用来给类添加元数据。
import { Controller } from '@nestjs/common'
export const water = () => Controller('water')
TIP
类装饰器也可以用 applyDecorators 方法来组合。
四、总结
本文我们了解了 Nest.js 中常用的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是非常有必要的。当知道如何自定义装饰器后,一是可以知道装饰器的实现原理,二是可以减少代码量,通过自定义装饰器把用到的装饰器组合起来,这样就可以减少代码量了。希望本文对大家的学习有所帮助。若有不足之处,欢迎指正。