欢迎来到本系列教程的第三篇,关于使用 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 服务器的终端窗口,您应该会看到以下错误
从日志中您可以看到,由于 title
字段(在 Prisma 模式中标记为 @unique
),Prisma Client 抛出了唯一约束验证错误。异常类型为 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 存储库中找到本教程的完成代码。如果您发现问题,请随时在存储库中提出 issue 或提交 PR。您也可以直接在 Twitter 上与我联系。
不要错过下一篇文章!
注册 Prisma 新闻通讯