iT邦幫忙

2023 iThome 鐵人賽

0
自我挑戰組

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

【Day48】ChatGPT請教教我:NestJS!(七)- Service & 例外處理 & HTTP請求

  • 分享至 

  • xImage
  •  

我們在一開始的時候,有講解到module、controller、service之間的關係
但後來的幾個章節,因為都集中討論controller,變成回傳資料省略service這一塊

今天終於要把service接回來了!
會複習service要怎麼連接,以及service的一些相關功能

再討論一下service內的例外處理,包含HTTP狀態碼的分類,以及service發出http請求

大概就這樣,Go Go!

Service介紹

https://ithelp.ithome.com.tw/upload/images/20231030/201357500yGR1T16Iv.png

當然可以!讓我來為你詳細介紹 NestJS 中的服務(service)以及如何使用它。

在 NestJS 中,服務是一種用來處理業務邏輯的組件。它們可以幫助我們保持程式碼的結構清晰,並使我們的應用更容易測試和維護。

以下是一個簡單的 NestJS 服務的建立步驟:

  1. 創建一個服務文件

    首先,在你的專案中創建一個服務文件,通常會以 .service.ts 結尾。比如,如果你要創建一個用戶服務,你可以創建一個 user.service.ts 文件。

  2. 定義服務類別

    在創建的服務文件中,你需要定義一個服務類別。這個類別可以包含各種處理業務邏輯的方法。

    // user.service.ts
    
    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class UserService {
      private users = ['John', 'Jane', 'Doe'];
    
      getUsers(): string[] {
        return this.users;
      }
    
      addUser(user: string): void {
        this.users.push(user);
      }
    }
    
  3. 註冊服務

    接下來,你需要在模組中註冊這個服務,以便在其他地方可以使用它。你可以在相應的模組中使用 providers 陣列進行註冊。

    // user.module.ts
    
    import { Module } from '@nestjs/common';
    import { UserService } from './user.service';
    import { UserController } from './user.controller';
    
    @Module({
    	controller: [UserController],
      providers: [UserService],
      exports: [UserService], // 這行是可選的,它允許在其他模組中使用這個服務
    })
    export class UserModule {}
    
  4. 使用服務

    現在你可以在其他地方(如控制器或其他服務)中使用這個服務了。在你想要使用的地方,通過依賴注入的方式來使用這個服務。

    // user.controller.ts
    
    import { Controller, Get } from '@nestjs/common';
    import { UserService } from './user.service';
    
    @Controller('users')
    export class UserController {
      constructor(private readonly userService: UserService) {}
    
      @Get()
      getUsers(): string[] {
        return this.userService.getUsers();
      }
    }
    

這就是一個簡單的 NestJS 服務的建立過程。希望這能夠對你有所幫助!如果你有任何問題或需要進一步的解釋,請隨時告訴我。

正確來說應該要像上面這樣,Controller就負責呼叫service的function來處理
而不是把運算與處理邏輯,也一起塞在controller中

controller應該處理的是路由、輸入參數、權限管理,把該丟進serivce中的參數整理好之後
確保參數是合法的,再丟給service去執行

Service進階內容

https://ithelp.ithome.com.tw/upload/images/20231030/20135750YPICpsgnrx.png

當然!除了基本的服務操作之外,還有一些進階的主題和技巧可以讓你更好地使用 NestJS 中的服務。

  1. 依賴注入

    NestJS 通過依賴注入(Dependency Injection)來管理模組間的依賴關係。這使得服務可以輕鬆地被其他模組或服務所使用。在範例中,你可以看到我們如何在控制器中使用了 UserService,而不需要直接實例化它。

    constructor(private readonly userService: UserService) {}
    
  2. 單一職責原則

    保持服務的單一職責,讓它專注於特定的業務邏輯。這樣可以使服務更容易測試、理解和維護。

  3. 非同步操作

    在一些情況下,你可能需要處理非同步的操作,比如處理文件上傳或者發送 HTTP 請求。在這種情況下,你可以使用 asyncawait 來處理這些操作。

    async someAsyncOperation(): Promise<any> {
      // 非同步操作
    }
    
  4. 提供者和作用域

    NestJS 提供了不同的提供者作用域,包括 Singleton(單例,默認)、Request(請求)、Transient(瞬態)等。你可以在模組中使用 @Injectable({ scope: Scope }) 來設置服務的作用域。

    @Injectable({ scope: Scope.REQUEST }) // 設置為請求作用域
    export class UserService {}
    
  5. 模組之間的服務共享

    如果你想在多個模組中共享同一個服務,你可以在多個模組中導入並提供它。這樣可以使得服務在整個應用程序中都是可用的。

  6. 測試服務

    使用單元測試來確保服務的正確性。你可以使用測試框架(如 Jest)來創建和運行測試案例。

以上是一些進階的主題,可以幫助你更好地使用 NestJS 中的服務。如果你對其中任何一個主題有進一步的疑問或需要更多的範例,請隨時告訴我!

