欢迎来到本系列教程的第三篇,关于使用 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 客户端并为数据库播种。
现在,您应该能够访问 https://127.0.0.1:3000/api/
的 API 文档。
项目结构和文件
您克隆的存储库应具有以下结构
此存储库中值得注意的文件和目录是
src
目录包含应用程序的源代码。有三个模块app
模块位于src
目录的根目录,是应用程序的入口点。它负责启动 Web 服务器。prisma
模块包含 Prisma 客户端,您的数据库接口。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 服务器的终端窗口,您应该看到以下错误
从日志中您可以看到,由于 title
字段(在 Prisma 模式中标记为 @unique
),Prisma 客户端抛出了唯一约束验证错误。异常类型为 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
方法实现
在这里,您进行了以下更改
- 为了确保此过滤器捕获
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
是一个全局过滤器,因此它可以处理应用程序中所有路由的这种特定类型的错误。
我建议扩展异常过滤器实现以处理其他错误。例如,您可以添加一个案例来处理 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 存储库中找到本教程的完成代码。如果您发现问题,请随时在存储库中提出问题或提交 PR。您也可以在 Twitter 上直接与我联系。
不要错过下一篇文章!
注册 Prisma 新闻通讯