iT邦幫忙

2023 iThome 鐵人賽

0
自我挑戰組

用ChatGPT詠唱來完成工作與點亮前後端技能樹系列 第 45

【Day45】ChatGPT請教教我:NestJS!(四)- Controller & Middleware!

  • 分享至 

  • xImage
  •  

【Day43】ChatGPT請教教我:NestJS!(二)- Controller & 裝飾器 的尾聲
ChatGPT提到了Controller的進階內容,就是中間件的處理

中間件是什麼?ChatGPT給我們的關鍵字有下列這些

  1. Pipes
  2. Guards
  3. Interceptors
  4. Filters
    我們就一步一步了解NestJS的中間件
    當然,中間件並不是一個服務中完全必要的東西,就如ChatGPT所說,他是很進階的內容
    因此如果初學要真的把這個部分給跳掉也不是太大的問題(大概)

總之,NestJS Controller的最後一哩路,Go Go!

Middleware 介紹

https://ithelp.ithome.com.tw/upload/images/20231029/20135750a2PbGNEBtl.png

當然可以!在 NestJS 中,Middleware 是一個非常重要的概念,它允許你在請求進入控制器之前或離開控制器之後進行處理。這對於處理驗證、日誌記錄等工作非常有用。

以下是關於 NestJS Middleware 的詳細介紹:

Middleware 是什麼?

Middleware 是一個函數,它在請求進入控制器之前或離開控制器之後執行。它有能力修改請求或回應物件,也可以執行一些額外的邏輯。

在 NestJS 中如何使用 Middleware?

在 NestJS 中使用 Middleware 非常容易,你可以通過 @nestjs/common 模組中的 MiddlewareBuilder 來建立和套用 Middleware。

創建一個 Middleware:

首先,你需要創建一個 Middleware 類別。這個類別需要實現 NestMiddleware 接口。你可以在這個類別中定義一些中間件邏輯。

範例程式碼:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

套用 Middleware:

接著,你需要將 Middleware 套用到你的路由或控制器上。你可以在模組中使用 configure() 方法,也可以使用 @UseMiddleware() 裝飾器來套用 Middleware。

範例程式碼:

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';

@Module({
  // 模組的定義
})
export class YourModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'your-route', method: RequestMethod.ALL });
  }
}

Middleware 的執行順序:

在套用多個 Middleware 時,它們的執行順序與它們在 configure() 方法中套用的順序相同。

結論:

Middleware 是 NestJS 中一個非常強大的功能,可以用於處理請求前後的邏輯。它可以幫助你實現驗證、日誌記錄等功能,使代碼更加模組化和可維護。

希望這份簡介對你有所幫助!如果你有任何問題或需要進一步的說明,請隨時告訴我。

https://ithelp.ithome.com.tw/upload/images/20231029/201357503pP4odjU8v.png

當然,我會提供一個情境和相對應的範例,讓你更容易理解如何使用 Middleware。

情境:

假設你正在開發一個網路應用程式,這個應用程式需要驗證使用者的身份以確保只有授權的使用者才能訪問某些路由。你想要在每次請求進入控制器之前進行這個驗證。

範例:

首先,你需要建立一個 Middleware 來處理驗證:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    // 在這裡進行身份驗證的邏輯
    const token = req.headers.authorization;

    if (token !== 'valid_token') {
      res.status(401).send('Unauthorized');
      return;
    }

    // 驗證通過,繼續處理請求
    next();
  }
}

接著,在你的模組中套用這個 Middleware:

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { AuthMiddleware } from './auth.middleware';

@Module({
  // 模組的定義
})
export class YourModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AuthMiddleware)
      .forRoutes({ path: 'secured-route', method: RequestMethod.ALL });
  }
}

在這個情境中,當有人訪問 'secured-route' 時,AuthMiddleware 會先執行,它會檢查請求的授權標頭,如果驗證通過,則允許請求繼續處理,否則返回 401 Unauthorized。

這是一個簡單的範例,示範了如何使用 Middleware 來進行身份驗證。你可以根據自己的需求,定義不同的 Middleware 來處理不同的情境。希望這個範例對你有所幫助!如果你有任何問題或需要進一步的說明,請隨時告訴我。

https://ithelp.ithome.com.tw/upload/images/20231029/20135750Uhkc1POjBl.png

在 NestJS 中,與 Middleware 相關的概念還包括以下幾點:

  1. Exception Filters (例外過濾器):
    • 當應用程序拋出異常時,可以使用例外過濾器來捕獲和處理它們。這可以幫助你統一處理異常情況,提供一致的錯誤響應。
  2. Guards (守衛):
    • 守衛是一種用於保護路由的機制,它可以在進入控制器之前進行請求的預處理,並根據特定條件決定是否允許進入控制器。例如,可以用於身份驗證。
  3. Pipes (管道):
    • 管道是用於處理請求數據的機制,它可以在請求進入控制器之前進行數據的轉換、驗證等處理。
  4. Interceptors (攔截器):
    • 攔截器可以在請求處理過程中對請求和回應進行全局的轉換、修改,或者附加額外的邏輯。
  5. Filters (過濾器):
    • 過濾器可以用於在請求進入控制器之後,回傳結果之前進行後處理,例如對控制器回傳的數據進行統一格式化。
  6. Pipes, Guards, Interceptors, Filters (PGIF) 之間的執行順序:
    • PGIF 是一個常用的縮寫,表示 Pipes、Guards、Interceptors 和 Filters。它們在請求處理過程中的執行順序是有規則的,可以按照特定的優先順序進行執行。
  7. 局部 Middleware:
    • 你可以將 Middleware 套用到特定的路由或控制器上,以限制它們的作用範圍。
  8. 全局 Middleware:
    • 你也可以將 Middleware 配置為全局套用於整個應用程式,這樣它將對所有路由起作用。