https://ithelp.ithome.com.tw/upload/images/20231030/20135750wU1KNmJSGR.png

當然可以!除了前面提到的主題外,還有一些進階的用法和技巧可以幫助你更有效地使用服務。

  1. HTTP 請求

    在服務中發送 HTTP 請求是一個常見的需求。你可以使用 NestJS 提供的 HttpService 來處理這些請求。

    import { Injectable, HttpService } from '@nestjs/common';
    import { AxiosResponse } from 'axios';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class ApiService {
      constructor(private httpService: HttpService) {}
    
      getData(): Observable<AxiosResponse<any>> {
        return this.httpService.get('https://api.example.com/data');
      }
    }
    
  2. 錯誤處理

    在服務中處理錯誤是很重要的一部分。你可以使用 try-catch 來處理同步錯誤,也可以使用 RxJS 的 catchError 來處理非同步錯誤。

    import { Injectable, HttpService } from '@nestjs/common';
    import { catchError } from 'rxjs/operators';
    import { throwError } from 'rxjs';
    
    @Injectable()
    export class ApiService {
      constructor(private httpService: HttpService) {}
    
      getData() {
        return this.httpService.get('https://api.example.com/data').pipe(
          catchError(error => {
            // 處理錯誤
            return throwError(error);
          }),
        );
      }
    }
    
  3. 日誌紀錄

    在服務中添加日誌紀錄可以幫助你追蹤和調試程式。你可以使用 NestJS 提供的 Logger 服務來做到這一點。

    import { Injectable, Logger } from '@nestjs/common';
    
    @Injectable()
    export class ExampleService {
      private readonly logger = new Logger(ExampleService.name);
    
      someMethod() {
        this.logger.log('這是一個日誌訊息');
      }
    }
    
  4. 事件處理

    NestJS 提供了內建的事件系統,你可以在服務中使用它來觸發和監聽事件。

    import { Injectable, EventEmitter2 } from '@nestjs/common';
    
    @Injectable()
    export class ExampleService {
      constructor(private readonly eventEmitter: EventEmitter2) {}
    
      triggerEvent(data: any) {
        this.eventEmitter.emit('custom.event', data);
      }
    }
    
  5. 生命週期鉤子

    NestJS 提供了一些生命週期鉤子(lifecycle hooks)可以讓你在服務的不同階段執行特定的操作,例如 onModuleInitonModuleDestroy

    import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
    
    @Injectable()
    export class ExampleService implements OnModuleInit, OnModuleDestroy {
      onModuleInit() {
        // 在模組初始化時執行
      }
    
      onModuleDestroy() {
        // 在模組銷毀時執行
      }
    }
    

這些是一些進階的用法和技巧,可以讓你更靈活地使用 NestJS 中的服務。如果你對其中任何一個主題有進一步的疑問或需要更多的範例,請隨時告訴我!

OK,要了這麼多關鍵字,可以針對有興趣的內容來逐一詢問了
接下來要問的主題是這幾個

  1. scope
  2. 拋出例外、錯誤處理
  3. HTTP 請求

其他進階的內容像是「事件處理」或「生命週期鉤子」是更進階的用法
不一定會用到、不一定是必要,我們這篇就先不討論

Service Scope (服務作用域)

https://ithelp.ithome.com.tw/upload/images/20231030/20135750e6QDG9pFEn.png

當談到 NestJS 中服務(Service)的作用域(Scope)時,主要有三種不同的作用域,分別是 Singleton、Request、和 Transient。這些作用域決定了服務實例的生命週期和可訪問性。讓我們詳細介紹每一種作用域:

  1. Singleton(單例)

    • 預設作用域:當你不明確設置作用域時,服務的作用域默認為 Singleton。
    • 生命週期:在整個應用程序的運行過程中,只會創建一個實例。
    • 可訪問性:單例服務對整個應用程序都是可見的,它可以在不同的模組中注入並共享相同的實例。
    @Injectable() // 預設作用域為 Singleton
    export class SingletonService {}
    
  2. Request(請求)

    • 生命週期:每個 HTTP 請求(或者其他受 NestJS 支援的協議,如 WebSockets)都會創建一個新的實例,並在請求處理結束後銷毀。
    • 可訪問性:Request 作用域的服務僅在處理特定請求期間可用。這些服務不能跨越不同請求共享狀態。

    要將服務設置為 Request 作用域,你需要在服務的 @Injectable() 裝飾器中指定作用域:

    @Injectable({ scope: Scope.REQUEST })
    export class RequestScopedService {}
    
  3. Transient(瞬態)

    • 生命週期:每次注入服務時,都會創建一個新的實例。
    • 可訪問性:每次注入時都會得到一個全新的實例,因此它不會被共享。

    要將服務設置為 Transient 作用域,你需要在服務的 @Injectable() 裝飾器中指定作用域:

    @Injectable({ scope: Scope.TRANSIENT })
    export class TransientService {}
    

