├── node_modules
├── prisma
│ ├── migrations
│ ├── schema.prisma
│ └── seed.ts
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── main.ts
│ ├── articles
│ └── prisma
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── README.md
├── .env
├── docker-compose.yml
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json

此存儲庫中值得注意的文件和目錄包括:

請注意,有關這些組件的更多詳細信息,請閱讀本教程系列的第一部分。

直接檢測并引發異常

本節將指導您如何在應用程序代碼中直接觸發異常。您將針對一個終端節點的問題進行修復。目前,若向該終端節點傳遞無效的值,它不會返回 HTTP 狀態碼,而是直接返回錯誤信息。

例如,對于 GET 請求 /articles/:id,如果傳遞的 ID(如 234235)不存在,則會出現問題。

請求不存在的文章會返回 HTTP 200

要解決此問題,您必須在 findOnearticles.controller.ts 文件中的方法做出更改。如果文章不存在,您應該拋出一個 NotFoundException,這是 NestJS 提供的內置異常。

更新 findOnearticles.controller.ts 文件中的相關方法。

// src/articles/articles.controller.ts

import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
NotFoundException,
} from '@nestjs/common';

@Get(':id')
@ApiOkResponse({ type: ArticleEntity })
findOne(@Param('id') id: string) {
return this.articlesService.findOne(+id);
async findOne(@Param('id') id: string) {
const article = await this.articlesService.findOne(+id);
if (!article) {
throw new NotFoundException(Article with ${id} does not exist.); } return article; }

如果您再次發出相同的請求,您應該會收到一條用戶友好的錯誤消息:

請求不存在的文章會返回 HTTP 404

使用異常篩選器處理異常

專用例外層的優點

在上一節中,您學會了如何檢測錯誤狀態并手動拋出異常。然而,在許多情況下,應用程序代碼會自動生成異常。此時,您應該捕獲這些異常并向用戶返回合適的 HTTP 錯誤響應。

盡管您可以在每個控制器中單獨處理異常,但這并非最佳實踐,原因如下:

為了解決這些問題,NestJS 提供了一個異常層,用于處理整個應用程序中未捕獲的異常。在 NestJS 中,您可以創建異常過濾器來定義如何響應應用程序內部拋出的不同類型的異常。

NestJS 全局異常過濾器

NestJS 支持全局異常過濾器,它可以捕獲所有未處理的異常。為了理解全局異常過濾器的工作原理,我們來看一個示例。請向?/articles?終端節點發送 POST 請求,并觀察其響應。

{
"title": "Let’s build a REST API with NestJS and Prisma.",
"description": "NestJS Series announcement.",
"body": "NestJS is one of the hottest Node.js frameworks around. In this series, you will learn how to build a backend REST API with NestJS, Prisma, PostgreSQL and Swagger.",
"published": true
}

第一個請求將會成功,但第二個請求會因為您已經創建了具有相同標題(title)的文章而失敗。您將會收到一個與標題相關的錯誤。

{
"statusCode": 500,
"message": "Internal server error"
}

如果您查看運行 NestJS 服務器的終端窗口,您應該會看到以下錯誤:

[Nest] 6803  - 12/06/2022, 3:25:40 PM   ERROR [ExceptionsHandler]
Invalid this.prisma.article.create() invocation in /Users/tasinishmam/my-code/median/src/articles/articles.service.ts:11:32 8 constructor(private prisma: PrismaService) {} 9 10 create(createArticleDto: CreateArticleDto) { → 11 return this.prisma.article.create( Unique constraint failed on the fields: (title) Error: Invalid this.prisma.article.create() invocation in /Users/tasinishmam/my-code/median/src/articles/articles.service.ts:11:32 8 constructor(private prisma: PrismaService) {} 9 10 create(createArticleDto: CreateArticleDto) { → 11 return this.prisma.article.create( Unique constraint failed on the fields: (title)

從日志中,您可以觀察到 Prisma Client 因為某個字段觸發了唯一性約束驗證錯誤,該字段在 Prisma 架構中被標記為唯一。引發的異常類型是?PrismaClientKnownRequestError,并且這個異常在 Prisma 的命名空間級別被導出。具體地說,是因為?title?字段的唯一性約束導致了這個問題。

由于這個異常不是由您的應用程序直接捕獲和處理的,因此它會被內置的全局異常過濾器自動捕獲。然而,這個全局異常過濾器默認生成的是 HTTP “Internal Server Error”(500)響應。

創建手動異常篩選條件

在本節中,您將創建一個自定義的異常過濾器來處理上述類型的異常。這個自定義過濾器將能夠捕獲所有類型的異常,并且能夠為用戶返回清晰、友好的錯誤消息,特別是針對?PrismaClientKnownRequestError?這類異常。

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

npx nest generate filter prisma-client-exception

這將創建一個名為 src/prisma-client-exception.filter.ts 的新文件。

// src/prisma-client-exception.filter.ts

import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';

@Catch()
export class PrismaClientExceptionFilter<T> implements ExceptionFilter {
catch(exception: T, host: ArgumentsHost) {}
}

注意:請注意,已創建了一個名為 prisma-client-exception.filter.spec.ts 的測試文件。目前,您可以暫時忽略這個文件。

由于 prisma-client-exception.filter.ts 中的方法實現為空,您可能會收到來自 ESLint 的錯誤提示。為了解決這個問題,您需要更新該方法以實現一個捕獲 Prisma 客戶端異常的過濾器。這里假設您已經有了相關的實現計劃或代碼框架。

// src/prisma-client-exception.filter.ts

import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { Prisma } from '@prisma/client';

@Catch(Prisma.PrismaClientKnownRequestError) // 1
export class PrismaClientExceptionFilter extends BaseExceptionFilter { // 2
catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
console.error(exception.message); // 3

// default 500 error code
super.catch(exception, host);
}
}

在這里,您進行了以下更改:

  1. 為了確保此過濾器能夠捕獲?PrismaClientKnownRequestError?類型的異常,您將其添加到了?@Catch(PrismaClientKnownRequestError)?裝飾器中。
  2. 異常過濾器擴展了 NestJS 核心包中的 BaseExceptionFilter 類,該類為向用戶返回“Internal server error”響應的方法提供了默認實現。您可以在 NestJS 文檔中了解更多相關信息。
  3. 您添加了一個 console.error 語句,用于將錯誤消息記錄到控制臺,這對于調試目的非常有用。

由于 Prisma 會引發許多不同類型的錯誤,您需要弄清楚如何從捕獲的異常中提取錯誤代碼。異常對象具有一個包含錯誤代碼的屬性,您可以在 Prisma 錯誤消息參考中找到所有錯誤代碼的列表。

您要查找的錯誤代碼是?P2002,它通常發生在唯一約束沖突中。接下來,您將更新?catch?方法,以便在捕獲到此錯誤時引發一個帶有 HTTP 409 Conflict 狀態碼的響應,并向用戶提供自定義錯誤消息。

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

//src/prisma-client-exception.filter.ts

import { ArgumentsHost, Catch, HttpStatus } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { Prisma } from '@prisma/client';
import { Response } from 'express';

@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaClientExceptionFilter extends BaseExceptionFilter {
catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
console.error(exception.message);
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const message = exception.message.replace(/\n/g, '');

switch (exception.code) {
case 'P2002': {
const status = HttpStatus.CONFLICT;
response.status(status).json({
statusCode: status,
message: message,
});
break;
}
default:
// default 500 error code
super.catch(exception, host);
break;
}
}
}

在這里,您將訪問底層框架對象并直接修改響應。默認情況下,express 是 NestJS 在后臺使用的 HTTP 框架。對于除 之外的任何異常代碼,您將發送默認的 “Internal server error” 響應。

注意:對于生產應用程序,請注意不要在錯誤消息中向用戶泄露任何敏感信息。

將異常篩選器應用于應用程序

要使?PrismaClientExceptionFilter?生效,您需要將其應用于適當的范圍。異常過濾器的應用范圍可以是單個路由(方法級)、整個控制器(控制器級)或整個應用程序(全局級)。

為了將異常過濾器應用于整個應用程序,請更新 main.ts 文件。

// src/main.ts

import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { PrismaClientExceptionFilter } from './prisma-client-exception.filter';

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

const config = new DocumentBuilder()
.setTitle('Median')
.setDescription('The Median API description')
.setVersion('0.1')
.build();

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter));

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

現在,嘗試向 /articles 終端節點再次發出相同的 POST 請求。

{
"title": "Let’s build a REST API with NestJS and Prisma.",
"description": "NestJS Series announcement.",
"body": "NestJS is one of the hottest Node.js frameworks around. In this series, you will learn how to build a backend REST API with NestJS, Prisma, PostgreSQL and Swagger.",
"published": true
}

這次,您將收到一個更用戶友好的錯誤消息:

{
"statusCode": 409,
"message": "Invalid this.prisma.article.create() invocation in /Users/tasinishmam/my-code/median/src/articles/articles.service.ts:11:32 8 constructor(private prisma: PrismaService) {} 9 10 create(createArticleDto: CreateArticleDto) {→ 11 return this.prisma.article.create(Unique constraint failed on the fields: (title)" }

由于?PrismaClientExceptionFilter?是一個全局過濾器,因此它能夠為應用程序中的所有路由處理?PrismaClientKnownRequestError?這種特定類型的錯誤。

我建議您進一步擴展異常過濾器的實現,以處理其他類型的錯誤。例如,您可以添加一個分支來處理錯誤代碼 P2025,這個錯誤代碼通常在數據庫中找不到記錄時出現。對于這種情況,您應該返回 HttpStatus.NOT_FOUND 狀態碼。這對于 PATCH /articles/:id 和 DELETE /articles/:id 端點特別有用,因為這些操作通常依賴于特定記錄的存在。

使用nest js-prime包處理 Prisma 異常

到目前為止,您已經了解了在 NestJS 應用程序中手動處理 Prisma 異常的不同技術。有一個用于將 Prisma 與 NestJS 一起使用的專用包,稱為nestjs-prisma您還可以使用它來處理 Prisma 異常。此包是一個很好的考慮選擇,因為它刪除了大量樣板代碼。

有關如何安裝和使用 nestjs-prisma 包的詳細說明,請參考其官方文檔。使用此包時,您無需手動創建與 Prisma 相關的單獨模塊和服務,因為這些都會由包自動為您生成。

在?nestjs-prisma?文檔的“Exception Filter”部分,您可以了解到如何使用該包來處理 Prisma 異常。在本教程的后續章節中,我們將更深入地介紹這個軟件包。

總結和結束語

祝賀!在本教程中,您獲取了一個現有的 NestJS 應用程序,并學習了如何集成錯誤處理。您學習了兩種不同的錯誤處理方法:直接在應用程序代碼中處理和創建異常過濾器。

在本章中,您特別學習了如何處理由 Prisma 引發的錯誤。但請注意,這些技術不僅適用于 Prisma,它們同樣可以用于處理應用程序中的其他任何類型錯誤。

您可以在 end-error-handling-part-3 分支上找到本教程的結束點。如果您在學習過程中遇到問題,請隨時在存儲庫中提出疑問或提交 PR(Pull Request)。當然,您也可以直接在 Twitter 上與我聯系。

原文鏈接:https://www.prisma.io/blog/nestjs-prisma-error-handling-7D056s1kOop2

上一篇:

使用NestJS和Prisma構建REST API:輸入驗證和轉換

下一篇:

Photo StoryTelling —— 利用生成式AI與Google API,在您的相冊中進行創作
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費