Web应用程序中的错误处理环节非常重要。从客户的角度出发,必须了解请求是如何进行的,并且在发生任何错误的情况下,最重要的是向客户提供为啥会错误的原因,尤其是如果错误是由于客户的行为引起的。在不同的情况下,通知具体原因是必须的——考虑到服务器端验证,由于错误请求或简单未找到情况而导致的业务逻辑错误等等。
Webflux中错误处理的机制与Spring MVC不同。响应式应用程序的核心构建块– Mono和Flux提供了一种特殊的方式来处理错误情况,尽管旧的基于异常的错误处理在某些情况下仍可能有效,但它违反了Spring Webflux的本质。在这篇文章中,将概述有关业务错误和数据缺失时如何处理Webflux中的错误。在本文中,将不讨论技术错误,因为那是由Spring框架处理的。
我们何时需要处理Webflux中的错误
在我们进入错误处理主题之前,让我们定义我们想要实现的目标。假设我们使用常规架构构建应用程序,该架构具有垂直分层和水平分层的功能。这意味着,我们的应用程序包含三个主要层:存储库(一个用于处理数据访问),服务(一个用于执行自定义业务逻辑)和处理程序(用于处理HTTP请求/响应;将其理解为Spring MVC中的控制器)。
在存储库级别(处理外部API和所有数据访问组件的客户端,也请在这里抽象)通常发生所谓的技术错误。例如,数据库可能出了点问题,因此存储库组件将引发错误。该错误是的子类,RuntimeException如果使用Spring,则由框架处理,因此无需在这做任何事情。最后会出现500的错误代码。
另一种情况是我们所说的业务错误。在发生此类违规情况时,必须为客户提供有意义的回应。如果返回图表,则会注意到,此类错误通常发生在服务级别,因此必须对其进行处理。
那么,如何在Spring Webflux API中处理错误并提供错误响应?
从业务逻辑开始
在使用响应式Webflux之前,我们经常使用基于异常的错误处理。这意味着提供了一个自定义的运行时异常(的子类ResponseStatusException),该异常与特定的http状态映射。
但是,Webflux方法是不同的。主楼块Mono和Flux组件,这在整个应用程序的流程链(注意,从这里我指的是既Mono和Flux为Mono)。在任何级别上抛出异常都会破坏异步特性。此外,Spring反应性存储库(一个例子而已)ReactiveMongoRepository不使用异常来指示错误情况。Mono容器提供了传播错误条件和空条件的功能:
- Mono.empty()=此静态方法创建一个Mono无需发出任何物品即可完成的容器
- Mono.error()=此静态方法创建一个Mono容器,该容器在订阅后立即以错误终止
有了这些知识,我们现在可以设计一种假设的登录/注册流程,以处理以下情况:
1)实体不存在
2)发生错误。
如果在存储库级别发生错误,Spring将通过返回Mono错误状态进行处理。找不到所需的数据时-空Mono。我们还可以在服务内部添加一些对业务规则的验证。看一下这篇文章中重构的注册流程代码:
![7c637407b04d358f4882245fddefdc08.png](https://i-blog.csdnimg.cn/blog_migrate/13ebbf34a7600f2058b8e92b4ed7ddb1.jpeg)
在处理程序中显示http响应
此级别可以对应于Spring MVC中的旧良好控制器。处理程序的目的是使用HTTP请求和响应,从而将业务逻辑与外部世界连接。在最简单的实现中,注册过程的处理程序如下所示:
注册处理程序
![65012d097229928ee463472ea0e679df.png](https://i-blog.csdnimg.cn/blog_migrate/2ac596f7ec112e809a69c6ac83a21fda.jpeg)
该代码执行以下基本操作:
- 接受来自HTTP请求的主体有效负载
- 调用业务逻辑组件
- 返回结果作为HTTP响应
但是,它没有利用我们在上一节中讨论的自定义错误处理的优势。当用户已经存在时,我们需要处理一种情况。为此,让我们重构此代码块:
重构了注册处理程序
![f41ac86cf306f74b6100e5906c04cbaf.png](https://i-blog.csdnimg.cn/blog_migrate/4f5dccaf6e335d85db6b450f5645a172.jpeg)
在此代码中,我们可以向客户端指定错误的原因。如果用户确实存在于数据库中,我们将向409提供错误代码,因此他必须使用另一个电子邮件地址进行注册过程。那就是商业错误。对于技术错误,我们的API显示500错误代码。
我们可以验证,当我们创建一个新用户时,一切正常,预期结果是200成功代码
另一方面,如果您尝试使用相同的电子邮件地址进行注册,则API应该以400错误代码响应。
此外,我们不再需要实体success字段SignupResponse,因为使用错误代码处理了不成功的注册。我想提到的是另一种情况–空响应的问题。
特殊情况:对空结果的回应
为什么这是特例?从技术上讲,空响应不是错误,也不是业务错误或技术错误。开发人员之间有不同的意见,如何正确处理它。
让我们来看看登录流程。与登录流程相反,登录流程是一种常见情况。对于注册流程,我们必须确保该用户尚不存在,但是对于登录,我们必须知道该用户已经存在。如果没有,我们需要返回一个错误响应。
看一下login处理程序的初始实现:
![db7dd47610f477c848e4764c7f9ec8c9.png](https://i-blog.csdnimg.cn/blog_migrate/2dc4afd438b3b75f67ed1504252aa533.jpeg)
从服务组件的角度来看,我们可以预期三种情况:
- 用户存在并且登录成功=返回 LoginResponse
- 用户存在,但登录被拒绝=返回错误
- 用户不存在=返回空用户
我们已经看到了如何处理处理程序中的错误。空响应情况使用switchIfEmpty方法进行管理。看一下重构的实现:
![5929e71ee3f526a4179cdf86a062ada9.png](https://i-blog.csdnimg.cn/blog_migrate/fe1754a4204e151eb6fdbe7befb9b994.jpeg)
请注意,与onErrorResume方法不同,switchIfEmpty接受替代方法Mono而不是函数作为参数。然后检查一下是否都按期运行,现有用户实体和有效凭证的登录名应返回有效响应。
如果提交的凭据错误(密码不匹配),但是用户确实存在(案例2),我们将获得Bad request错误代码。
最后,如果存储库找不到用户实体,则处理程序将回答Not found。
喜欢记得关注一下哦。