欢迎来到本系列教程的第三部分,该系列教程是关于使用 NestJS、Prisma 和 PostgreSQL 构建 REST API!在本教程中,您将学习如何在 NestJS 应用程序中执行错误处理。
目录
介绍
在本系列的第一章中,您创建了一个新的 NestJS 项目并将其与 Prisma、PostgreSQL 和 Swagger 集成。然后,您为博客应用程序的后端构建了一个基本的 REST API。在第二章中,您学习了如何进行输入验证和转换。
在本章中,您将学习如何在 NestJS 中处理错误。您将了解两种不同的策略
- 首先,您将学习如何在 API 控制器中的应用程序代码中直接检测并抛出错误。
- 接下来,您将学习如何使用异常过滤器来处理应用程序中的未处理异常。
在本教程中,您将使用在第一章中构建的 REST API。您不需要完成第二章就可以学习本教程。
开发环境
要学习本教程,您需要
- ... 安装 Node.js。
- ... 安装 Docker 和 Docker Compose。如果您使用的是 Linux,请确保您的 Docker 版本为 20.10.0 或更高版本。您可以通过在终端中运行
docker version
来检查您的 Docker 版本。 - ... 可选 安装 Prisma VS Code 扩展。Prisma VS Code 扩展为 Prisma 添加了一些非常好的 IntelliSense 和语法突出显示。
- ... 可选 访问 Unix shell(如 Linux 和 macOS 中的终端/shell)以运行本系列中提供的命令。
如果您没有 Unix shell(例如,您使用的是 Windows 机器),您仍然可以学习,但 shell 命令可能需要针对您的机器进行修改。
克隆存储库
本教程的起点是本系列第一部分的结尾。它包含一个用 NestJS 构建的基本 REST API。
本教程的起点位于 end-rest-api-part-1
分支的 GitHub 存储库 中。要开始使用,请克隆存储库并检出 end-rest-api-part-1
分支
现在,执行以下操作开始
- 导航到克隆的目录
- 安装依赖项
- 使用 Docker 启动 PostgreSQL 数据库
- 应用数据库迁移
- 启动项目
注意:第 4 步还将生成 Prisma Client 并为数据库播种。
现在,您应该能够访问 http://localhost: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 数据库的数据库连接字符串。
注意:有关这些组件的更多信息,请阅读本教程系列的第一部分。
直接检测和抛出异常
本节将教您如何在应用程序代码中直接抛出异常。您将解决 GET /articles/:id
端点中的一个问题。目前,如果您为此端点提供一个不存在的 id
值,它将返回空内容以及 HTTP 200
状态,而不是错误。
例如,尝试发出一个 GET /articles/234235
请求
要解决此问题,您必须更改 articles.controller.ts
中的 findOne
方法。如果文章不存在,您将抛出一个 NotFoundException
,这是 NestJS 提供的一个内置异常。
更新 articles.controller.ts
中的 findOne
方法
如果您再次发出相同的请求,您应该会收到一条用户友好的错误消息
使用异常过滤器处理异常
专用异常层的优势
您在上一节中检测到一个错误状态并手动抛出了一个异常。在许多情况下,异常将由您的应用程序代码自动生成。在这种情况下,您应该处理该异常并向用户返回适当的 HTTP 错误。
虽然可以在每个控制器中逐个手动处理异常,但出于许多原因,这不是一个好主意
- 它会使您的核心应用程序逻辑充斥着大量的错误处理代码。
- 您的许多端点将处理类似的错误,例如找不到资源。您将不得不在许多地方重复相同的错误处理代码。
- 由于错误处理逻辑分散在许多位置,因此很难更改您的错误处理逻辑。
为了解决这些问题,NestJS 有一个异常层,负责处理应用程序中未处理的异常。在 NestJS 中,您可以创建异常过滤器,这些过滤器定义了如何处理应用程序中抛出的不同类型的异常。
NestJS 全局异常过滤器
NestJS 有一个全局异常过滤器,它会捕获所有未处理的异常。要了解全局异常过滤器,让我们看一个例子。向具有以下主体的 POST /articles
端点发送两个请求
第一个请求将成功,但第二个请求将失败,因为您已经创建了一个具有相同 title
字段的文章。您将收到以下错误
如果您查看运行 NestJS 服务器的终端窗口,您应该会看到以下错误
从日志中可以看到,Prisma Client 由于 title
字段(在 Prisma 模式中标记为 @unique
)而抛出一个唯一约束验证错误。异常类型为 PrismaClientKnownRequestError
,并在 Prisma 命名空间级别导出。
由于 PrismaClientKnownRequestError
未被您的应用程序直接处理,因此它会被内置的全局异常过滤器自动处理。此过滤器会生成 HTTP 500
“内部服务器错误”响应。
创建一个手动异常过滤器
在本节中,您将创建一个自定义异常过滤器来处理您看到的 PrismaClientKnownRequestError
。此过滤器将捕获所有类型为 PrismaClientKnownRequestError
的异常,并向用户返回清晰的用户友好错误消息。
首先使用 Nest CLI 生成一个过滤器类
这会创建一个新文件 src/prisma-client-exception.filter.ts
,其中包含以下内容
注意:还会创建一个名为
src/prisma-client-exception.filter.spec.ts
的第二个文件,用于创建测试。您现在可以忽略此文件。
由于 catch
方法为空,因此您将收到 eslint
的错误。按如下方式更新 PrismaClientExceptionFilter
中的 catch
方法实现
在这里,您进行了以下更改
- 为了确保此过滤器捕获类型为
PrismaClientKnownRequestError
的异常,您已将其添加到@Catch
装饰器。 - 异常过滤器扩展了 NestJS 核心包中的
BaseExceptionFilter
类。此类为catch
方法提供了一个默认实现,该实现向用户返回“内部服务器错误”响应。您可以在 NestJS 文档中了解有关此内容的更多信息。 - 您添加了一个
console.error
语句,用于将错误消息记录到控制台。这对于调试目的很有用。
Prisma 为许多不同类型的错误抛出 PrismaClientKnownRequestError
。因此,您需要弄清楚如何从 PrismaClientKnownRequestError
异常中提取错误代码。PrismaClientKnownRequestError
异常具有一个 code
属性,其中包含错误代码。您可以在Prisma 错误消息参考中找到错误代码列表。
您要查找的错误代码是 P2002
,它发生在唯一约束冲突时。您现在将更新 catch
方法,以便在此错误发生时抛出一个 HTTP 409 Conflict
响应。您还将向用户提供自定义错误消息。
像这样更新您的异常过滤器实现
在这里,您正在访问底层框架 Response
对象并直接修改响应。默认情况下,express 是 NestJS 在底层使用的 HTTP 框架。对于 P2002
之外的任何异常代码,您都将发送默认的“内部服务器错误”响应。
注意:对于生产应用程序,请注意不要在错误消息中向用户泄漏任何敏感信息。
将异常过滤器应用于您的应用程序
现在,为了使 PrismaClientExceptionFilter
生效,您需要将其应用于某个范围。可以将异常过滤器限定于单个路由(方法范围)、整个控制器(控制器范围)或整个应用程序(全局范围)。
通过更新 main.ts
文件,将异常过滤器应用于整个应用程序
现在,尝试向 POST /articles
端点发出相同的请求
这次您将获得更用户友好的错误消息
由于 PrismaClientExceptionFilter
是一个全局过滤器,因此它可以处理应用程序中所有路由的这种特定类型的错误。
我建议扩展异常过滤器实现以处理其他错误。例如,您可以添加一个 case 来处理 P2025
错误代码,当在数据库中找不到记录时会发生此错误。对于此错误,您应该返回状态代码 HttpStatus.NOT_FOUND
。这对于 PATCH /articles/:id
和 DELETE /articles/:id
端点很有用。
奖励:使用 nestjs-prisma
包处理 Prisma 异常
到目前为止,您已经学习了在 NestJS 应用程序中手动处理 Prisma 异常的不同技术。有一个专用的包用于将 Prisma 与 NestJS 一起使用,名为 nestjs-prisma
,您也可以使用它来处理 Prisma 异常。此包是一个很好的选择,因为它消除了许多样板代码。
有关安装和使用该包的说明,请参见 nestjs-prisma
文档。使用此包时,您无需手动创建单独的 prisma
模块和服务,因为此包会自动为您创建它们。
您可以在文档的异常过滤器部分中了解如何使用该包来处理 Prisma 异常。在本教程的未来章节中,我们将更详细地介绍 nestjs-prisma
包。
总结和最后说明
恭喜!在本教程中,您使用了现有的 NestJS 应用程序,并学习了如何集成错误处理。您学习了两种不同的错误处理方法:直接在您的应用程序代码中以及通过创建异常过滤器。
在本章中,您学习了如何处理 Prisma 错误。但是这些技术本身并不局限于 Prisma。您可以使用它们来处理应用程序中的任何类型的错误。
您可以在 end-error-handling-part-3
分支中找到本教程的完整代码,该分支位于 GitHub 仓库 中。如果您发现问题,请随时在仓库中提出 issue 或提交 PR。您也可以直接在 Twitter 上联系我。
不要错过下一篇文章!
注册 Prisma 新闻通讯