選擇適當的作用域

  • 使用 Singleton 作用域,當你需要在整個應用程序中共享相同的實例,這通常適用於服務的全域設定或共享狀態。
  • 使用 Request 作用域,當你需要處理特定請求期間的狀態或上下文,例如身份驗證或請求相關的資料。
  • 使用 Transient 作用域,當你需要在不同的地方獲得全新的實例,而不共享狀態。

這就是 NestJS 中服務作用域的詳細介紹。選擇適當的作用域可以幫助你更好地管理服務的生命週期和資料共享。如果你有任何進一步的問題,請隨時提出。

https://ithelp.ithome.com.tw/upload/images/20231030/20135750x24L7JSeGK.png

當然!我將為你提供一個範例,以展示在 NestJS 中不同作用域的效果。

首先,我們將創建三個不同作用域的服務:

  1. Singleton Service(單例服務)
  2. Request Scoped Service(請求作用域服務)
  3. Transient Service(瞬態服務)

然後,我們將在一個控制器中使用這些服務來展示它們的效果。

  1. 創建服務
// singleton.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class SingletonService {
  private readonly value: number = Math.random();

  getValue(): number {
    return this.value;
  }
}
// request-scoped.service.ts
import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
  private readonly value: number = Math.random();

  getValue(): number {
    return this.value;
  }
}
// transient.service.ts
import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {
  private readonly value: number = Math.random();

  getValue(): number {
    return this.value;
  }
}
  1. 創建控制器
// example.controller.ts
import { Controller, Get } from '@nestjs/common';
import { SingletonService } from './singleton.service';
import { RequestScopedService } from './request-scoped.service';
import { TransientService } from './transient.service';

@Controller('example')
export class ExampleController {
  constructor(
    private readonly singletonService: SingletonService,
    private readonly requestScopedService: RequestScopedService,
    private readonly transientService: TransientService,
  ) {}

  @Get('singleton')
  getSingletonValue(): number {
    return this.singletonService.getValue();
  }

  @Get('request-scoped')
  getRequestScopedValue(): number {
    return this.requestScopedService.getValue();
  }

  @Get('transient')
  getTransientValue(): number {
    return this.transientService.getValue();
  }
}
  1. 註冊模組
// example.module.ts
import { Module } from '@nestjs/common';
import { ExampleController } from './example.controller';
import { SingletonService } from './singleton.service';
import { RequestScopedService } from './request-scoped.service';
import { TransientService } from './transient.service';

@Module({
  controllers: [ExampleController],
  providers: [SingletonService, RequestScopedService, TransientService],
})
export class ExampleModule {}

這個範例將創建了三個不同作用域的服務,並在一個控制器中使用它們來返回一個隨機生成的值。現在你可以在你的應用程序中使用這些服務來觀察它們的行為。

請注意,在這個範例中,SingletonService 將會在應用程序的整個生命週期內保持相同的值,而 RequestScopedService 在每個 HTTP 請求中都會生成一個新的值,而 TransientService 則會在每次注入時生成一個新的值。

如果你需要任何進一步的解釋或協助,請隨時讓我知道!

全部照做之後,再swagger中測試結果,果然預設的singleton 的會維持原本的結果
https://ithelp.ithome.com.tw/upload/images/20231030/20135750zJgmbRfY8N.png

其他兩個會隨著請求或是每次注入都變成新的serveice實例,重新創建物件與執行初始化
https://ithelp.ithome.com.tw/upload/images/20231030/20135750ue5jeTzOfj.png

當你使用這三種不同作用域的服務時,你會觀察到以下的結果:

  1. Singleton Service (單例服務)
    • 結果:無論你在應用程序的哪個地方注入 SingletonService,它返回的值都是相同的。
    • 理由:這是因為 Singleton 作用域的服務在整個應用程序生命週期中只會被創建一次。當你第一次注入 SingletonService 時,它會被創建,並保持在內存中。之後無論在哪裡注入 SingletonService,都會得到相同的實例。
  2. Request Scoped Service (請求作用域服務)
    • 結果:每個 HTTP 請求 (或者其他受 NestJS 支援的協議) 都會創建一個新的實例,因此在同一個請求期間,你會得到相同的值,但不同請求之間的值會不同。
    • 理由:這是因為 Request 作用域的服務會在每個請求開始時創建一個新的實例,並在請求結束時銷毀。這確保了在同一個請求期間,你得到的是相同的實例。
  3. Transient Service (瞬態服務)
    • 結果:每次你注入 TransientService 都會創建一個新的實例,因此每次你使用它時,你會得到不同的值。
    • 理由:這是因為 Transient 作用域的服務會在每次注入時創建一個新的實例,這意味著每次你使用 TransientService 都會得到一個全新的實例,而不會共享狀態。

總的來說,這些不同作用域的服務讓你可以根據需求來控制服務的生命週期和資料共享。這對於管理應用程序的狀態和資料非常有用,並使得服務在不同情況下可以有不同的行為。

Scope 的介紹就先到這邊,再根據每次請求、注入是否要使用同個實例來決定要使用哪一種

