什么是管道(pipe)?
管道就是一個實現(xiàn)了 PipeTransform 接口并用 @Injectable() 裝飾器修飾的類凉驻。
管道的作用簡單來說就是腻要,可以將輸入的數(shù)據(jù)處理過后輸出。
我們在前面的例子中將參數(shù)驗證的邏輯寫在了控制器中涝登,這就打破了單一責任原則雄家,控制器只應該處理請求的分發(fā),驗證的邏輯應該讓驗證器來做胀滚。
在 Nest 中正確的做法是趟济,使用管道驗證器乱投,改寫我們的 findOne 如下:
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id): Promise<User> {
return await this.usersService.findOne(id);
}
我們使用了 @nestjs/common 包中內(nèi)置的一個管道 ParseIntPipe,它將一個字符串類型的數(shù)據(jù)轉換成一個 int 類型顷编,如果失敗則拋出異常戚炫,現(xiàn)在訪問 http://127.0.0.1:3000/users/exception :
{
"statusCode":400,
"date":"2018-7-31",
"path":"/users/exception"
}
我們自定義的錯誤碼又沒了!這可不是我們想要的結果媳纬,看來還是需要自己定義管道驗證器嘹悼。
現(xiàn)在新建一個管道:
$ nest g pi users/pipes/user-id
CREATE /src/users/pipes/user-id/user-id.pipe.ts (216 bytes)
去掉 user-id/ 這一層目錄,默認生成的管道使用的還是老版本的 @Pipe 裝飾器层宫, Nest 5.0 已經(jīng)修改為 @Injectable 裝飾器了,所以我們修改生成的代碼再給它加上兩行打印語句其监,如下:
import { ArgumentMetadata, PipeTransform, Injectable } from '@nestjs/common';
@Injectable()
export class UserIdPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
console.log(value);
console.log(metadata);
return value;
}
}
PipeTransfrom 接口就只有一個 transfrom 方法萌腿, 其中的 value 參數(shù)就是我們需要處理的數(shù)據(jù)源,而 ArgumentMetadata 就是這個對象的一些 元數(shù)據(jù)(metadate)抖苦,它的接口定義是下面這樣的:
export interface ArgumentMetadata {
type: 'body' | 'query' | 'param' | 'custom';
metatype?: new (...args) => any;
data?: string;
}
這里直接引用官方文檔的說明:
TypeScript接口在編譯期間消失毁菱,所以如果你使用接口而不是類,那么元類型的值將是一個 Object锌历。
現(xiàn)在讓我們的管道工作起來贮庞,修改 findOne 如下:
@Get(':id')
async findOne(@Param('id', new UserIdPipe()) id): Promise<User> {
return await this.usersService.findOne(id);
}
訪問 http://127.0.0.1:3000/users/exception, 看到控制臺輸出:
exception
{ metatype: [Function: Object], type: 'param', data: 'id' }
我們已經(jīng)在管道中拿到了參數(shù)的值和元數(shù)據(jù)究西,我們可以寫自己的驗證邏輯然后在驗證失敗的時候讓他拋出 ApiException:
import { ArgumentMetadata, PipeTransform, Injectable, HttpStatus } from '@nestjs/common';
import { ApiException } from 'common/exceptions/api.exception';
import { ApiErrorCode } from 'common/enums/api-error-code.enum';
@Injectable()
export class UserIdPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
value = parseInt(value)
if(isNaN(value) || typeof value !== 'number' || value <= 0) {
throw new ApiException('用戶ID無效', ApiErrorCode.USER_ID_INVALID, HttpStatus.BAD_REQUEST);
}
return value;
}
}
訪問 http://127.0.0.1:3000/users/exception窗慎,發(fā)現(xiàn)我們的管道工作的很好,我們自定義的業(yè)務狀態(tài)碼也回來了啦:
{
"errorCode":10001,
"errorMessage":"用戶ID無效",
"date":"2018-8-1",
"path":"/users/exception"
}
本篇只介紹管道最基本的用法卤材,在后面我們會循序漸進的學習管道的高級用法遮斥。