常用的装饰器以及自定义装饰器
前言
本章我们来了解下 Nest.js 中的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是有非常大的好处的。通过对自定义装饰器的学习可以知道装饰器的实现原理。
常用的装饰器
@Module
在 nest 中是以模块的形式来组织代码的.@Module 装饰器用来定义一个模块,它接受一个对象,告诉 nest 如何构建模块
@Module({
imports: [],
controllers: [],
providers: [],
exports: [],
})
export class AppModule {}
- imports:导入模块列表,这些模块导出了本模块中的提供者,这样它们就可以在本模块中使用。
- controllers:控制器列表,控制器的作用是处理传入的请求,并返回响应。
- providers:提供者列表,提供者的作用是创建可以被模块中的其他提供者使用的对象。
- exports:导出提供者列表,导出提供者的作用是创建可以被其他模块使用的模块。
@Controller
@Controller 装饰器用来定义一个控制器,它接受一个字符串参数,表示控制器的路由前缀。
@Controller("cats")
export class CatsController {}
上面的代码定义了一个控制器,它的路由前缀是 cats,那么它的路由就是/user
。
@Injectable
@Injectable 装饰器用来定义一个提供者,它接受一个可选的字符串参数,表示提供者的名称。
@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
) {}
}
@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
,元数据的值是一个字符串数组。这样就可以通过@Auth
装饰器给路由添加元数据了。
@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");
注意
类装饰器也可以用 applyDecorators 方法来组合。
总结
本文我们了解了 Nest.js 中常用的装饰器,以及如何自定义装饰器。这对于熟练使用 Nest.js 是非常有必要的。
当知道如何自定义装饰器后,一是可以知道装饰器的实现原理,二是可以减少代码量,通过自定义装饰器把用到的装饰器组合起来,这样就可以减少代码量了。希望本文对大家的学习有所帮助。若有不足之处,欢迎指正。