接下來要談談例外處理的部分

Service 拋出例外

【Day45】ChatGPT請教教我:NestJS!(四)- Controller & Middleware!的時候有提到例外攔截器

那是內部已經拋出錯誤之後,要針對區域或是全局的例外進行攔截與處理
可能紀錄Log或是整理回傳給使用者的訊息格式之類的

那這邊要討論的是service 在處理到一半,可能就發現有某些錯誤狀況
需要主動進行拋出例外的動作,將程序跳出原本的function並回傳錯誤碼

我們請ChatGPT來為NestJS中service要怎麼拋出例外進行說明
https://ithelp.ithome.com.tw/upload/images/20231030/20135750GRZT4CRmSt.png

在 NestJS 中,服務(Service)可以拋出例外(Exceptions)以處理應用程序中的錯誤情況。這可以幫助你更好地處理錯誤,提高代碼的可讀性和可維護性。以下是關於如何在 NestJS 中拋出例外的詳細介紹:

  1. NestJS 中的例外類型

    NestJS 提供了多種內置的例外類型,以應對不同的錯誤情況。一些常見的內置例外包括:

    • HttpException: 用於拋出 HTTP 相關的例外,如 404(Not Found)、500(Internal Server Error)等。
    • UnauthorizedException: 用於處理未授權訪問資源的情況。
    • NotFoundException: 用於處理找不到資源的情況。
    • BadRequestException: 用於處理客戶端發送的錯誤請求。
    • 等等...

    你可以根據你的需求選擇合適的內置例外,或者創建自定義的例外類型。

  2. 如何拋出例外

    在服務中,你可以使用 throw 陳述來拋出例外。例如,以下是如何在服務中拋出 NotFoundException 的方式:

    import { Injectable, NotFoundException } from '@nestjs/common';
    
    @Injectable()
    export class MyService {
      findItem(id: number) {
        const item = someFunctionToFindItem(id);
    
        if (!item) {
          throw new NotFoundException(`Item with ID ${id} not found`);
        }
    
        return item;
      }
    }
    

    這樣當 findItem 方法找不到對應的項目時,它會拋出一個 NotFoundException,並將錯誤消息返回給客戶端。

  3. 全局例外過濾器

    NestJS 提供了全局例外過濾器(Exception Filters),它們可以用於捕獲和處理服務中拋出的例外。這些過濾器允許你對例外進行自訂處理,例如記錄錯誤、返回特定的錯誤響應等。

    你可以在全局模組或特定模組中設置例外過濾器,以定義全局的錯誤處理邏輯。

  4. 自定義例外

    除了使用內置的例外類型,你還可以創建自定義的例外類型,以應對特定的錯誤情況。要創建自定義例外,你可以擴展 HttpException 類別或 Error 類別。

    import { HttpException, HttpStatus } from '@nestjs/common';
    
    export class MyCustomException extends HttpException {
      constructor(message: string) {
        super({ message, status: HttpStatus.BAD_REQUEST }, HttpStatus.BAD_REQUEST);
      }
    }
    

    然後,你可以在服務中使用這個自定義例外:

    throw new MyCustomException('This is a custom exception');
    

這些是在 NestJS 中拋出例外的詳細介紹。拋出例外是處理錯誤的重要方式,它可以幫助你優雅地處理各種錯誤情況,提高應用程序的可靠性。如果你有任何進一步的問題或需要更多的範例,請隨時告訴我!

第一點提到,NestJS就內建了一些例外類型,讓你直接根據對應的情況,直接使用該種例外
而不是全部都throw new Error() 而已

NestJS內建例外類型

https://ithelp.ithome.com.tw/upload/images/20231030/20135750GrU2yhgYv7.png

在 NestJS 中,內建了多種例外類型來幫助你處理不同的錯誤情況。以下是一些常見的內建例外類型以及它們的詳細介紹:

  1. HttpException:

    • 描述HttpException 是所有內建例外類型的基類。它允許你指定 HTTP 狀態碼和響應消息來表示錯誤情況。

    • 範例

      throw new HttpException('Not Found', HttpStatus.NOT_FOUND);
      
  2. BadRequestException:

    • 描述:用於表示客戶端發送的錯誤請求,通常與 HTTP 400 Bad Request 狀態碼相關聯。

    • 範例

      throw new BadRequestException('Invalid request');
      
  3. UnauthorizedException:

    • 描述:用於處理未授權訪問資源的情況,通常與 HTTP 401 Unauthorized 狀態碼相關聯。

    • 範例

      throw new UnauthorizedException('Unauthorized access');
      
  4. NotFoundException:

    • 描述:用於處理找不到資源的情況,通常與 HTTP 404 Not Found 狀態碼相關聯。

    • 範例

      throw new NotFoundException('Resource not found');
      
  5. MethodNotAllowedException:

    • 描述:用於表示指定的 HTTP 方法不被允許的情況,通常與 HTTP 405 Method Not Allowed 狀態碼相關聯。

    • 範例

      throw new MethodNotAllowedException('Method not allowed');
      
  6. InternalServerErrorException:

    • 描述:用於表示內部服務器錯誤的情況,通常與 HTTP 500 Internal Server Error 狀態碼相關聯。

    • 範例

      throw new InternalServerErrorException('Internal server error');
      

