Spring Webflux中的错误处理


Web应用程序中的错误处理主题非常重要。从客户的角度出发,必须了解请求是如何进行的,并且在发生任何错误的情况下至关重要的是向客户提供有效的原因,尤其是如果错误是由于客户的行为引起的。在不同的情况下,向呼叫者通知具体原因很重要–考虑服务器端验证,由于错误请求或简单找不到的情况而导致的业务逻辑错误。

Webflux中错误处理的机制与我们从Spring MVC所了解的不同。响应式应用程序的核心构建块–Mono并Flux提供了一种处理错误情况的特殊方法,尽管基于旧的基于异常的错误处理在某些情况下仍可能有效,但它违反了Spring Webflux的本质。在本文中,我将概述有关业务错误和数据缺失时如何处理Webflux中的错误。我不会在本文中介绍技术错误,因为它们是由Spring框架处理的。

我们何时需要处理Webflux中的错误 在我们进入错误处理主题之前,让我们定义我们想要实现的目标。假设我们使用常规架构构建应用程序,该架构具有垂直分层和水平分层的功能。这意味着,我们的应用程序包含三个主要层:存储库(一个用于处理数据访问),服务(一个用于执行自定义业务逻辑)和处理程序(用于处理HTTP请求/响应;将其理解为Spring MVC中的控制器)。

虽然,我需要在这里澄清一下,但从技术角度来看,错误并不相同。在存储库级别(处理外部API和所有数据访问组件的客户端也请在这里也进行抽象)通常会发生所谓的技术错误。例如,数据库可能出了点问题,因此存储库组件将引发错误。此错误是的子类,RuntimeException如果我们使用Spring,则由框架处理,因此我们无需在此处做任何事情。这将导致500错误代码。

另一种情况是我们所说的业务错误。这违反了您的应用程序的自定义业务规则。尽管可能会有这样的错误被认为不是一个好主意,但它们是不可避免的,因为如前所述,在发生此类违规情况时,我们必须向客户提供有意义的回应。如果您返回图表,则会注意到,此类错误通常发生在服务级别,因此我们必须对其进行处理。

现在,让我们看看如何在Spring Webflux API中处理错误并提供错误响应。

从业务逻辑开始 在我以前的文章中,我演示了如何为Spring Webflux REST API构建两因素身份验证。该示例遵循上述架构,并由存储库,服务和处理程序组织而成。如前所述,业务错误发生在服务内部。在使用响应式Webflux之前,我们经常使用基于异常的错误处理。这意味着您提供了一个自定义的运行时异常(子类ResponseStatusException),该异常与特定的http状态映射。

但是,Webflux方法是不同的。主要构建模块是Mono和Flux组件,它们链接到整个应用程序流程(请注意,在这里,我将二者都称为Mono和)。在任何级别上引发异常都会破坏异步性质。此外,Spring反应性存储库(例如)不使用异常来指示错误情况。容器提供了传播错误条件和空条件的功能:FluxMonoReactiveMongoRepositoryMono

  • Mono.empty()=此静态方法创建一个Mono无需发出任何物品即可完成的容器 *Mono.error()=此静态方法创建一个Mono容器,该容器在订阅后立即以错误终止 有了这些知识,我们现在可以设计一种假设的登录/注册流程,以处理以下情况:1)实体不存在且2)错误发生。如果在存储库级别发生错误,Spring将通过返回Mono错误状态进行处理。找不到所需的数据时-空Mono。我们还可以在服务内部添加一些对业务规则的验证。看一下这篇文章中重构的注册流程代码:

20210316132856.png

现在,我们有了业务逻辑。下一阶段是将结果映射为处理程序级别的HTTP响应。

在处理程序中显示http响应 此级别可以对应于Spring MVC中的旧良好控制器。处理程序的目的是处理HTTP请求和响应,并以此将业务逻辑与外部世界联系起来。在最简单的实现中,注册过程的处理程序如下所示:

20210316132931.png

此代码执行以下基本操作:

  1. 接受来自HTTP请求的主体有效负载
  2. 调用业务逻辑组件
  3. 返回结果作为HTTP响应 但是,它没有利用我们在上一节中讨论的自定义错误处理的优势。当用户已经存在时,我们需要处理一种情况。为此,让我们重构此代码块:

20210316133007.png

请注意,与之前的帖子相比,我在这里使用bodyValue()而不是body()。这是因为body方法实际上接受生产者,而data此处的对象是实体(SignupResponse)。为此,我使用bodyValue()传递的值数据。在这里阅读更多内容。

在此代码中,我们可以向客户端指定错误的原因。如果用户确实存在于数据库中,我们将向409呼叫者提供错误代码,因此他/他必须使用另一个电子邮件地址进行注册过程。那就是商业错误。对于技术错误,我们的API显示500错误代码。

而且,我们不再需要success用于SignupResponse实体的字段,因为使用错误代码处理了不成功的注册。我想提到的是另一种情况–空响应的问题。这就是我们在登录示例中看到的内容。

特殊情况:对空结果的回应 为什么这是特例?从技术上讲,空响应不是错误,也不是业务错误或技术错误。开发人员之间有不同的意见,如何正确地处理它。我认为,即使不是传统意义上的例外,我们仍然需要向404客户端公开错误代码,以证明缺少所请求的信息。

让我们来看看登录流程。与注册流程相反,登录流程是一种常见情况。对于注册流程,我们必须确保该用户尚不存在,但是对于登录,我们必须知道该用户已经存在。如果没有,我们需要返回一个错误响应。

看一下login处理程序的初始实现:

20210316133103.png

从服务组件的角度来看,我们可以预期以下三种情况:

用户存在并且登录成功=返回 LoginResponse 用户存在,但登录被拒绝=返回错误 用户不存在=返回空用户 我们已经看到了如何处理处理程序中的错误。空响应情况使用switchIfEmpty方法进行管理。看一下重构的实现: 20210316133133.png

请注意,与onErrorResume方法不同,它switchIfEmpty接受替代方法Mono而不是函数作为参数。现在,让我们检查一切是否按预期进行。现有用户实体和有效凭据的登录名应返回有效响应:


原文链接:http://codingdict.com