Spring WebFlux怎么进行异常处理源码解析

 更新时间:2023年08月25日 11:47:30   作者:六七十三  
这篇文章主要为大家介绍了Spring WebFlux怎么进行异常处理源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1 概览

在本教程中,我们将通过一个实际示例了解Spring WebFlux项目中处理错误的各种策略。

我们还将指出使用一种策略比另一种策略更有利的地方,并在最后提供完整源代码的链接。

2 开始示例代码

maven 设置和之前介绍 Spring WebFlux 的文章一样,

对于我们的示例,我们将使用一个 RESTful 端点,它将用户名作为查询参数并返回“Hello username”作为结果。首先,让我们创建一个路由函数,这个路由函数将 “/hello” 请求路由到处理程序中名为 handleRequest 的方法,代码如下:

@Bean
public RouterFunction<ServerResponse> routeRequest(Handler handler) {
    return RouterFunctions.route(RequestPredicates.GET("/hello")
      .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), 
        handler::handleRequest);
    }

然后,我们定义个 handleRequest() 方法,这个方法调用 sayHello() 方法,并找到一个在 ServerResponse 中包含或返回其(sayHello方法的返回)结果的方法。

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return 
      //...
        sayHello(request)
      //...
}

最后,实现 sayHello 方法,实现很简单,直接拼接 hello 和参数 username 即可。

private Mono<String> sayHello(ServerRequest request) {
    try {
        // 应该是 username
        return Mono.just("Hello, " + request.queryParam("username").get());
    } catch (Exception e) {
        return Mono.error(e);
    }
}

因此,只要我们的请求中带了 username 参数,我们的请求就能正常返回。举个例子:我们请求“/hello?username=Tonni”,类似请求,我们总是能正常返回。

然而,如果我们的请求不带 username 参数,我们的请求就会抛出异常了。下面,我们来看看 Spring WebFlux 在哪里以及怎么重组代码来处理我们的异常。

3 方法级别处理异常

Mono 和 Flux API 中内置了两个关键运算符来处理方法级别的错误。我们简要探讨一下它们及其用法。

3.1 onErrorReturn 处理异常

当我们碰到异常的时候,我们可以用 onErrorReturn 来直接返回静态结果:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .onErrorReturn("Hello Stranger")
      .flatMap(s -> ServerResponse.ok()
        .contentType(MediaType.TEXT_PLAIN)
        .bodyValue(s));
}

这里,每当有问题的连接函数抛出异常的时候,我们直接返回一个静态结果:“Hello Stranger”。

3.2 onErrorResume 处理异常

有三种使用 onErrorResume 处理异常的方式:

  • 计算动态回调值
  • 通过回调函数执行其他分支
  • 捕获、包装并重新抛出错误,例如,作为自定义业务异常

让我们看看怎么计算值:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .flatMap(s -> ServerResponse.ok()
        .contentType(MediaType.TEXT_PLAIN)
        .bodyValue(s))
      .onErrorResume(e -> Mono.just("Error " + e.getMessage())
        .flatMap(s -> ServerResponse.ok()
          .contentType(MediaType.TEXT_PLAIN)
          .bodyValue(s)));
}

这里,每当 sayHello 抛出异常的时候,我们返回一个 “Error + 异常信息(e.getMessage())”。

接下来,我们看看当异常发生调用回调函数:

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .flatMap(s -> ServerResponse.ok()
        .contentType(MediaType.TEXT_PLAIN)
        .bodyValue(s))
      .onErrorResume(e -> sayHelloFallback()
        .flatMap(s -> ServerResponse.ok()
        .contentType(MediaType.TEXT_PLAIN)
        .bodyValue(s)));
}

这里,每当 sayHello 抛出异常的时候,我们执行一个其他函数 sayHelloFallback 。

最后,使用 onErrorResume 来捕获、包装并重新抛出错误,举例如:NameRequiredException

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return ServerResponse.ok()
      .body(sayHello(request)
      .onErrorResume(e -> Mono.error(new NameRequiredException(
        HttpStatus.BAD_REQUEST, 
        "username is required", e))), String.class);
}

这里,当 sayHello 抛出异常的时候,我们抛出一个定制异常 NameRequiredException,message是 “username is required”。

全局处理异常

目前为止,我们提供的所有示例都在方法级别上处理了错误处理。但是我们可以选择在全局层面处理异常。为此,我们只需要两步:

  • 自定义一个全局错误响应属性
  • 实现全局错误处理 handler

这样我们程序抛出的异常将会自动转换成 HTTP 状态和 JSON 错误体。我们只需要继承 DefaultErrorAttributes 类然后重写 getErrorAttributes 方法就可以自定义这些。

public class GlobalErrorAttributes extends DefaultErrorAttributes{
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
      ErrorAttributeOptions options) {
        Map<String, Object> map = super.getErrorAttributes(
          request, options);
        map.put("status", HttpStatus.BAD_REQUEST);
        map.put("message", "username is required");
        return map;
    }
}