這些是一些常見的內建例外類型,它們可以幫助你在 NestJS 應用程序中處理各種不同的錯誤情況。通過選擇適當的例外類型,你可以清晰地傳達錯誤信息並採取適當的措施來處理錯誤。如果你需要進一步了解或有任何進一步的問題,請隨時告訴我!

這些例外情形都是跟著HTTP訂下的狀態碼而分類的
來一起了解一下HTTP狀態碼有哪些,各自又表示了什麼樣子的問題

HTTP 狀態碼

https://ithelp.ithome.com.tw/upload/images/20231030/201357503gQaY5RhtY.png

HTTP 狀態碼是網絡通信中的一個重要部分,它用於指示網絡請求的結果。每個狀態碼都有特定的含義,並且用於通知客戶端發生了什麼情況。以下是一些常見的 HTTP 狀態碼及其詳細介紹:

  1. 1xx(Informational):信息性狀態碼
    • 100 Continue: 服務器收到請求的一部分,但仍在等待客戶端發送其餘部分。
    • 101 Switching Protocols: 客戶端要求服務器切換協議。
  2. 2xx(Successful):成功狀態碼
    • 200 OK: 請求成功,並且服務器返回相應的內容。
    • 201 Created: 請求成功,並且新的資源已被創建。
    • 204 No Content: 請求成功,但沒有返回內容。
  3. 3xx(Redirection):重定向狀態碼
    • 301 Moved Permanently: 資源的 URI 已被永久移動到新位置。
    • 302 Found: 資源的 URI 已暫時移動到新位置。
    • 304 Not Modified: 客戶端已經擁有最新版本的資源,無需再次下載。
  4. 4xx(Client Errors):客戶端錯誤狀態碼
    • 400 Bad Request: 服務器無法理解或處理客戶端的請求。
    • 401 Unauthorized: 客戶端需要進行身份驗證才能訪問資源。
    • 403 Forbidden: 客戶端被拒絕訪問資源,即使身份驗證成功也不行。
    • 404 Not Found: 未找到所請求的資源。
  5. 5xx(Server Errors):服務器錯誤狀態碼
    • 500 Internal Server Error: 服務器遇到了一個意外的錯誤,導致無法處理請求。
    • 502 Bad Gateway: 服務器作為網關或代理接收到來自上游服務器的無效回應。
    • 503 Service Unavailable: 服務器暫時無法處理請求,通常是由於過載或維護。

這些是一些常見的 HTTP 狀態碼及其詳細介紹。理解這些狀態碼可以幫助你更好地理解網絡通信中發生的情況,並提供相應的處理措施。如果你對任何特定的狀態碼有進一步的疑問,請隨時告訴我!

我們最熟悉或最廣為人知的大概就是404 Not Found
除此之外的狀態碼代號與意義,身為前後端工程師都該了解一下
才知道收到問題時是表示什麼類型的問題,或是知道要怎麼吐出對的錯誤代碼

所以再回頭看看ChatGPT所提供的內建NestJS內建例外

  1. throw new HttpException('Not Found', HttpStatus.NOT_FOUND); ⇒ 自己指定
  2. throw new UnauthorizedException('Unauthorized access'); ⇒ 400
  3. throw new UnauthorizedException('Unauthorized access'); ⇒ 401
  4. throw new NotFoundException('Resource not found'); ⇒ 404
  5. throw new MethodNotAllowedException('Method not allowed'); ⇒ 405
  6. throw new InternalServerErrorException('Internal server error'); ⇒ 500

除了第一個自己指定的語法之外
其他就是為了遇到對應的錯誤時,可以直接使用相應的錯誤內容
不需額外指定server:要回傳500錯誤

那接著來了解一下 HttpException 要怎麼使用
https://ithelp.ithome.com.tw/upload/images/20231030/20135750BKOv61fcpg.png

當你在 NestJS 中處理 HTTP 請求時,可能會需要返回不同的 HTTP 狀態碼以及相應的響應信息。為了簡化這個過程,NestJS 提供了 HttpException 類型,它可以用於拋出 HTTP 相關的例外,同時你可以通過 HttpStatus 來指定所需的狀態碼。

以下是對 HttpExceptionHttpStatus 的詳細介紹:

HttpException:

  • 描述:

    HttpException 是 NestJS 提供的一個基本例外類型,用於拋出 HTTP 相關的錯誤。它可以用於指示發生了什麼樣的錯誤情況,並且可以附帶一個自定義的錯誤消息。

  • 使用方式:

    你可以在服務、控制器或其他地方使用 throw new HttpException() 來拋出一個 HTTP 相關的例外。你可以指定一個錯誤消息和一個 HTTP 狀態碼。

    throw new HttpException('Custom error message', HttpStatus.BAD_REQUEST);
    