這些都是與 Middleware 相關的一些重要概念。它們允許你在請求進入控制器之前或之後進行各種額外處理,使得應用程式的邏輯更加模組化和可維護。如果你對這些概念中的任何一個有特定的問題,請隨時告訴我,我會樂意提供更多詳細的說明。

OK! 詳細用 Middleware 這個關鍵字去詢問,又得到更多相關的內容
除了一開始提到的PGIF之外,還有額外提到Exception Filters
我們就照上面列表的順序一個一個了解

至於有關「手動拋出例外」的介紹,會和後面的service章節放一起
先來看看例外過濾器,當收到例外時可以怎麼統一處理

Exception Filters (例外過濾器

https://ithelp.ithome.com.tw/upload/images/20231029/20135750O22dU21bSd.png

當然,讓我來為你提供關於 NestJS 中的例外過濾器(Exception Filters)的詳細介紹和教學。

什麼是例外過濾器?

例外過濾器是 NestJS 中處理異常的機制。當在應用程序的執行過程中拋出異常時,例外過濾器可以用於捕獲這些異常,並且你可以在這裡進行自定義的處理。

創建一個例外過濾器:

首先,你需要創建一個例外過濾器類別。這個類別需要實現 ExceptionFilter 接口。你可以在這個類別中定義處理異常的邏輯。

範例程式碼:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Response, Request } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

使用例外過濾器:

接著,你需要在你的控制器或模組中套用這個例外過濾器。你可以在控制器類別或模組中使用 @Catch() 裝飾器來套用例外過濾器。

範例程式碼:

import { Controller, Get, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';

@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
  @Get()
  findAll() {
    throw new ForbiddenException();
  }
}

在這個範例中,當在 'cats' 路由中調用 findAll 方法時,如果拋出了 ForbiddenException,則會觸發 HttpExceptionFilter 中的 catch 方法。

結論:

例外過濾器允許你捕獲並處理在應用程序執行期間拋出的異常。你可以在這裡定義自定義的處理邏輯,並返回適當的回應。這對於統一處理錯誤情況非常有用。

希望這份教學對你有所幫助!如果你有任何問題或需要進一步的說明,請隨時告訴我。

先大概知道可以像這樣掛載例外處理的裝飾器,有關拋出例外的部分我們後續再討論
我們繼續下一個

Guards (守衛

https://ithelp.ithome.com.tw/upload/images/20231029/20135750a8KbxccrME.png

當然,讓我來為你提供關於 NestJS 中的守衛(Guards)的詳細介紹和教學。

什麼是守衛(Guards)?

守衛是 NestJS 中一種用於保護路由的機制。它可以在進入控制器之前對請求進行預處理,並根據特定條件來決定是否允許進入控制器。

創建一個守衛:

首先,你需要創建一個守衛類別。這個類別可以實現 CanActivateCanActivateContext 或者其他相關的守衛接口。你可以在這個類別中定義守衛的邏輯。

範例程式碼:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  private validateRequest(request): boolean {
    // 在這裡進行身份驗證的邏輯
    const token = request.headers.authorization;

    if (token !== 'valid_token') {
      return false;
    }

    return true;
  }
}

使用守衛:

接著,你需要在你的控制器或模組中套用這個守衛。你可以在控制器類別或模組中使用 @UseGuards() 裝飾器來套用守衛。

範例程式碼:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
  @Get()
  findAll() {
    // 這個控制器只會在 AuthGuard 驗證通過後才執行
    return 'This action returns all cats';
  }
}

在這個範例中,當在 'cats' 路由中調用 findAll 方法時,會先執行 AuthGuard,只有當身份驗證通過時,才會執行控制器中的邏輯。

結論:

守衛允許你在進入控制器之前對請求進行預處理,並根據特定的條件來決定是否允許進入控制器。這對於實現身份驗證、權限控制等功能非常有用。

希望這份教學對你有所幫助!如果你有任何問題或需要進一步的說明,請隨時告訴我。

這邊有一個疑問,Guards和一開始說的Middleware,都有提到「身份驗證」
這兩個作用差異在哪裡?

Guards vs. Middleware

https://ithelp.ithome.com.tw/upload/images/20231029/20135750fzpJzq8UTi.png

Middleware 和 Guards 在 NestJS 中確實都可以用於身份驗證,但它們之間有一些重要的區別。

