iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0
自我挑戰組

初探後端世界-使用Node.js框架開發網頁系列 第 7

你需要知道的9個元件功能介紹 -6(Pipe)

  • 分享至 

  • xImage
  •  

Pipe

Pipe是在錯誤處理機制範圍中扮演一個檢查的角色,用來處理使用者傳入的參數.典型的應用場景有:

  1. 轉換:將輸入的數據轉為所需的數據(資料型別的轉換).
  2. 驗證:將輸入的資料進行驗證,如果成功,則可以進入到controller對應的handler裡,如果不行就拋出exception.
    由上述可以發現pipe能有效地做到隔離驗證程序與主程序.

內建的pipe

驗證類:
-ValidationPipe
型別轉換類(Parse* pipe):
-ParseIntPipe
-ParseFloatPipe
-ParseBoolPipe
-ParseArrayPipe
-ParseUUIDPipe
-ParseEnumPipe
-DefaultValuePipe
-ParseFilePipe

使用pipe

將pipe與特定路由處理程序綁在一起,以確保在該方法被調用之前運行
任務要求:驗證路由參數是否為整數(確保findUser( )接受的參數是一個整數,否則在路由處理程序被調用之前就拋出異常)
方法: 在@Param裝飾器的第一個參數帶入路由參數,第二個參數帶入用來型別轉換的ParseIntPipe,因為受到轉換,所以id型別是number

@Get(':id')
async getUser(@Param('id', ParseIntPipe) id: number) {
  return{
          id,
          user:'user123'
   };
}

api測試-1(路由參數為整數):
https://ithelp.ithome.com.tw/upload/images/20230922/20163253YkS3eLek6J.png

api測試-2(路由參數不為整數):
https://ithelp.ithome.com.tw/upload/images/20230922/20163253XsOOjYr5m6.png

自訂pipe

產生pipe的指令:
$ nest g pipe <PIPE_NAME>

產生出來的pipe程式碼骨架:

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

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

解釋說明:

  1. 是一個帶有@Injectable的類別
  2. 必須自行設計transform(value:any,metadata:ArgumentMetadata)方法,這是資料驗證與解析的地方
  3. 參數value是當前處理的方法的值(被路由處理程序接收之前)
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('不是數字');
    }
    return val;
  }
}

解釋說明:

  1. 設定一個變數val,並用parseInt將參數value的型別轉換成數字(number)
  2. 用isNaN()檢查我們的值(val)是否為數字,如果不是就是NaN,要throw BadRequestException,如果是數字則回傳該值

接著來套用自訂的ParseIntPipe

import { Controller, Get, Param} from '@nestjs/common';
import { ParseIntPipe } from '../../pipes/parse-int/parse-int.pipe';

@Controller('users')
export class UserController {
    @Get(':id')
    getUser(@Param('id',ParseIntPipe) id:number){
        return{
            id,
            user:'user123'
        };
    }
}

解釋說明:
注意ParseIntPipe的來源,如果是來自@nestjs/common就不會套用到自訂的Exception

api測試(路由參數不為整數):
https://ithelp.ithome.com.tw/upload/images/20230922/201632535B5YYqnXJW.png

ValidationPipe

ValidationPipe可以針對client端傳來的Http Body(主體資料)做驗證,檢查傳進來的資料是否符合驗證需求.

安裝class-validator、class-transformer(以yarn為例):
$ yarn add class-validator class-transformer

如何驗證物件格式-以class形式建立DTO

一、先建立一個dto資料夾,並創建一個create-user.dto.ts
https://ithelp.ithome.com.tw/upload/images/20230922/20163253jiM1oTEB0K.png

設定我們的CreateTodoDto內容:

import { IsOptional, Length, Max, Min} from "class-validator";

export class CreateUserDto{
    @Length(1,20)
    public readonly name:string;

    @Min(1)
    @Max(100)
    public readonly age:number;

    @IsOptional()
    @Length(1,100)
    public readonly about:string;
}

解釋說明:
我們依據自己的需求在 https://github.com/typestack/class-validator 中找尋需要的裝飾器套用在設定的變數上即可.
比如@Length(min,max)設定name的長度限制、@Min( )、@Max( )設定最小最大值、@isOptional( )設定about為可選填的

二、把validationPipe配置成全域:
在app.module.ts中

import { Module, ValidationPipe } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './features/user/user.module';
import { APP_PIPE } from '@nestjs/core';

@Module({
  imports: [UserModule],
  controllers: [AppController],
  providers: [AppService,
    {
      provide:APP_PIPE,
      useClass:ValidationPipe,
    }],
})
export class AppModule {}

解釋說明:
新增一個provider,其token為APP_PIPE,並使用useClass來產生ValidationPipe

三、新增api-getUser
在user.controller.ts中新增

    @Post()
    postUser(@Body() dto:CreateUserDto){
        return dto;
    }

解釋說明:
使用post裝飾器,並用postUser這個handler裡面使用@Body( )來取得Http Body,取得的dto型別為CreateTodoDto.

api測試-1(通過驗證):
https://ithelp.ithome.com.tw/upload/images/20230922/20163253oUx5CH0Kb4.png

api測試-2(無通過驗證):
https://ithelp.ithome.com.tw/upload/images/20230922/20163253ErGb1PjbSm.png

Mapped Types-常用於新增/修改同類型資料的dto

ㄧ、新增update-user.dto.ts
https://ithelp.ithome.com.tw/upload/images/20230922/20163253utZ93wZfMU.png

二、使用PartialType讓UpdateUserDto去繼承CreateUserDto,Partial類型是取用既有DTO的所有欄位並將其全轉成IsOptional( )

import { PartialType } from "@nestjs/mapped-types";
import { CreateUserDto } from "./create-user.dto";

export class UpdateUserDto extends PartialType(CreateUserDto){}

關於Mapped Types的更多資訊:https://docs.nestjs.com/openapi/mapped-types

三、新增一個Handler-UpdateUser:

    @Patch(':id')
    updateUser(@Param('id')id:string, @Body()dto:UpdateUserDto){
        return{id,...dto}
    }

解釋說明:使用Param裝飾器取得id,型別為string、使用@Body裝飾器取得dto,型別為UpdateUserDto

api測試結果:
https://ithelp.ithome.com.tw/upload/images/20230922/2016325328yGBWDpOu.png

沒有任何防護措施,帶入其他欄位(text),依然可以更新:
https://ithelp.ithome.com.tw/upload/images/20230922/20163253qPlFsFfGPS.png

解決辦法:開啟whitelist將多餘不必要的欄位過濾掉

在app.module.ts中:

 {
      provide:APP_PIPE,
      useFactory:()=>{
        return new ValidationPipe({whitelist:true})
      },
    }

api測試結果(帶入其他欄位時):
https://ithelp.ithome.com.tw/upload/images/20230922/20163253eJUiV50tIf.png


上一篇
你需要知道的9個元件功能介紹 -5(Exception & Exception filter)
下一篇
你需要知道的9個元件功能介紹 -7(Interceptor)
系列文
初探後端世界-使用Node.js框架開發網頁12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言