HttpStatus:

  • 描述:

    HttpStatus 是 NestJS 提供的一個列舉類型,它包含了各種常見的 HTTP 狀態碼,這些狀態碼對應於不同的錯誤情況或成功情況。

  • 常用的 HttpStatus:

    • HttpStatus.OK: 200 OK,請求成功。
    • HttpStatus.CREATED: 201 Created,成功創建資源。
    • HttpStatus.BAD_REQUEST: 400 Bad Request,客戶端發送了一個無效的請求。
    • HttpStatus.UNAUTHORIZED: 401 Unauthorized,需要進行身份驗證才能訪問資源。
    • HttpStatus.NOT_FOUND: 404 Not Found,未找到所請求的資源。
    • HttpStatus.INTERNAL_SERVER_ERROR: 500 Internal Server Error,服務器遇到了一個意外的錯誤。
  • 使用方式:

    你可以將 HttpStatus 作為 HttpException 的第二個參數使用,以指定所需的狀態碼。

    throw new HttpException('Custom error message', HttpStatus.BAD_REQUEST);
    

範例:

以下是一個簡單的範例,演示了如何在 NestJS 中使用 HttpExceptionHttpStatus

import { Injectable, HttpException, HttpStatus } from '@nestjs/common';

@Injectable()
export class MyService {
  createUser(userData: any) {
    if (!userData.name) {
      throw new HttpException('Name is required', HttpStatus.BAD_REQUEST);
    }

    // Logic to create user
    return 'User created successfully';
  }
}

在這個例子中,如果客戶端發送的請求中沒有包含名稱(name)信息,則會拋出一個 HttpException,並返回 400 Bad Request 狀態碼。

透過使用 HttpExceptionHttpStatus,你可以更靈活地處理 HTTP 請求中的不同情況,並返回相應的狀態碼和響應信息。

這樣的話,HttpException 主要是提供自訂
而且有HttpStatus 這個enum可以提供常用的狀態碼列表

確認一下使用 BadRequestException 和使用 HttpException 指定HttpStatus.BAD_REQUEST
是否有差異
https://ithelp.ithome.com.tw/upload/images/20231030/20135750IIdONF64iW.png

throw new BadRequestException('Invalid request');throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); 都可以用於拋出 HTTP 400 Bad Request 錯誤,但它們之間有一些微妙的差異:

  1. 使用 BadRequestException

    throw new BadRequestException('Invalid request');
    
    • BadRequestException 是 NestJS 中提供的一個特定例外類型,它預設與 HTTP 400 Bad Request 狀態碼相關聯。
    • 這是一個更簡化的方式來拋出 HTTP 400 錯誤,不需要另外指定狀態碼,因為它已經預設為 400。
    • BadRequestException 可以接受一個可選的錯誤訊息,這樣你可以自定義錯誤訊息。
  2. 使用 HttpExceptionHttpStatus

    throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST);
    
    • HttpException 是 NestJS 的通用例外類型,它可以用於拋出各種 HTTP 相關的錯誤,並需要明確指定所需的狀態碼。
    • 在這種情況下,你需要使用 HttpStatus.BAD_REQUEST 來指定 400 Bad Request 狀態碼。
    • 你可以提供自定義的錯誤訊息,就像 BadRequestException 一樣。

總之,使用 BadRequestException 更為簡化且直觀,因為它已預設與 400 Bad Request 狀態碼相關聯。而使用 HttpExceptionHttpStatus 則更具靈活性,因為你可以明確指定所需的狀態碼,這在某些特定情況下可能更有用。你可以根據項目需求和個人偏好來選擇使用哪種方法。

錯誤訊息格式

拋出了錯誤之後,要看看接收端收到錯誤會是什麼樣子
https://ithelp.ithome.com.tw/upload/images/20231030/20135750oGKVIK6ibr.png

在 NestJS 中,當你拋出錯誤後,預設情況下,客戶端會收到一個 JSON 格式的錯誤響應。這個響應會包含以下信息:

  1. status:HTTP 狀態碼,表示錯誤的類型。
  2. error:錯誤的類型或名稱。
  3. message:自定義的錯誤消息,這是你在拋出錯誤時提供的消息。

這是一個範例錯誤響應的JSON格式:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid request"
}

在這個例子中,拋出的是一個 BadRequestException,因此 HTTP 狀態碼是 400,錯誤類型是 "Bad Request",自定義的錯誤消息是 "Invalid request"。

這個預設的錯誤響應可以幫助客戶端了解發生了什麼錯誤,並提供相應的信息以便進一步處理。如果你需要進一步自定義錯誤響應的內容,你可以使用全局例外過濾器或自定義異常過濾器來進行處理。

OK,這邊暫時是針對拋出例外的方式與例外的類型、錯誤訊息的格式的討論
下一個!發送HTTP Request!

HTTP 請求