Middleware 與 Guards 的差異:

  1. 用途
    • Middleware: Middleware 是一個函數,它可以在請求進入控制器之前或離開控制器之後執行,用於執行一些額外的邏輯,如身份驗證、日誌記錄等。
    • Guards: 守衛是一種用於保護路由的機制,它可以在進入控制器之前進行請求的預處理,並根據特定條件來決定是否允許進入控制器,通常用於身份驗證和權限控制。
  2. 套用範圍
    • Middleware: Middleware 可以被套用到整個應用程式、特定的模組、路由或控制器上,具有較大的彈性。
    • Guards: 守衛通常被套用到特定的路由或控制器上,用於對進入控制器的請求進行檢查。
  3. 返回值
    • Middleware: Middleware 可以在請求進入控制器之前或離開控制器之後修改請求或回應物件,但不會對請求的授權進行直接影響。
    • Guards: 守衛的主要目的是進行請求的預處理,並根據特定條件來決定是否允許進入控制器。守衛可以返回 true 表示允許進入,或返回 false 或拋出異常來阻止進入。
  4. 錯誤處理
    • Middleware: Middleware 可以捕獲並處理請求進入控制器之前或離開控制器之後的異常。
    • Guards: 守衛也可以捕獲並處理異常,但通常用於特定的請求預處理,例如身份驗證錯誤。

總的來說,Middleware 更適合於執行較為通用的邏輯,例如日誌記錄、壓縮、解析等,而 Guards 更適合於實現特定的身份驗證、權限控制等功能。

在實際應用中,通常會根據需求和場景來選擇使用 Middleware 或 Guards,甚至可以同時使用它們來實現更全面的功能。

所以Middleware比較泛用,主要是處理額外的邏輯,可以處理一些額外的邏輯像是Log紀錄

在我的理解上,Guards也是一種Middleware,只是作用比較單一與進階
通常處理身份驗證,阻擋不合法或是過期的憑證等等
處理授權請求進入的時候,還可以選擇套用範圍是整個控制器還是特定路由
https://ithelp.ithome.com.tw/upload/images/20231029/20135750XgtmhiO9FD.png

是的,既可以將 Middleware 和 Guards 套用在整個控制器上,也可以僅套用在特定的路由上。這取決於你的需求和場景。

套用在整個控制器上的情境:

情境
假設你開發了一個需要驗證身份的應用程式,而每一個路由都需要驗證用戶的登錄狀態。

使用 Guards:

// auth.guard.ts

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  private validateRequest(request): boolean {
    // 在這裡進行身份驗證的邏輯
    const token = request.headers.authorization;

    if (token !== 'valid_token') {
      return false;
    }

    return true;
  }
}
// cats.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
@UseGuards(AuthGuard) // 將守衛套用在整個控制器上
export class CatsController {
  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}

在這個情境中,AuthGuard 被套用在 CatsController 上,這意味著所有的路由都會受到這個守衛的保護,需要通過身份驗證才能進入。

套用在特定路由上的情境:

情境
假設你開發了一個購物網站,大部分路由都可以公開訪問,但某些特定的路由(如結帳頁面)需要用戶先登錄才能訪問。

使用 Guards:

// auth.guard.ts(與前述相同)

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  // ...
}
// checkout.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('checkout')
@UseGuards(AuthGuard) // 將守衛套用在特定控制器上
export class CheckoutController {
  @Get()
  processOrder() {
    return 'Processing your order...';
  }
}

在這個情境中,AuthGuard 被套用在 CheckoutController 上,這表示只有進入 checkout 路由時,才會進行身份驗證。

總的來說,你可以根據具體的需求,選擇是在整個控制器上套用 Middleware 或 Guards,還是僅在特定路由上套用。這樣可以更好地滿足你的應用程式的功能和安全性需求。

Guards 先介紹到這邊,我們繼續下面一位~

Pipes(管道)

https://ithelp.ithome.com.tw/upload/images/20231029/201357505kubs6RtpX.png

當然,讓我為你提供 NestJS 中的 Pipes(管道)的詳細介紹和教學。

什麼是 Pipes(管道)?

在 NestJS 中,Pipes 是一種用於處理請求數據的機制。它們可以在請求進入控制器之前對請求的數據進行轉換、驗證等處理。

主要目的與功能:

  1. 數據轉換
    • Pipes 允許你在請求進入控制器之前對數據進行轉換。例如,將請求中的數字字符串轉換為實際的數字。
  2. 數據驗證
    • 可以使用 Pipes 來驗證請求中的數據是否符合特定的條件。如果驗證失敗,可以拋出異常來中斷請求的執行。
  3. 全局數據轉換和驗證
    • Pipes 可以在整個應用程序範圍內套用,從而實現對所有路由或控制器的統一數據處理。

應用情境與範例:

情境
假設你正在開發一個購物網站,用戶可以提交訂單,但你想要確保訂單的數量不小於1。

使用 Pipes 進行數據驗證:

首先,你需要創建一個自定義的 Pipe:

// validate-quantity.pipe.ts

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