这里,当异常抛出是,我们想要返回 BAD_REQUEST 状态码和“username is required”错误信息作为错误属性的一部分。

然后,我们来实现全局错误处理 handler。

为此,Spring 提供了一个方便的 AbstractErrorWebExceptionHandler 类,供我们在处理全局错误时进行扩展和实现:

@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends 
    AbstractErrorWebExceptionHandler {
    // constructors
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(
      ErrorAttributes errorAttributes) {
        return RouterFunctions.route(
          RequestPredicates.all(), this::renderErrorResponse);
    }
    private Mono<ServerResponse> renderErrorResponse(
       ServerRequest request) {
       Map<String, Object> errorPropertiesMap = getErrorAttributes(request, 
         ErrorAttributeOptions.defaults());
       return ServerResponse.status(HttpStatus.BAD_REQUEST)
         .contentType(MediaType.APPLICATION_JSON)
         .body(BodyInserters.fromValue(errorPropertiesMap));
    }
}

在这个例子中,我们设置 handler 的 order 为 -2。这是为了给它一个比默认 handler,也就是 DefaultErrorWebExceptionHandler 一个更高的优先级,它设置的 order 为 -1。

errorAttributes 对象将是我们在 Web 异常处理程序的构造函数中传递的对象的精确副本。理想情况下,这应该是我们自定义的错误属性类。

然后我们明确生命了,我们希望将所有的异常处理路由到 renderErrorResponse() 中。

最后,我们获取了错误属性并插入到服务端响应体中。

然后这会生成一个 JSON 响应,其中包含了错误的详细信息,HTTP 状态、机器端的异常信息等。对于浏览器端,它有一个 “white-label”错误处理程序,可以以 HTML 形式呈现相同的数据,当然这个页面可以定制。

总结

在本文中,我们研究了在 Spring WebFlux 项目中处理异常的集中策略,并指出使用一个策略优于其他策略的地方。

源码在 github 上

翻译自原文:https://www.baeldung.com/spri...

以上就是Spring WebFlux怎么进行异常处理源码解析的详细内容,更多关于Spring WebFlux异常处理的资料请关注脚本之家其它相关文章!

相关文章

  • sqlmap 使用教程(从入门到进阶)

    sqlmap 使用教程(从入门到进阶)

    sqlmap是一款强大的开源 SQL 注入自动化测试工具,支持识别并利用多种类型的 SQL 注入漏洞(Boolean-based、Time-based、Error-based、Stacked Queries 等),本文给大家介绍sqlmap使用教程,感兴趣的朋友一起看看吧
    2016-11-11
  • java中断线程的正确姿势完整示例

    java中断线程的正确姿势完整示例

    这篇文章主要为大家介绍了java中断线程的正确姿势完整示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • SpringBoot 图书管理系统(删除、强制登录、更新图书)详细代码

    SpringBoot 图书管理系统(删除、强制登录、更新图书)详细代码

    在企业开发中,通常不采用delete语句进行物理删除,而是使用逻辑删除,逻辑删除通过修改标识字段来表示数据已被删除,方便数据恢复,本文给大家介绍SpringBoot 图书管理系统实例代码,感兴趣的朋友跟随小编一起看看吧
    2024-09-09
  • Intellij IDEA根据maven依赖名查找它是哪个pom.xml引入的(图文详解)

    Intellij IDEA根据maven依赖名查找它是哪个pom.xml引入的(图文详解)

    这篇文章主要介绍了Intellij IDEA根据maven依赖名查找它是哪个pom.xml引入的,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • java中为何重写equals时必须重写hashCode方法详解

    java中为何重写equals时必须重写hashCode方法详解

    这篇文章主要给大家介绍了关于java中为什么重写equals时必须重写hashCode方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • MyBatis-Plus分页插件不生效的解决方法

    MyBatis-Plus分页插件不生效的解决方法

    这篇文章主要介绍了MyBatis-Plus分页插件不生效的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • SpringMVC的REST风格的四种请求方式总结

    SpringMVC的REST风格的四种请求方式总结

    下面小编就为大家带来一篇SpringMVC的REST风格的四种请求方式总结。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java中的SynchronousQueue队列详解

    Java中的SynchronousQueue队列详解

    这篇文章主要介绍了Java中的SynchronousQueue队列详解,SynchronousQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的,SynchronousQueue和其他的BlockingQueue不同的是SynchronousQueue的capacity是0,需要的朋友可以参考下
    2023-12-12
  • Java 按行读取文件按行写入文件并以空格分割字符串的方法

    Java 按行读取文件按行写入文件并以空格分割字符串的方法

    今天小编就为大家分享一篇Java 按行读取文件按行写入文件并以空格分割字符串的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • maven项目下solr和spring的整合配置详解

    maven项目下solr和spring的整合配置详解

    这篇文章主要介绍了maven项目下solr和spring的整合配置详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11

最新评论