## 應用上下文
Nest提供了一些應用類來簡化在不同應用上下文之間編寫應用(例如Nest HTTP應用,微服務和WebSockets應用)。這些應用可以用于創建通用的守衛,過濾器和攔截器,可以工作在控制器,方法和應用上下文中。
本章包括`ArgumentsHost`和`ExecutionContext`兩個類.
## `ArgumentsHost`類
`ArgumentsHost`類提供了獲取傳遞給處理程序的參數。它允許選擇合適的上下文(例如HTTP,RPC(微服務)或者Websockets)來從框架中獲取參數。框架提供了`ArgumentsHost`的實例,作為`host`參數提供給需要獲取的地方。例如,在異常過濾器中傳入`ArgumentsHost`參數來調用`catch()`方法。
`ArgumentsHost`簡單地抽象為處理程序參數。例如,在HTTP應用中(使用`@nestjs/platform-express`時),host對象封裝了Express的`[request, response, next] `數組,`reuest`是一個`request`對象,`response`是一個`response`對象,`next`是控制應用的請求響應循環的函數。此外,在GraphQL應用中,host包含`[root, args, context, info]`數組。
## 當前應用上下文
當構建通用的守衛,過濾器和攔截器時,意味著要跨應用上下文運行,我們需要一種方法來確定我們的方法當前正在運行的應用程序類型。可以使用 `ArgumentsHost`的`getType()`方法。
```typescript
if (host.getType() === 'http') {
// do something that is only important in the context of regular HTTP requests (REST)
} else if (host.getType() === 'rpc') {
// do something that is only important in the context of Microservice requests
} else if (host.getType<GqlContextType>() === 'graphql') {
// do something that is only important in the context of GraphQL requests
}
```
> `GqlContextType`從中`@nestjs/graphql`導入。
有了可用的應用程序類型,我們可以編寫更多的通用組件,如下所示。
### `Host`處理程序參數
要獲取傳遞給處理程序的參數數組,使用host對象的`getArgs()`方法。
```typescript
const [req, res, next] = host.getArgs();
```
可以使用`getArgByIndex()`根據索引獲取指定參數:
```typescript
const request = host.getArgByIndex(0);
const response = host.getArgByIndex(1);
```
在這些例子中我們通過索引來獲取請求響應對象,這并不推薦,因為它將應用和特定上下文耦合。為了使代碼魯棒性更好,更可復用,你可以在程序中使用host對象的應用方法來切換合適的應用上下文,如下所示:
```typescript
/**
* Switch context to RPC.
*/
switchToRpc(): RpcArgumentsHost;
/**
* Switch context to HTTP.
*/
switchToHttp(): HttpArgumentsHost;
/**
* Switch context to WebSockets.
*/
switchToWs(): WsArgumentsHost;
```
使用 `switchToHttp`() 方法重寫前面的例子, `host.switchToHttp()`幫助方法調用一個HTTP應用的`HttpArgumentsHost`對象. `HttpArgumentsHost`對象有兩個有用的方法,我們可以用來提取期望的對象。我們也可以使用Express類型的斷言來返回原生的Express類型對象:
```typescript
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
```
類似地,`WsArgumentsHost`和`RpcArgumentsHost`有返回微服務和WebSockets上下文的方法,以下是`WsArgumentsHost`的方法:
```typescript
export interface WsArgumentsHost {
/**
* Returns the data object.
*/
getData<T>(): T;
/**
* Returns the client object.
*/
getClient<T>(): T;
}
```
以下是`RpcArgumentsHost`的方法:
```typescript
export interface RpcArgumentsHost {
/**
* Returns the data object.
*/
getData<T>(): T;
/**
* Returns the context object.
*/
getContext<T>(): T;
}
```
### 執行上下文類
`ExecutionContext`擴展了`ArgumentsHost`,提供有關當前執行過程的其他詳細信息。和`ArgumentsHost`類似,Nest在需要的時候提供了一個`ExecutionContext`的實例, 例如守衛的`canActivate()`方法和攔截器的`intercept()`方法,它提供以下方法:
```typescript
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;
}
```
`getHandler()`方法返回要調用的處理程序的引用。`getClass()`方法返回一個特定處理程序所屬的控制器類。例如,一個HTTP上下文,如果當前處理的是一個POST請求,在`CatsController`中綁定`create()`方法。`getHandler()`返回`create()`方法和`getClass()`方法所在的`CatsController`類的引用(不是實例)。
```typescript
const methodKey = ctx.getHandler().name; // "create"
const className = ctx.getClass().name; // "CatsController"
```
訪問對當前類和處理程序方法的引用的能力提供了極大的靈活性。最重要的是,它讓我們有機會通過`@SetMetadata()`裝飾器從守衛或攔截器中訪問元數據集。我們將在下面介紹這個用例。
### 反射和元數據
Nest提供了通過`@SetMetadata()`裝飾器將自定義元數據附加在路徑處理程序的能力。我們可以在類中獲取這些元數據來執行特定決策。
>cats.controller.ts
```typescript
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
```
> `@SetMetadata()`裝飾器從`@nestjs/common`導入。
基于上述結構,我們將`roles`元數據(`roles`是一個元數據,并且`['admin']` 是對應的值)關聯到`create()`方法。在這種情況下,不推薦直接在路徑中使用`@SetMetadata()`,而是應該如下創建自己的裝飾器:
>roles.decorator.ts
```typescript
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
```
這種方法更干凈、更易讀,并且是強類型的。我們現在可以使用自定義的`@Roles()`裝飾器,并將其應用在`create()`方法中。
>cats.controller.ts
```typescript
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
```
要訪問`roles`路徑 (自定義元數據),我們將使用`Reflector`輔助類,它由框架提供,開箱即用,從`@nestjs/core`包導入。`Reflector`可以通過常規方式注入到類:
>roles.guard.ts
```typescript
@Injectable()
export class RolesGuard {
constructor(private reflector: Reflector) {}
}
```
> `Reflector`類從`@nestjs/core`導入。
使用`get()`方法讀取處理程序的元數據。
```typescript
const roles = this.reflector.get<string[]>('roles', context.getHandler());
```
`Reflector#get`方法允許通過傳遞兩個參數簡單獲取元數據:一個元數據key和一個context(裝飾器對象)來獲取元數據。在本例中,指定的key是`roles`(向上指回`roles.decorator.ts`以及在此處調用的`SetMetadata()`方法)。context 由`context.getHandler()`提供,用于從當前路徑處理程序中獲取元數據,`getHandler()`給了我們一個到路徑處理函數的引用。
我們也可以組織我們的控制器,來從控制器層獲取元數據,以在控制器所有路徑中應用。
>cats.controller.ts
```typescript
@Roles('admin')
@Controller('cats')
export class CatsController {}
```
在本例中,要獲取控制器元數據,將`context.getClass()`作為第二個參數(將控制器類作為上下文提供以獲取元數據)來替代`context.getHandler()`:
>roles.guard.ts
```typescript
const roles = this.reflector.get<string[]>('roles', context.getClass());
```
要具備在多層提供元數據的能力,需要從多個上下文獲取與合并元數據。`Reflector`類提供兩個應用方法來幫助實現該功能。這些方法同時獲取控制器和方法元數據,并通過不同方法來合并他們。
考慮以下場景,在兩個水平應用`roles`都提供了元數據:
>cats.controller.ts
```typescript
@Roles('user')
@Controller('cats')
export class CatsController {
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
}
```
如果你想將`user`指定為默認角色,并且出于特定目的有選擇地進行覆蓋,可以使用
`getAllAndOverride()`方法。
```typescript
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
```
使用該代碼編寫守衛,在上下文中應用`create()`方法,采用上述元數據,將生成包含 `['admin']`的`roles`。
要獲取與合并元數據(該方法合并數組和對象),使用`getAllAndMerge()`方法:
```typescript
const roles = this.reflector.getAllAndMerge<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
```
這會生成包含['user', 'admin']的`roles`。
對于這兩種合并方法,傳輸元數據作為第一個參數,數組或者元數據對象上下文(例如,調用`getHandler()`和/或`getClass()`)作為第二個參數。
- 介紹
- 概述
- 第一步
- 控制器
- 提供者
- 模塊
- 中間件
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 自定義裝飾器
- 基礎知識
- 自定義提供者
- 異步提供者
- 動態模塊
- 注入作用域
- 循環依賴
- 模塊參考
- 懶加載模塊
- 應用上下文
- 生命周期事件
- 跨平臺
- 測試
- 技術
- 數據庫
- Mongo
- 配置
- 驗證
- 緩存
- 序列化
- 版本控制
- 定時任務
- 隊列
- 日志
- Cookies
- 事件
- 壓縮
- 文件上傳
- 流式處理文件
- HTTP模塊
- Session(會話)
- MVC
- 性能(Fastify)
- 服務器端事件發送
- 安全
- 認證(Authentication)
- 授權(Authorization)
- 加密和散列
- Helmet
- CORS(跨域請求)
- CSRF保護
- 限速
- GraphQL
- 快速開始
- 解析器(resolvers)
- 變更(Mutations)
- 訂閱(Subscriptions)
- 標量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 聯合類型
- 枚舉(Enums)
- 字段中間件
- 映射類型
- 插件
- 復雜性
- 擴展
- CLI插件
- 生成SDL
- 其他功能
- 聯合服務
- 遷移指南
- Websocket
- 網關
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 適配器
- 微服務
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定義傳輸器
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 獨立應用
- Cli
- 概述
- 工作空間
- 庫
- 用法
- 腳本
- Openapi
- 介紹
- 類型和參數
- 操作
- 安全
- 映射類型
- 裝飾器
- CLI插件
- 其他特性
- 遷移指南
- 秘籍
- CRUD 生成器
- 熱重載
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模塊
- Swagger
- 健康檢查
- CQRS
- 文檔
- Prisma
- 靜態服務
- Nest Commander
- 問答
- Serverless
- HTTP 適配器
- 全局路由前綴
- 混合應用
- HTTPS 和多服務器
- 請求生命周期
- 常見錯誤
- 實例
- 遷移指南
- 發現
- 誰在使用Nest?