@Injectable()
export class ValidateQuantityPipe implements PipeTransform<string, number> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const quantity = parseInt(value, 10);

    if (isNaN(quantity) || quantity < 1) {
      throw new BadRequestException('Invalid quantity. Quantity must be a positive number.');
    }

    return quantity;
  }
}

然後,在你的控制器中使用這個 Pipe:

// orders.controller.ts

import { Controller, Post, Body, UsePipes } from '@nestjs/common';
import { ValidateQuantityPipe } from './validate-quantity.pipe';

@Controller('orders')
export class OrdersController {
  @Post()
  @UsePipes(ValidateQuantityPipe) // 使用 Pipe 進行數據驗證
  createOrder(@Body('quantity') quantity: number) {
    return `Order created with quantity: ${quantity}`;
  }
}

在這個範例中,ValidateQuantityPipe 用於驗證請求中的 quantity,確保它是一個大於0的有效數字。如果驗證失敗,將拋出 BadRequestException

這就是使用 Pipes 進行數據驗證的一個範例。 Pipes 還可以進行更多的數據轉換和驗證操作,具體取決於你的需求。

希望這份教學對你有所幫助!如果你有任何問題或需要進一步的說明,請隨時告訴我。

那pips又和我們上個章節的 【Day44】ChatGPT請教教我:NestJS!(三)- Controller & 資料驗證 !class-validator & class-transformer! 微妙的相似
來搞清楚一下有關驗證和轉換,和這兩個套件的差異

Pipes vs. class-validator

https://ithelp.ithome.com.tw/upload/images/20231029/20135750SxzCYmM0aB.png

使用 Pipes 和 class-validator 都可以用於數據驗證,但它們之間有一些重要的區別:

Pipes 的數據驗證:

  • 主要功能
    • Pipes 是 NestJS 提供的一種機制,用於處理請求數據。透過自定義的 PipeTransform 接口實現,可以在請求進入控制器之前對數據進行轉換、驗證等處理。
  • 範例
    • 你可以創建一個自定義的 Pipe 來進行特定的數據驗證,如上一個回答中的範例所示。

class-validator 的數據驗證:

  • 主要功能
    • class-validator 是一個獨立的庫,它提供了一種基於裝飾器和純 TypeScript 的方式來進行對象的驗證。它可以用於 NestJS 或其他 TypeScript 應用程序中。
  • 範例
    • 使用 class-validator,你可以定義一個普通的 TypeScript 類別,並在其中使用裝飾器來定義驗證規則。例如:
import { IsInt, Min } from 'class-validator';

export class CreateCatDto {
  @IsInt()
  @Min(1)
  quantity: number;
}

在這個範例中,quantity 屬性被定義為整數並且必須大於或等於1。

主要差異:

  1. 依賴關係
    • Pipes 是 NestJS 提供的內建功能,與 NestJS 緊密結合,適用於 NestJS 應用程序。
    • class-validator 是一個獨立的庫,可以在任何 TypeScript 應用程序中使用,不僅限於 NestJS。
  2. 使用方式
    • Pipes 通過實現 NestJS 提供的 PipeTransform 接口來定義自定義的 Pipe。
    • class-validator 使用裝飾器來定義驗證規則,可以直接套用在類別的屬性上。
  3. 範例
    • Pipes 的範例請參考前一個回答中的情境和範例。
    • class-validator 的範例已在上方提供。

總的來說,兩者都可以用於數據驗證,但適用的情境和用法略有不同。如果你是在 NestJS 中開發應用程序,可以考慮使用 Pipes;如果你想在任何 TypeScript 應用程序中進行對象的驗證,可以考慮使用 class-validator。

Pipes vs. class-transformer

https://ithelp.ithome.com.tw/upload/images/20231029/20135750K6Ln2se7hc.png

使用 Pipes 和 class-transformer 都可以進行數據轉換,它們之間的主要區別如下:

Pipes 的數據轉換:

  • 主要功能
    • Pipes 是 NestJS 提供的一種機制,用於處理請求數據。可以在請求進入控制器之前對數據進行轉換,例如將請求的數字字符串轉換為實際的數字。
  • 範例
    • 如前述數據驗證的範例,可以將數字字符串轉換為實際的數字。

class-transformer 的數據轉換:

  • 主要功能
    • class-transformer 是一個獨立的庫,它提供了一種基於裝飾器和純 TypeScript 的方式來進行對象的轉換。它可以用於 NestJS 或其他 TypeScript 應用程序中。
  • 範例
    • 使用 class-transformer,你可以定義一個普通的 TypeScript 類別,並在其中使用裝飾器來定義對象的轉換規則。例如:
import { Type } from 'class-transformer';

export class CreateCatDto {
  name: string;

  @Type(() => Number)
  age: number;
}

在這個範例中,age 屬性將被強制轉換為數字類型。

主要差異:

  1. 依賴關係
    • Pipes 是 NestJS 提供的內建功能,與 NestJS 緊密結合,適用於 NestJS 應用程序。
    • class-transformer 是一個獨立的庫,可以在任何 TypeScript 應用程序中使用,不僅限於 NestJS。
  2. 使用方式
    • Pipes 通過實現 NestJS 提供的 PipeTransform 接口來定義自定義的 Pipe。
    • class-transformer 使用裝飾器來定義對象的轉換規則,可以直接套用在類別的屬性上。
  3. 範例
    • Pipes 的範例請參考前一個回答中的情境和範例。
    • class-transformer 的範例已在上方提供。