不只是前端發給後端會需要,後端有時也會需要發送請求到其他的後端
比如串接第三方API等等,就有機會使用到
https://ithelp.ithome.com.tw/upload/images/20231030/20135750enL4P1JV0M.png

在 NestJS 中,你可以使用 HttpService 來在服務中發送 HTTP 請求。HttpService 提供了一組方法,使你能夠向其他服務或外部 API 發送 GET、POST、PUT、DELETE 等類型的請求。

以下是在 NestJS 中如何在服務中發送 HTTP 請求的詳細教學:

  1. 引入 HttpModule

    首先,你需要確保在你的模組中引入了 HttpModule。你可以在相應的模組中導入 HttpModule

    import { Module, HttpModule } from '@nestjs/common';
    
    @Module({
      imports: [HttpModule],
      // ...
    })
    export class YourModule {}
    
  2. 在服務中注入 HttpService

    在你的服務中,你需要注入 HttpService 以便使用它來發送 HTTP 請求:

    import { Injectable, HttpService } from '@nestjs/common';
    import { AxiosResponse } from 'axios';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class YourService {
      constructor(private httpService: HttpService) {}
    
      // ...
    }
    
  3. 使用 HttpService 來發送請求

    你可以在你的服務中使用 httpService 的方法來發送請求。以下是一個簡單的例子:

    import { Injectable, HttpService } from '@nestjs/common';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class YourService {
      constructor(private httpService: HttpService) {}
    
      fetchData(): Observable<any> {
        const url = '<https://api.example.com/data>';
    
        return this.httpService.get(url);
      }
    }
    

    在這個例子中,fetchData 方法使用 httpService.get() 方法向指定的 URL 發送 GET 請求。

  4. 處理請求的回應

    httpService 的請求方法(如 getpostput 等)返回一個可觀察對象(Observable)。你可以使用 RxJS 來處理這個可觀察對象的回應。

    以下是一個示例,展示了如何處理 GET 請求的回應:

    import { Injectable, HttpService } from '@nestjs/common';
    import { Observable } from 'rxjs';
    import { AxiosResponse } from 'axios';
    
    @Injectable()
    export class YourService {
      constructor(private httpService: HttpService) {}
    
      fetchData(): Observable<AxiosResponse<any>> {
        const url = '<https://api.example.com/data>';
    
        return this.httpService.get(url);
      }
    }
    

這樣,你就可以在 NestJS 服務中使用 HttpService 來發送 HTTP 請求了。請注意,發送請求可能需要進一步的配置,比如設置請求標頭、處理錯誤等,具體取決於你的需求。

所以NestJS本身就有預設的HttpModule了
要用的時候把它引入,就能把httpService注入到自己的service內

HTTP 請求:使用第三方套件

https://ithelp.ithome.com.tw/upload/images/20231030/201357509YcNmfyS9S.png

除了 NestJS 預設提供的 HttpService,你也可以使用其他第三方套件來處理 HTTP 請求。以下列舉了一些常用的第三方套件,並簡單介紹它們的特色以及提供一個使用範例:

  1. Axios:

    • 特色

      • Axios 是一個基於 Promise 的 HTTP 客戶端,可以用於瀏覽器和 Node.js 環境中。
      • 它支援拦截器、取消請求、自動轉換 JSON 等功能。
    • 安裝

      npm install axios
      
    • 範例

      import axios from 'axios';
      
      async function fetchData() {
        try {
          const response = await axios.get('<https://api.example.com/data>');
          console.log(response.data);
        } catch (error) {
          console.error(error);
        }
      }
      
      fetchData();
      
  2. node-fetch:

    • 特色

      • node-fetch 是一個 Node.js 環境下的輕量級 HTTP 客戶端,它提供了類似於瀏覽器的 fetch API。
    • 安裝

      npm install node-fetch
      
    • 範例

      const fetch = require('node-fetch');
      
      async function fetchData() {
        try {
          const response = await fetch('<https://api.example.com/data>');
          const data = await response.json();
          console.log(data);
        } catch (error) {
          console.error(error);
        }
      }
      
      fetchData();
      
  3. Superagent:

    • 特色

      • Superagent 是一個簡潔的 HTTP 請求庫,支援 Promise、拦截器等功能。
    • 安裝

      npm install superagent
      
    • 範例

      const superagent = require('superagent');
      
      async function fetchData() {
        try {
          const response = await superagent.get('<https://api.example.com/data>');
          console.log(response.body);
        } catch (error) {
          console.error(error);
        }
      }
      
      fetchData();
      

這些是一些常用的第三方套件,你可以根據項目需求選擇適合的套件來處理 HTTP 請求。請注意,這些套件都提供了詳細的文件和示例,你可以查閱它們的官方文檔以獲得更多信息。

NestJS內建Http Module vs. 第三方套件

https://ithelp.ithome.com.tw/upload/images/20231030/201357507j2K8VXbJV.png

