歡迎來到使用 NestJS、Prisma 和 PostgreSQL 構建 REST API 系列的第二篇教程!在本教程中,您將學習如何在 API 中執行輸入驗證和轉換。
目錄
引言
在本系列的第一部分中,您建立了一個新的 NestJS 專案,並將其與 Prisma、PostgreSQL 和 Swagger 整合。然後,您為部落格應用程式的後端構建了一個基本的 REST API。
在本部分中,您將學習如何驗證輸入,使其符合您的 API 規範。執行輸入驗證是為了確保只有格式正確的資料從客戶端透過您的 API。驗證傳送到 Web 應用程式的任何資料的正確性是最佳實踐。這有助於防止畸形資料和濫用您的 API。
您還將學習如何執行輸入轉換。輸入轉換是一種技術,它允許您在請求的資料被路由處理程式處理之前,攔截並轉換從客戶端傳送的資料。這對於將資料轉換為適當的型別、為缺失欄位應用預設值、清理輸入等非常有用。
開發環境
要跟隨本教程,您需要具備以下條件:
- 已安裝 Node.js。
- 已安裝 Docker 或 PostgreSQL。
- 已安裝 Prisma VSCode 擴充套件。(可選)
- 可訪問 Unix shell(如 Linux 和 macOS 中的終端/shell)以執行本系列中提供的命令。(可選)
注意:
可選的 Prisma VS Code 擴充套件為 Prisma 添加了一些不錯的智慧感知和語法高亮功能。
如果您沒有 Unix shell(例如,您使用的是 Windows 機器),您仍然可以繼續學習,但可能需要根據您的機器修改 shell 命令。
克隆倉庫
本教程的起點是本系列第一部分的結尾。它包含一個用 NestJS 構建的初步 REST API。我建議您在開始本教程之前完成第一個教程。
本教程的起點可在 GitHub 倉庫的 begin-validation 分支中找到。要開始,請克隆倉庫並檢出 begin-validation 分支
現在,執行以下操作以開始:
- 導航到克隆的目錄
- 安裝依賴
- 使用 Docker 啟動 PostgreSQL 資料庫
- 應用資料庫遷移
- 啟動專案
注意:步驟 4 還會生成 Prisma Client 併為資料庫填充初始資料。
現在,您應該能夠透過 https://:3000/api/ 訪問 API 文件。
專案結構和檔案
您克隆的倉庫應具有以下結構:
此倉庫中值得注意的檔案和目錄有:
src目錄包含應用程式的原始碼。有三個模組:app模組位於src目錄的根部,是應用程式的入口點。它負責啟動 Web 伺服器。prisma模組包含 Prisma Client,即您的資料庫查詢構建器。articles模組定義了/articles路由的端點及相應的業務邏輯。
prisma模組包含以下內容:schema.prisma檔案定義了資料庫模式。migrations目錄包含資料庫遷移歷史。seed.ts檔案包含一個指令碼,用於向您的開發資料庫填充模擬資料。
docker-compose.yml檔案定義了您的 PostgreSQL 資料庫的 Docker 映象。.env檔案包含您的 PostgreSQL 資料庫的連線字串。
注意:有關這些元件的更多資訊,請參閱本教程系列的第一部分。
執行輸入驗證
要執行輸入驗證,您將使用 NestJS Pipes(管道)。管道作用於路由處理程式正在處理的引數。Nest 在路由處理程式之前呼叫管道,管道接收路由處理程式所需的引數。管道可以做很多事情,例如驗證輸入、向輸入新增欄位等。管道類似於中介軟體,但管道的範圍僅限於處理輸入引數。NestJS 提供了一些開箱即用的管道,但您也可以建立自己的自定義管道。
管道有兩種典型的用例:
- 驗證:評估輸入資料,如果有效,則保持不變地傳遞;否則,當資料不正確時丟擲異常。
- 轉換:將輸入資料轉換為所需的形式(例如,從字串轉換為整數)。
NestJS 驗證管道將檢查傳遞給路由的引數。如果引數有效,管道將不加修改地將引數傳遞給路由處理程式。但是,如果引數違反任何指定的驗證規則,管道將丟擲異常。
以下兩張圖展示了驗證管道對於任意 /example 路由的工作原理。