同時使用 Pipes 和 class-transformer:

如果你同時使用 Pipes 和 class-transformer,它們的執行順序如下:

  1. Pipes 先於 class-transformer 執行
    • 首先,Pipes 將在請求進入控制器之前進行數據處理,包括轉換、驗證等。
  2. class-transformer 接著執行
    • 之後,class-transformer 將根據定義的轉換規則進行對象的轉換。

總的來說,兩者可以共同使用來實現更全面的數據處理和轉換。具體使用哪一種方式取決於你的需求和偏好。

內建 Pipe

還記得上一篇,我們處理了GET Method中,參數是boolean的處理嗎
方法是GET並用@Query 取得參數的時候
任何參數都會被當作是字串來處理

正常來說經過class-transformer,字串數字也可以被正確轉成數字型態

但是boolean就不一樣了, 因為傳入的參數false被當作字串 'false'
使用內建的隱式轉換型別,不管像是 if ('false') 還是Boolean('false')
只要是非空字串,都會被當作true處理

這時候ChatGPT在上一篇提供的解法就是使用內建Pipes
回顧一下他提供的方式:

對於在 NestJS 中處理 GET 請求中的布林參數,NestJS 在這方面提供了一些內建的解決方案,而不需要手動進行轉換。你可以使用 ParseBoolPipe 來處理布林參數的自動轉換和驗證。

以下是如何使用 ParseBoolPipe 的示例:

import { Controller, Get, Query, ParseBoolPipe } from '@nestjs/common';

@Controller()
export class AppController {
  @Get('test')
  async test(@Query('isCheck', ParseBoolPipe) isCheck: boolean): Promise<string> {
    if (isCheck) return `is Checked, isCheck = ${isCheck}`;
    return 'not checked';
  }
}

在這個示例中,我們使用 ParseBoolPipe 來處理 isCheck 參數,它會自動將字符串 'true' 轉換為布林值 true,字符串 'false' 轉換為布林值 false,同時也處理了其他可能的布林值字符串。如果參數無法解析為布林值,它將返回 Bad Request 錯誤(HTTP 400)。

這樣你不需要手動進行類型轉換和驗證,NestJS 會自動處理。

希望這能解決你的問題!如果你有任何進一步的問題,請隨時提出。

所以這種內建Pipe和@UsePipes 看起來用法又不太一樣,來請教ChatGPT
https://ithelp.ithome.com.tw/upload/images/20231029/20135750kw9Map6mzY.png

是的,ParseBoolPipe 是 NestJS 內建的一種 Pipe。它是用於將請求中的布林值參數轉換為相應的 JavaScript 布林值的一種預建 Pipe。

舉例來說,如果你有一個路由接受名為 isActive 的布林值參數,你可以使用 ParseBoolPipe 來確保它被正確地解析為 JavaScript 布林值。

使用方式如下:

import { Controller, Get, Query, ParseBoolPipe } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findCats(@Query('isActive', ParseBoolPipe) isActive: boolean) {
    // isActive 將被解析為 JavaScript 布林值
    return `Active status: ${isActive}`;
  }
}

在這個例子中,ParseBoolPipe 將會解析 isActive 參數為布林值,這樣你就可以在控制器中正確地使用它。

總的來說,ParseBoolPipe 是 NestJS 提供的一個方便的內建 Pipe,用於快速地處理請求中的布林值參數。

https://ithelp.ithome.com.tw/upload/images/20231029/201357504IcbcAsYID.png

內建的 Pipe 和 UsePipes 裝飾器是 NestJS 中用於處理請求數據的兩種不同方式。

內建的 Pipe:

  1. 主要功能

    • 內建的 Pipe 是 NestJS 提供的一組預先定義的數據處理器,用於對請求中的數據進行轉換、驗證等操作。
  2. 使用方法

    • 內建的 Pipe 可以直接在控制器的方法參數上使用。例如,@Query()@Body() 等裝飾器後面可以接內建的 Pipe。
  3. 範例

    • 使用內建的 Pipe 來解析布林值:
    import { Controller, Get, Query, ParseBoolPipe } from '@nestjs/common';
    
    @Controller('cats')
    export class CatsController {
      @Get()
      findCats(@Query('isActive', ParseBoolPipe) isActive: boolean) {
        return `Active status: ${isActive}`;
      }
    }
    

UsePipes 裝飾器:

  1. 主要功能

    • UsePipes 是一個控制器/路由處理器的裝飾器,用於套用自定義的 Pipe 或一組 Pipes。
  2. 使用方法

    • 可以通過將 UsePipes 裝飾器放在控制器類別或路由處理器的上方,來套用指定的 Pipes。
  3. 範例

    • 使用 UsePipes 來套用自定義的 Pipe:
    import { Controller, Get, UsePipes } from '@nestjs/common';
    import { CustomPipe } from './custom.pipe';
    
    @Controller('cats')
    @UsePipes(CustomPipe) // 使用自定義的 Pipe
    export class CatsController {
      @Get()
      findCats(@Query('param') param: string) {
        return `Received param: ${param}`;
      }
    }
    