使用 NestJS 預設的 HttpModule 和使用第三方套件進行 HTTP 請求都有各自的優缺點,具體取決於你的項目需求和偏好。以下是兩者之間的優劣分別:

使用預設的 HttpModule:

優點:

  1. 集成性
    • HttpModule 是 NestJS 提供的內建模組,與框架完全整合,無需額外安裝或配置。
  2. 簡單易用
    • 對於基本的 HTTP 請求,使用 HttpModule 非常簡單直觀,無需引入額外的套件。
  3. 內建支援拦截器
    • 可以利用 HttpModule 內建的拦截器來對請求或響應進行處理,這在某些場景下非常方便。

缺點:

  1. 功能相對有限
    • HttpModule 提供了基本的請求功能,但對於一些複雜的場景,如處理複雜的請求配置、拦截器鏈等,可能需要進一步的處理。
  2. 依賴於框架
    • 使用 HttpModule 會使你的代碼與 NestJS 框架深度耦合,可能不夠彈性。

使用第三方套件(如 Axios、node-fetch 等):

優點:

  1. 功能豐富
    • 第三方套件通常提供了更多功能,如拦截器、取消請求、進度監控等,適用於複雜的場景。
  2. 獨立性
    • 使用第三方套件使你的代碼與特定的套件無關聯,更具彈性,可以輕易切換或更換套件。
  3. 生態系統
    • 一些第三方套件(如 Axios)擁有強大的生態系統,提供了豐富的功能和插件。

缺點:

  1. 額外的依賴
    • 使用第三方套件需要額外安裝、配置,增加了專案的依賴項。
  2. 學習成本
    • 如果你選擇一個新的第三方套件,你可能需要花一些時間來學習它的用法和特性。

總的來說,使用預設的 HttpModule 會使得基本的 HTTP 請求變得簡單明了,並且與 NestJS 框架完全整合。然而,如果你需要更複雜的功能或者想要保持更大的彈性,使用第三方套件可能是一個更好的選擇。你可以根據你的項目需求來選擇最適合的方法。

統整重點

https://ithelp.ithome.com.tw/upload/images/20231030/201357503gw1QCZISp.png

綜合以上對話內容,我們可以得出以下重點:

  1. NestJS 中的 Service
    • Service 在 NestJS 中是用於處理業務邏輯的一個重要組件,它可以用來處理數據操作、邏輯判斷等。
  2. NestJS 中的 Exception
    • 在 NestJS 中,我們可以使用 HttpException 來拋出 HTTP 相關的錯誤,並通過指定 HttpStatus 來指定狀態碼。
  3. 內建例外類型
    • NestJS 提供了一系列內建的例外類型,如 BadRequestExceptionUnauthorizedException 等,便於處理不同的錯誤情況。
  4. HTTP 狀態碼
    • HTTP 狀態碼用於指示網絡請求的結果,常見的狀態碼有 200 OK、201 Created、400 Bad Request、404 Not Found 等。
  5. 使用第三方套件進行 HTTP 請求
    • 可以使用第三方套件如 Axios、node-fetch 等來處理 HTTP 請求,這些套件提供了更多功能和彈性。

總結來說,NestJS 提供了強大的功能和工具,使得開發者可以輕鬆處理前端工程師所需的功能,並提供了豐富的例外處理機制來處理不同的錯誤情況。同時,開發者可以根據項目需求選擇使用預設的 HttpModule 或者第三方套件來進行 HTTP 請求,以滿足特定的開發需求。

請注意,在開發過程中,適當地選擇適用的工具和套件,並理解它們的特性和優劣,將有助於提高開發效率並保證代碼品質。祝你在成為一名優秀的前端工程師的路上一切順利!如果你有任何進一步的問題,隨時向我提問。

結語

本次的對話內容記錄在這邊:
https://chat.openai.com/share/52706357-dec6-4d75-9392-7f2c4bf89abc

到這邊為止,就把NestJS的module、Controller、DTO & class-validator、中間件、Swagger以及Service的內容給討論完畢了!

在Service中,我們可以使用NodeJS語法來進行運算、處理邏輯,也可以發送HTTP請求
遇到例外時可以搭配正確的HTTP狀態碼回傳…

還少了什麼呢? YES~ 我們的資料庫~
在前面的篇章:[【Day39】ChatGPT請教教我:基礎SQL語法!NodeJS連結SQL!]時(https://ithelp.ithome.com.tw/articles/10340372)
除了介紹SQL語法之外,還有在NodeJS中搭配mysql2這個套件,進行mysql的連線

下一篇!介紹除了直接用mysql2之外的SQL套件,kenx!
我是學會拋出例外與對應狀態碼的前端工程師一宵三筵,我們明天見!


上一篇
【Day47】ChatGPT請教教我:NestJS!(六)- API測試兼文件!swagger!
下一篇
【Day49】ChatGPT請教教我:NestJS!(八)- SQL & Knex!SQL注入攻擊!?
系列文
用ChatGPT詠唱來完成工作與點亮前後端技能樹56
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言