2022年12月14日

使用 NestJS 和 Prisma 構建 REST API:錯誤處理

6分鐘閱讀

歡迎閱讀本系列關於使用 NestJS、Prisma 和 PostgreSQL 構建 REST API 的第三個教程!在本教程中,您將學習如何在 NestJS 應用程式中進行錯誤處理。

Building a REST API with NestJS and Prisma: Error Handling

目錄

簡介

在本系列的第一章中,您建立了一個新的 NestJS 專案,並將其與 Prisma、PostgreSQL 和 Swagger 整合。然後,您為部落格應用程式的後端構建了一個基本的 REST API。在第二章中,您學習瞭如何進行輸入驗證和轉換。

在本章中,您將學習如何在 NestJS 中處理錯誤。您將瞭解兩種不同的策略:

  1. 首先,您將學習如何在 API 控制器內的應用程式程式碼中直接檢測和丟擲錯誤。
  2. 接下來,您將學習如何使用異常過濾器來處理整個應用程式中未捕獲的異常。

在本教程中,您將使用第一章中構建的 REST API。您無需完成第二章即可繼續本教程。

開發環境

要繼續本教程,您需要:

  • ... 安裝 Node.js
  • ... 安裝 DockerDocker Compose。如果您使用 Linux,請確保您的 Docker 版本為 20.10.0 或更高。您可以透過在終端中執行 docker version 來檢查您的 Docker 版本。
  • ... 可選地安裝 Prisma VS Code 擴充套件。Prisma VS Code 擴充套件為 Prisma 提供了非常好的智慧感知和語法高亮。
  • ... 可選地擁有 Unix shell 訪問許可權(例如 Linux 和 macOS 中的終端/shell)以執行本系列中提供的命令。

如果您沒有 Unix shell(例如,您使用的是 Windows 機器),您仍然可以繼續,但 shell 命令可能需要根據您的機器進行修改。

克隆倉庫

本教程的起點是本系列第一部分的結尾。它包含一個使用 NestJS 構建的基本 REST API。

本教程的起點位於 GitHub 倉庫end-rest-api-part-1 分支中。要開始,請克隆倉庫並檢出 end-rest-api-part-1 分支。

現在,執行以下操作以開始:

  1. 導航到克隆的目錄
  1. 安裝依賴
  1. 使用 Docker 啟動 PostgreSQL 資料庫
  1. 應用資料庫遷移
  1. 啟動專案

注意:步驟 4 還會生成 Prisma Client 併為資料庫填充資料。

現在,您應該可以透過 https://:3000/api/ 訪問 API 文件。

專案結構和檔案

您克隆的倉庫應具有以下結構:

該倉庫中值得注意的檔案和目錄有:

  • src 目錄包含應用程式的原始碼。共有三個模組:
    • app 模組位於 src 目錄的根目錄下,是應用程式的入口點。它負責啟動 Web 伺服器。
    • prisma 模組包含 Prisma Client,它是您與資料庫的介面。
    • articles 模組定義了 /articles 路由的端點及配套的業務邏輯。
  • prisma 模組包含以下內容:
    • schema.prisma 檔案定義了資料庫 schema。
    • migrations 目錄包含資料庫遷移歷史。
    • seed.ts 檔案包含一個指令碼,用於向您的開發資料庫填充模擬資料。
  • docker-compose.yml 檔案定義了您的 PostgreSQL 資料庫的 Docker 映象。
  • .env 檔案包含您的 PostgreSQL 資料庫的連線字串。

注意:有關這些元件的更多資訊,請參閱本教程系列的第一部分

直接檢測和丟擲異常

本節將教您如何在應用程式程式碼中直接丟擲異常。您將解決 GET /articles/:id 端點中的一個問題。目前,如果您為此端點提供一個不存在的 id 值,它將返回空內容並附帶 HTTP 200 狀態,而不是錯誤。

例如,嘗試傳送一個 GET /articles/234235 請求:

Requesting an article that does not exist returns HTTP 200

為了解決這個問題,您必須修改 articles.controller.ts 中的 findOne 方法。如果文章不存在,您將丟擲 NotFoundException,這是 NestJS 提供的一個內建異常。

更新 articles.controller.ts 中的 findOne 方法:

如果您再次傳送相同的請求,您應該會收到一條使用者友好的錯誤訊息:

Requesting an article that does not exist returns HTTP 404

使用異常過濾器處理異常

專用異常層的優勢

在上一節中,您檢測到了一種錯誤狀態並手動丟擲了一個異常。在許多情況下,應用程式程式碼會自動生成異常。在這種情況下,您應該處理該異常並向用戶返回適當的 HTTP 錯誤。

雖然可以在每個控制器中手動逐個處理異常,但出於多種原因,這不是一個好主意:

  • 它會使您的核心應用程式邏輯充斥大量錯誤處理程式碼。
  • 您的許多端點將處理類似的錯誤,例如找不到資源。您將不得不在許多地方重複相同的錯誤處理程式碼。
  • 由於錯誤處理邏輯分散在許多位置,因此很難更改。

為了解決這些問題,NestJS 有一個異常層,負責處理應用程式中未捕獲的異常。在 NestJS 中,您可以建立異常過濾器來定義如何處理應用程式內部丟擲的不同型別的異常。

NestJS 全域性異常過濾器

NestJS 有一個全域性異常過濾器,它可以捕獲所有未處理的異常。為了理解全域性異常過濾器,我們來看一個例子。向 POST /articles 端點發送兩個帶有以下正文的請求:

第一個請求會成功,但第二個請求會失敗,因為您已經建立了一篇具有相同 title 欄位的文章。您將收到以下錯誤:

如果您檢視執行 NestJS 伺服器的終端視窗,您應該會看到以下錯誤:

從日誌中可以看出,Prisma Client 丟擲了一個唯一約束驗證錯誤,原因是 title 欄位在 Prisma schema 中被標記為 @unique。該異常的型別是 PrismaClientKnownRequestError,並在 Prisma 名稱空間級別匯出。

由於 PrismaClientKnownRequestError 未被您的應用程式直接處理,它會自動由內建的全域性異常過濾器處理。此過濾器會生成 HTTP 500 “內部伺服器錯誤”響應。

建立手動異常過濾器

在本節中,您將建立一個自定義異常過濾器來處理您看到的 PrismaClientKnownRequestError。此過濾器將捕獲所有型別為 PrismaClientKnownRequestError 的異常,並向用戶返回清晰的使用者友好錯誤訊息。

首先使用 Nest CLI 生成一個過濾器類:

這將建立一個新檔案 src/prisma-client-exception.filter.ts,內容如下:

注意:還會建立一個名為 src/prisma-client-exception.filter.spec.ts 的檔案用於建立測試。您可以暫時忽略此檔案。

您會收到來自 eslint 的錯誤,因為 catch 方法為空。請按如下方式更新 PrismaClientExceptionFilter 中的 catch 方法實現:

您在此處進行了以下更改:

  1. 為確保此過濾器捕獲型別為 PrismaClientKnownRequestError 的異常,您將其新增到了 @Catch 裝飾器。
  2. 異常過濾器擴充套件了 NestJS 核心包中的 BaseExceptionFilter 類。該類為 catch 方法提供了一個預設實現,向用戶返回“內部伺服器錯誤”響應。您可以在NestJS 文件中瞭解更多資訊。
  3. 您添加了一個 console.error 語句,將錯誤訊息記錄到控制檯。這對於除錯很有用。

Prisma 會針對許多不同型別的錯誤丟擲 PrismaClientKnownRequestError。因此,您需要弄清楚如何從 PrismaClientKnownRequestError 異常中提取錯誤程式碼。PrismaClientKnownRequestError 異常有一個包含錯誤程式碼的 code 屬性。您可以在Prisma 錯誤訊息參考中找到錯誤程式碼列表。

您要查詢的錯誤程式碼是 P2002,它在唯一約束衝突時發生。現在,您將更新 catch 方法,以便在此錯誤發生時丟擲 HTTP 409 Conflict 響應。您還將向用戶提供自定義錯誤訊息。

如下更新您的異常過濾器實現:

在這裡,您正在訪問底層框架的 Response 物件並直接修改響應。預設情況下,express 是 NestJS 內部使用的 HTTP 框架。對於 P2002 之外的任何異常程式碼,您將傳送預設的“內部伺服器錯誤”響應。

注意:對於生產應用程式,請注意不要在錯誤訊息中向用戶洩露任何敏感資訊。

將異常過濾器應用於您的應用程式

現在,要使 PrismaClientExceptionFilter 生效,您需要將其應用於特定的作用域。異常過濾器可以作用於單個路由(方法作用域)、整個控制器(控制器作用域)或整個應用程式(全域性作用域)。

透過更新 main.ts 檔案,將異常過濾器應用於您的整個應用程式:

現在,嘗試向 POST /articles 端點發送相同的請求:

這次您將收到一條更使用者友好的錯誤訊息:

由於 PrismaClientExceptionFilter 是一個全域性過濾器,它可以在您的應用程式中處理所有路由的此類錯誤。

我建議擴充套件異常過濾器的實現以處理其他錯誤。例如,您可以新增一個案例來處理 P2025 錯誤程式碼,該程式碼在資料庫中找不到記錄時發生。對於此錯誤,您應該返回狀態碼 HttpStatus.NOT_FOUND。這對於 PATCH /articles/:idDELETE /articles/:id 端點將非常有用。

額外內容:使用 nestjs-prisma 包處理 Prisma 異常

到目前為止,您已經學習了在 NestJS 應用程式中手動處理 Prisma 異常的不同技術。有一個專門用於在 NestJS 中使用 Prisma 的包,名為 nestjs-prisma,您也可以使用它來處理 Prisma 異常。這個包是一個值得考慮的優秀選項,因為它消除了大量樣板程式碼。

有關安裝和使用該包的說明可在nestjs-prisma 文件中找到。使用此包時,您無需手動建立單獨的 prisma 模組和服務,因為此包會自動為您建立它們。

您可以在文件的異常過濾器部分了解如何使用該包處理 Prisma 異常。在本教程的未來章節中,我們將更詳細地介紹 nestjs-prisma 包。

總結和最終說明

恭喜!在本教程中,您瞭解瞭如何在一個現有的 NestJS 應用程式中整合錯誤處理。您學習了兩種不同的錯誤處理方式:直接在應用程式程式碼中處理以及透過建立異常過濾器處理。

在本章中,您學習瞭如何處理 Prisma 錯誤。但這些技術本身並不僅限於 Prisma。您可以使用它們來處理應用程式中的任何型別的錯誤。

您可以在 GitHub 倉庫end-error-handling-part-3 分支中找到本教程的完整程式碼。如果您發現問題,請隨時在倉庫中提出 Issue 或提交 PR。您也可以直接在 Twitter 上聯絡我。

不要錯過下一篇文章!

訂閱 Prisma 郵件列表

© . This site is unofficial and not affiliated with Prisma Data, Inc.