主要差異:

  • 功能
    • 內建的 Pipe 是 NestJS 提供的一組預先定義的數據處理器,可用於快速處理常見的數據情況。
    • UsePipes 裝飾器用於套用自定義的 Pipe 或一組 Pipes,提供了更靈活的數據處理方案。
  • 範例用法
    • 內建的 Pipe 通常直接套用在方法參數上,用於對特定參數進行處理。
    • UsePipes 裝飾器用於整個控制器或特定路由上,可以套用多個自定義的 Pipe。

總的來說,內建的 Pipe 提供了一些預先定義的數據處理器,適用於快速的數據處理需求。而 UsePipes 裝飾器則提供了更靈活的方式來套用自定義的 Pipe,並可以在控制器或路由層級進行套用。选择使用哪种方式取决于你的需求和偏好。

https://ithelp.ithome.com.tw/upload/images/20231029/20135750MQzVd6KvUF.png

NestJS 提供了多種內建的 Pipe,用於處理請求數據,以下是一些常用的內建 Pipe 以及相應的範例:

  1. ParseIntPipe:將請求中的字符串轉換為整數。

    import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
    
    @Controller('cats')
    export class CatsController {
      @Get(':id')
      findCat(@Param('id', ParseIntPipe) id: number) {
        return `Cat ID: ${id}`;
      }
    }
    
  2. ParseFloatPipe:將請求中的字符串轉換為浮點數。

    import { Controller, Get, Query, ParseFloatPipe } from '@nestjs/common';
    
    @Controller('products')
    export class ProductsController {
      @Get()
      findProduct(@Query('price', ParseFloatPipe) price: number) {
        return `Product Price: ${price}`;
      }
    }
    
  3. ParseBoolPipe:將請求中的字符串(如 "true" 或 "false")轉換為布林值。

    import { Controller, Get, Query, ParseBoolPipe } from '@nestjs/common';
    
    @Controller('users')
    export class UsersController {
      @Get()
      findUsers(@Query('isActive', ParseBoolPipe) isActive: boolean) {
        return `Active status: ${isActive}`;
      }
    }
    
  4. ValidationPipe:用於對請求中的數據進行驗證,通常與 class-validator 库一起使用。

    import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
    import { CreateUserDto } from './create-user.dto';
    
    @Controller('users')
    export class UsersController {
      @Post()
      @UsePipes(new ValidationPipe()) // 使用 ValidationPipe 驗證請求數據
      createUser(@Body() createUserDto: CreateUserDto) {
        return `Created user: ${createUserDto.username}`;
      }
    }
    
  5. DefaultValuePipe:在未提供參數的情況下,設置默認值。

    import { Controller, Get, Query, DefaultValuePipe } from '@nestjs/common';
    
    @Controller('products')
    export class ProductsController {
      @Get()
      findProduct(@Query('limit', new DefaultValuePipe(10)) limit: number) {
        return `Limit: ${limit}`;
      }
    }
    
    

這些內建 Pipe 可以用於快速處理請求中的數據,提高代碼的可讀性和可維護性。根據你的需求,你可以選擇使用相應的內建 Pipe 或自定義 Pipe 來處理請求數據。

It’s 貪心 Time!
https://ithelp.ithome.com.tw/upload/images/20231029/20135750IDDYWmU7SX.png

除了之前提到的內建 Pipe,NestJS 還提供了一些其他有用的內建 Pipe,以下是一些額外的內建 Pipe:

  1. TrimPipe:用於去除請求中的字符串前後的空格。

    import { Controller, Post, Body, UsePipes, TrimPipe } from '@nestjs/common';
    
    @Controller('comments')
    export class CommentsController {
      @Post()
      @UsePipes(TrimPipe) // 使用 TrimPipe 去除空格
      createComment(@Body('text') text: string) {
        return `Comment text: ${text}`;
      }
    }
    
  2. UUIDValidationPipe:用於驗證請求中的 UUID(通用唯一標識符)字符串。

    import { Controller, Get, Param, UUIDValidationPipe } from '@nestjs/common';
    
    @Controller('items')
    export class ItemsController {
      @Get(':id')
      findItem(@Param('id', UUIDValidationPipe) id: string) {
        return `Item ID: ${id}`;
      }
    }
    
  3. DatePipe:用於將日期字符串轉換為 JavaScript 日期對象。

    import { Controller, Post, Body, UsePipes, DatePipe } from '@nestjs/common';
    
    @Controller('events')
    export class EventsController {
      @Post()
      @UsePipes(new DatePipe()) // 使用 DatePipe 轉換日期
      createEvent(@Body('date') date: Date) {
        return `Event date: ${date.toISOString()}`;
      }
    }
    

這些內建 Pipe 進一步擴展了 NestJS 的功能,可用於更多不同類型的請求數據處理需求。根據你的具體情況,你可以選擇使用相應的內建 Pipe 來簡化代碼。如果你需要更多特定的數據處理,還可以創建自定義 Pipe。