在本節中,您將重點關注驗證用例。
全域性設定 ValidationPipe
要執行輸入驗證,您將使用 NestJS 內建的 ValidationPipe。 ValidationPipe 提供了一種便捷的方法來強制所有傳入客戶端有效載荷的驗證規則,這些驗證規則透過 class-validator 包中的裝飾器宣告。
要使用此功能,您需要向專案中新增兩個包:
class-validator 包提供了用於驗證輸入資料的裝飾器,而 class-transformer 包提供了用於將輸入資料轉換為所需形式的裝飾器。這兩個包都與 NestJS 管道良好整合。
現在,在您的 main.ts 檔案中匯入 ValidationPipe,並使用 app.useGlobalPipes 方法使其在您的應用程式中全域性可用:
為 CreateArticleDto 新增驗證規則
現在,您將使用 class-validator 包為 CreateArticleDto 新增驗證裝飾器。您將為 CreateArticleDto 應用以下規則:
title不能為空,且長度不能少於 5 個字元。description的最大長度必須為 300。body和description不能為空。title、description和body必須是string型別,而published必須是boolean型別。
開啟 src/articles/dto/create-article.dto.ts 檔案並將其內容替換為以下內容:
這些規則將被 ValidationPipe 捕獲並自動應用於您的路由處理程式。使用裝飾器進行驗證的優點之一是 CreateArticleDto 仍然是 POST /articles 端點所有引數的單一真相來源。因此您無需定義單獨的驗證類。
測試您已設定的驗證規則。嘗試使用 POST /articles 端點建立一個文章,其中 title 非常短,例如這樣:
您應該會收到一個 HTTP 400 錯誤響應,以及響應正文中關於違反了哪些驗證規則的詳細資訊。

此圖解釋了 ValidationPipe 在幕後如何處理 /articles 路由的無效輸入:

從客戶端請求中剝離不必要的屬性
CreateArticleDTO 定義了需要傳送到 POST /articles 端點以建立新文章的屬性。UpdateArticleDTO 也做同樣的事情,但用於 PATCH /articles/{id} 端點。
目前,對於這兩個端點,都可以傳送 DTO 中未定義的額外屬性。這可能導致不可預見的錯誤或安全問題。例如,您可以手動將無效的 createdAt 和 updatedAt 值傳遞給 POST /articles 端點。由於 TypeScript 型別資訊在執行時不可用,您的應用程式將無法識別這些欄位在 DTO 中不可用。
舉個例子,嘗試向 POST /articles 端點發送以下請求:

透過這種方式,您可以注入無效值。在這裡,您建立了一篇文章,其 updatedAt 值早於 createdAt,這沒有意義。
為了防止這種情況,您需要從客戶端請求中過濾掉任何不必要的欄位/屬性。幸運的是,NestJS 也為此提供了開箱即用的解決方案。您只需在應用程式中初始化 ValidationPipe 時傳遞 whitelist: true 選項即可。
將此選項設定為 true 後,ValidationPipe 將自動移除所有未列入白名單的屬性,其中“未列入白名單”指的是沒有任何驗證裝飾器的屬性。需要注意的是,此選項將過濾掉所有沒有驗證裝飾器的屬性,即使它們在 DTO 中定義了也是如此。
現在,任何傳遞給請求的額外欄位/屬性都將由 NestJS 自動剝離,從而防止了之前展示的漏洞。
注意:NestJS
ValidationPipe是高度可配置的。所有可用的配置選項都在 NestJS 文件中進行了說明。如有必要,您還可以為您的應用程式構建自定義驗證管道。
使用 ParseIntPipe 轉換動態 URL 路徑
在您的 API 中,您目前接受 GET /articles/{id}、PATCH /articles/{id} 和 DELETE /articles/{id} 端點的 id 引數作為路徑的一部分。NestJS 從 URL 路徑中將 id 引數解析為字串。然後,該字串在傳遞給 ArticlesService 之前在您的應用程式程式碼中被轉換為數字。例如,檢視 DELETE /articles/{id} 路由處理程式:
由於 id 被定義為字串型別,Swagger API 也在生成的 API 文件中將此引數記錄為字串。這不直觀且不正確。

與其在路由處理程式內部手動進行此轉換,不如使用 NestJS 管道自動將 id 轉換為數字。將內建的 ParseIntPipe 新增到這三個端點的控制器路由處理程式中:
ParseIntPipe 將攔截字串型別的 id 引數,並自動將其解析為數字,然後傳遞給相應的路由處理程式。這還有一個優點,即在 Swagger 中將 id 引數正確地記錄為數字。

總結和最終說明
恭喜!在本教程中,您對現有的 REST API 進行了以下操作:
- 使用
ValidationPipe集成了驗證功能。 - 剝離了客戶端請求中不必要的屬性。
- 集成了
ParseIntPipe,用於解析string路徑變數並將其轉換為number。
您可能已經注意到 NestJS 大量依賴於裝飾器。這是一個非常有意的設計選擇。NestJS 旨在透過大量利用裝飾器來解決各種橫切關注點,從而提高程式碼可讀性和模組化。因此,控制器和服務方法無需被用於驗證、快取、日誌記錄等樣板程式碼所填充。
您可以在 GitHub 倉庫的 end-validation 分支中找到本教程的完整程式碼。如果您發現問題,請隨時在倉庫中提出問題或提交 PR。您也可以直接在 Twitter 上聯絡我。
不要錯過下一篇文章!
訂閱 Prisma 新聞通訊