前言
Nest提供了几个实用的类,帮助您轻松编写跨多个应用程序上下文(例如基于Nest HTTP服务器的、微服务和WebSockets应用程序上下文)运行的应用程序。这些实用工具提供有关当前执行上下文的信息,可用于构建通用的守卫、过滤器和拦截器
,可以适用于广泛的控制器、方法和执行上下文。
在本章中,我们将介绍两个这样的类:ArgumentsHost
和ExecutionContext
。
ps
:细心的话可以发现,之前我们的守卫、拦截器、自定义装饰器
都有用到,这里面就主要了解一下他的使用
执行上下文
ArgumentsHost
类提供了一些方法,用于检索传递给处理程序的参数。它允许选择适当的上下文(例如HTTP、RPC(微服务)或 WebSockets)来从中检索参数
我们会在很多地方看到他的实例,这也是 nest 给我们创建好的实例,这就是我们的当前应用上下文,例如: CanActivate
、ExceptionFilter
、createParamDecorator
也就是我们前面介绍的 guard校验
、http拦截
、自定义装饰器
等等
ps
: ExecutionContext 它继承自 ArgumentsHost 哈
@Injectable()
export class UserGuard implements CanActivate {
constructor(
private reflector: Reflector,
) { }
canActivate(
context: ExecutionContext,
): boolean { }
}
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp(); // 获取请求上下文
const response = ctx.getResponse(); // 获取请求上下文中的 response对象
const status = exception.getStatus(); // 获取异常状态码
}
}
export const User = createParamDecorator(
(data: string | undefined | null, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user
return data ? user?.[data] : user
},
);
其有三个方法,我们用的最多的是 http,另外两个是看自己需要获取
/**
* Switch context to RPC.
*/
switchToRpc(): RpcArgumentsHost;
/**
* Switch context to HTTP.
*/
switchToHttp(): HttpArgumentsHost;
/**
* Switch context to WebSockets.
*/
switchToWs(): WsArgumentsHost;
其也可以帮助我们更好切换到我们需要的上下文,例如:获取我们 http 请求信息
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
//request.headers,headers.token 是不是很熟悉
const response = ctx.getResponse<Response>();
行上下文类 ExecutionContext
能够提供有关当前执行过程的更多详细信息,可以获取我们的类和方法
export interface ExecutionContext extends ArgumentsHost {
/**
* Returns the type of the controller class which the current handler belongs to.
*/
getClass<T>(): Type<T>;
/**
* Returns a reference to the handler (method) that will be invoked next in the
* request pipeline.
*/
getHandler(): Function;
}
我们在通过 @SetMetadata() 装饰器
+ Reflector 反射
直接获取到我们设置的元数据,也就是我们前面讲过的授权相关
//设置一个权限装饰器
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
//使用权限装饰器
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
//我们在guard汇总使用
@Injectable()
export class UserGuard implements CanActivate {
constructor(
private reflector: Reflector,
) { }
canActivate(
context: ExecutionContext,
): boolean {
//这个getHandler可以帮助我们获取路由函数的引用,但是不太符合我们的要求
//const roles = this.reflector.get<string[]>('roles', context.getHandler());
//我们可以通过 getClass 获取我们设置的元数据,以便于我们更好使用权限
const roles = this.reflector.get<string[]>('roles', context.getClass());
//获得权限 'admin'
}
}
使用 getAllAndOverride
合并
我们 controller 和 内部方法设置的元数据(一般都用这个),如下所示
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
使用如下所示
//设置一个权限装饰器
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
//控制器中使用权限装饰器,同时在controller和内部方法上
@Roles('user')
@Controller('user')
export class UserController {
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
}
//我们在guard汇总使用
@Injectable()
export class UserGuard implements CanActivate {
constructor(
private reflector: Reflector,
) { }
canActivate(
context: ExecutionContext,
): boolean {
//获取混合后的权限['user','admin]
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
}
}