NestJS管道

管道是具有@Injectable()裝飾器的類听诸。管道應(yīng)實現(xiàn)PipeTransform接口
管道有兩個功能:1. 驗證桅狠。對輸入數(shù)據(jù)進行驗證截型,驗證通過繼續(xù)傳遞趴荸,否則拋出異常。2. 轉(zhuǎn)化宦焦。將輸入數(shù)據(jù)轉(zhuǎn)化后輸出发钝。管道可以綁定在controller或其他任何方法上

Pipe

內(nèi)置管道

NestJS內(nèi)置三個管道,即ValidationPipe波闹、ParseIntPipe和ParseUUIDPipe酝豪。
shared/pipes/validation.pipe.ts

import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

PipeTransform<T, R>是一個通用接口,其中T表示value的類型精堕,R是transform()方法的返回類型孵淘。
每個管道必須提供transform()方法。這個方法接收兩個參數(shù):

  • value:當(dāng)前處理的參數(shù)
  • metadata:元數(shù)據(jù)歹篓,元數(shù)據(jù)包含一些屬性瘫证,
export interface ArgumentMetadata {
  readonly type: 'body' | 'query' | 'param' | 'custom';
  readonly metatype?: Type<any>;
  readonly data?: string;
}

參數(shù) 描述
type 告訴我們該屬性是一個body @body揉阎、query @Query、param @Parem背捌、或者自定義屬性類型
matatype 屬性元類型毙籽,如String
data 傳遞給裝飾器的字符串,如@Body('string') 中的'string'

測試用例

cats.controller.ts

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

create-cat-dto.ts

export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}

我們要確保create正確執(zhí)行毡庆,需要驗證CreateCatDTO中的三個屬性坑赡,這個工作可以交給管道來完成

基于結(jié)構(gòu)的驗證

Joi是JavaScript最強大的模式描述語言和數(shù)據(jù)驗證器

$ npm install --save @hapi/joi
$ npm install --save-dev @types/hapi__joi
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ObjectSchema } from '@hapi/joi';

@Injectable()
export class JoiValidationPipe implements PipeTransform {
  constructor(private readonly schema: ObjectSchema) {}

  transform(value: any, metadata: ArgumentMetadata) {
    const { error } = this.schema.validate(value);
    if (error) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }
}

JoiValidationPipe 的構(gòu)造函數(shù)接受一個 ObjectSchema 類型的對象,通過調(diào)用該對象的 validate 方法來驗證 value

綁定管道

create-cat.schema.ts

import Joi = require("@hapi/joi");

export const createCatSchema = Joi.object({
    name: Joi.string(),
    age: Joi.number(),
    breed: Joi.string()
})

綁定管道十分簡單么抗,使用@UsePipes()裝飾器創(chuàng)建管道實例毅否,并將其傳遞給Joi驗證

@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

基于裝飾器的驗證

$ npm i --save class-validator class-transformer

基于裝飾器的驗證通過在類中中添加一些裝飾器來達到驗證目的

create-cat.dto.ts

import { IsString, IsInt } from 'class-validator';

export class CreateCatDto {
  @IsString()
  readonly name: string;

  @IsInt()
  readonly age: number;

  @IsString()
  readonly breed: string;
}

接來下可以構(gòu)建我們的pipe類

validation.pipe.ts

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}
使用管道
方法級別管道

cat.controller.ts

@Post()
@UsePipes(new ValidationPipe())
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

在方法級別設(shè)置管道需要使用 UsePipes() 裝飾器,該裝飾器接受一個Pipe實例蝇刀,也可以傳遞一個類搀突,有框架來實例化,如下所示:

@Post()
@UsePipes(ValidationPipe)
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
參數(shù)級別管道

可以為某個參數(shù)單獨設(shè)置管道

@Post()
async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

全局管道

方法一:修改main.ts

app.useGlobalPipes(new ValidationPipe());用于設(shè)置全局管道熊泵,這種方式適用于不使用任何module提供的方法的情況仰迁,因為它本身不屬于任何一個module,要解決這個問題顽分,可以用第二種方式

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

方法二:修改app.module.ts
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe
    }
  ]
})
export class AppModule {}

轉(zhuǎn)換管道

一般在以下情形使用轉(zhuǎn)換管道徐许,1. 需要對輸入數(shù)據(jù)進行處理 2. 需要使用默認(rèn)參數(shù)

parse-int.pipe.ts

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}

上面的代碼實現(xiàn)一個字符串轉(zhuǎn)化為整數(shù)的管道

使用

@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
  return await this.catsService.findOne(id);
}

另一個有用的例子是按ID從數(shù)據(jù)庫中選擇一個現(xiàn)有的用戶實體

@Get(':id')
findOne(@Param('id', UserByIdPipe) userEntity: UserEntity) {
  return userEntity;
}

@Injectable()
export class ParseIntPipe implements PipeTransform<string, UserEntity> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = await  this.usersService.findOne(id);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}

內(nèi)置驗證管道

Nest提供許多管道,可以查看這里https://docs.nestjs.com/techniques/validation卒蘸,部分管道同時支持驗證和轉(zhuǎn)化雌隅,如ValidationPipe

@Post()
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

{ transform: true } Pipe會返回轉(zhuǎn)化后結(jié)果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缸沃,隨后出現(xiàn)的幾起案子恰起,更是在濱河造成了極大的恐慌,老刑警劉巖趾牧,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件检盼,死亡現(xiàn)場離奇詭異,居然都是意外死亡翘单,警方通過查閱死者的電腦和手機吨枉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哄芜,“玉大人貌亭,你說我怎么就攤上這事∪想” “怎么了圃庭?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我剧腻,道長斟薇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任恕酸,我火速辦了婚禮,結(jié)果婚禮上胯陋,老公的妹妹穿的比我還像新娘蕊温。我一直安慰自己,他們只是感情好遏乔,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布义矛。 她就那樣靜靜地躺著,像睡著了一般盟萨。 火紅的嫁衣襯著肌膚如雪凉翻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天捻激,我揣著相機與錄音,去河邊找鬼。 笑死曲掰,一個胖子當(dāng)著我的面吹牛符衔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丈屹,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼调俘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了旺垒?” 一聲冷哼從身側(cè)響起彩库,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎先蒋,沒想到半個月后骇钦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡竞漾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年司忱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畴蹭。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡坦仍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叨襟,到底是詐尸還是另有隱情繁扎,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站梳玫,受9級特大地震影響爹梁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜提澎,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一姚垃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盼忌,春花似錦积糯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至跨嘉,卻和暖如春川慌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祠乃。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工梦重, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亮瓷。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓忍饰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寺庄。 傳聞我的和親對象是個殘疾皇子艾蓝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359