Pipe就差不多討就差不多討論到這邊,下面一位

Interceptors(攔截器)

https://ithelp.ithome.com.tw/upload/images/20231029/20135750QEJa2Oqyuz.png

當然,我很樂意為你提供有關 NestJS 中 Interceptors(攔截器)的詳細介紹和教學。

什麼是 Interceptors(攔截器)?

在 NestJS 中,Interceptors 是一種用於在請求和響應之間插入中間件的機制。它們允許你在處理請求或生成響應之前,對數據進行變換、檢查、處理等操作。

主要功能與用途:

  1. 處理請求和響應
    • Interceptors 可以在請求進入控制器之前或響應返回給客戶端之前執行操作。
  2. 數據變換
    • 可以用於在請求進入控制器之前,或在響應返回給客戶端之前對數據進行變換。
  3. 全局變換
    • Interceptors 可以在整個應用程序範圍內套用,從而實現對所有路由或控制器的統一數據處理。

使用 Interceptors 的步驟:

以下是在 NestJS 中使用 Interceptors 的基本步驟:

  1. 創建一個自定義的 Interceptor

    // custom.interceptor.ts
    
    import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class CustomInterceptor implements NestInterceptor {
      intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        // 在這裡進行攔截處理
        console.log('Before request is handled by the controller');
    
        return next.handle(); // 繼續處理請求
      }
    }
    
  2. 在控制器或提供者上套用 Interceptor

    • 可以在控制器的方法上套用,或在提供者上套用。
    // cats.controller.ts
    
    import { Controller, Get, UseInterceptors } from '@nestjs/common';
    import { CustomInterceptor } from './custom.interceptor';
    
    @Controller('cats')
    @UseInterceptors(CustomInterceptor) // 套用 Interceptor
    export class CatsController {
      @Get()
      findAll() {
        return 'This action returns all cats';
      }
    }
    
  3. 執行 Interceptor

    • 當客戶端發送請求時,Interceptor 將在請求進入控制器之前執行自定義的處理邏輯。

全局套用 Interceptor:

你也可以將 Interceptor 套用在整個應用程序範圍內,這樣它將影響所有的路由和控制器。

// main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CustomInterceptor } from './custom.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalInterceptors(new CustomInterceptor()); // 全局套用 Interceptor

  await app.listen(3000);
}
bootstrap();

這樣,自定義的 Interceptor 將在所有請求中生效。

總的來說,Interceptors 提供了一種靈活的機制,可以在請求和響應的不同階段進行數據處理。這可以幫助你實現全局的數據處理邏輯,或者在特定路由上應用特定的數據處理邏輯。

Filters(過濾器)

https://ithelp.ithome.com.tw/upload/images/20231029/20135750jT6nuRiRrW.png

當然,我很樂意為你提供有關 NestJS 中 Filters(過濾器)的詳細介紹和教學。

什麼是 Filters(過濾器)?

在 NestJS 中,Filters 是一種用於處理異常情況的機制。它允許你在發生異常時執行一些自定義的邏輯,例如日誌記錄、發送錯誤訊息等。

主要功能與用途:

  1. 處理異常情況
    • Filters 用於在發生異常時執行自定義的處理邏輯。
  2. 提供全局異常處理
    • 可以創建全局過濾器來處理所有控制器中發生的異常。
  3. 細分異常類型
    • 可以根據不同的異常類型創建特定的過濾器,以實現細粒度的異常處理。

使用 Filters 的步驟:

以下是在 NestJS 中使用 Filters 的基本步驟:

  1. 創建一個自定義的 Filter

    // custom.filter.ts
    
    import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
    
    @Catch(HttpException)
    export class CustomFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();
        const request = ctx.getRequest();
    
        const status = exception.getStatus();
        const message = exception.getResponse();
    
        response
          .status(status)
          .json({
            statusCode: status,
            timestamp: new Date().toISOString(),
            path: request.url,
            message,
          });
      }
    }
    
    
  2. 在控制器或提供者上套用 Filter

    • 可以在控制器的方法上套用,或在提供者上套用。
    // cats.controller.ts
    
    import { Controller, Get, UseFilters } from '@nestjs/common';
    import { CustomFilter } from './custom.filter';
    
    @Controller('cats')
    @UseFilters(CustomFilter) // 套用 Filter
    export class CatsController {
      @Get()
      findAll() {
        throw new HttpException('Not Found', HttpStatus.NOT_FOUND);
      }
    }
    
    
  3. 執行 Filter

    • 當控制器方法中拋出異常時,Filter 將捕獲該異常並執行自定義的處理邏輯。

全局套用 Filter:

你也可以將 Filter 套用在整個應用程序範圍內,這樣它將影響所有的控制器。

// main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CustomFilter } from './custom.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalFilters(new CustomFilter()); // 全局套用 Filter

  await app.listen(3000);
}
bootstrap();

這樣,自定義的 Filter 將在所有請求中生效。

總的來說,Filters 提供了一種靈活的機制,用於在發生異常時執行自定義的處理邏輯。這可以幫助你實現全局的異常處理邏輯,或者在特定控制器或提供者上應用特定的異常處理邏輯。

ChatGPT一開始列舉的時候,把Exception Filters 和 Filters分開放了!
語法上看起來基本上就一樣
差異在哪裡呢??
https://ithelp.ithome.com.tw/upload/images/20231029/20135750EZl27vH4Mp.png

在 NestJS 中,有 Filters 和 Exception Filters 兩種不同的機制,用於處理異常情況,它們之間的主要差別在於處理的方式和應用場景。

Filters(過濾器):

  1. 功能

    • Filters 是一種用於處理異常情況和成功情況的機制,它們可以捕捉並處理控制器方法中拋出的異常,也可以在成功情況下執行一些自定義邏輯。
  2. 用途

    • Filters 可以用於處理控制器方法中的異常,並可以實現全局的異常處理邏輯,或者對特定控制器或提供者的異常進行細分處理。
  3. 示例

    • 創建一個自定義 Filter 來處理成功和異常情況:
    import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
    
    @Catch(HttpException)
    export class CustomFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();
    
        if (exception.getStatus() === HttpStatus.NOT_FOUND) {
          response.status(HttpStatus.NOT_FOUND).json({ message: 'Resource not found' });
        } else {
          response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Internal server error' });
        }
      }
    }
    

Exception Filters(異常過濾器):

  1. 功能

    • Exception Filters 是 Filters 的特殊類型,專門用於處理異常情況。它們只捕捉和處理控制器方法中拋出的異常。
  2. 用途

    • Exception Filters 主要用於全局處理異常情況,並提供對所有控制器和路由的統一異常處理。
  3. 示例

    • 創建一個自定義 Exception Filter 來處理異常情況:
    import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
    
    @Catch(HttpException)
    export class CustomExceptionFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();
    
        response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ message: 'Internal server error' });
      }
    }
    

主要差別:

  • Filters 可以處理成功和異常情況,並且可以在控制器方法中使用,從而實現對成功和異常的細粒度處理。它們可以在控制器方法的 catch 裝飾器中使用。
  • Exception Filters 專門用於處理異常情況,它們是 Filters 的一個特殊類型,並且主要用於全局處理異常情況。它們不會處理成功情況。

總之,你可以根據具體需求選擇使用 Filters 或 Exception Filters。Filters 更靈活,可以用於不同情況的處理,而 Exception Filters 主要用於全局異常處理。

統整重點

https://ithelp.ithome.com.tw/upload/images/20231029/20135750vEAhZwTUfp.png

綜合以上內容,以下是關於 NestJS 中各種中間件和機制的重點和結論:

  1. 中間件(Middleware):中間件是一個用於處理 HTTP 請求的通用機制,它可以在請求進入控制器之前或返回客戶端之前執行操作。中間件可以用於全局處理、路由特定處理或在控制器方法上套用。
  2. Pipes(管道):Pipes 用於對特定參數進行數據處理,包括驗證、轉換等操作。NestJS 提供了多個內建 Pipes,並支持自定義 Pipes 的創建。Pipe 主要用於控制器方法處理請求之前對特定參數進行處理。
  3. Guards(守衛):Guards 用於路由保護和權限控制,可以在控制器或方法上套用,並決定是否允許請求進入控制器方法。Guards 主要用於路由保護和身份驗證。
  4. Interceptors(攔截器):Interceptors 允許你在請求進入控制器之前或響應返回給客戶端之前執行操作。它可以用於處理請求的數據變換、檢查等操作,並支持全局套用。
  5. Filters(過濾器):Filters 是用於處理異常情況的機制,它們允許你捕捉和處理控制器方法中拋出的異常。Filters 主要用於全局異常處理,並提供對異常情況的統一處理。

總結來說,NestJS 提供了多種中間件和機制,用於處理不同層次的請求處理需求。你可以根據具體情況選擇使用相應的中間件,以實現控制和處理請求、數據和異常的需求。這些工具提供了強大的功能和靈活性,有助於構建強大的 Node.js 應用程序。

結語

本次對話記錄在這邊:
https://chat.openai.com/share/6e54ee0e-e88f-4427-9d75-b0aedd156ce9

本篇和Middleware 相關的內容篇幅有點太長了,這些Middleware各自有他應用的方式
就藉由ChatGPT探討了一輪

如果有遇到 A 和 B 的差異到底在哪的問題
我個人使用體驗上覺得問ChatGPT蠻有效的解決這類的困惑,即便有些差異真是小到很細微

我原本和Middleware 相關比較常用的是Guards,用來驗證JWT取得user資訊
JWT是什麼呢?我們下一篇來探討!

我是還在努力和Middleware增進感情的前端工程師一宵三筵,我們明天見!


上一篇
【Day44】ChatGPT請教教我:NestJS!(三)- Controller & 資料驗證 !class-validator & class-transformer!
下一篇
【Day46】ChatGPT請教教我:NestJS!(五)- JWT 身份驗證!Guards!
系列文
用ChatGPT詠唱來完成工作與點亮前後端技能樹56
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言