开发者社区> 二哥聊RPA> 正文

Spring Boot 2.x 系列教程:WebFlux REST API 全局异常处理 Error Handling

简介: 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本文内容 为什么要全局异常处理? WebFlux REST 全局异常处理实战 小结 摘录:只有不断培养好习惯,同时不断打破坏习惯,我们的行为举止才能够自始至终都是正确的。
+关注继续查看
摘要: 原创出处 https://www.bysocket.com 

本文内容

  • 为什么要全局异常处理?
  • WebFlux REST 全局异常处理实战
  • 小结

摘录:只有不断培养好习惯,同时不断打破坏习惯,我们的行为举止才能够自始至终都是正确的。

一、为什么要全局异常处理?

前后端分离开发,一般提供 REST API,正常返回会有响应体,异常情况下会有对应的错误码响应。

挺多人咨询的,Spring Boot MVC 异常处理用切面 @RestControllerAdvice 注解去实现去全局异常处理。那 WebFlux 如何处理异常?如何实现统一错误码异常处理?

全局异常处理的好处:

  • 异常错误码等统一维护
  • 避免一些重复代码

二、WebFlux REST 全局异常处理实战

下面介绍如何统一拦截异常,进行响应处理。

2.1 工程信息

  • 运行环境:JDK 7 或 8,Maven 3.0+
  • 技术栈:SpringBoot 2.1.3
  • 代码地址:https://github.com/JeffLi1993/springboot-learning-example
  • 模块工程名: 2-x-spring-boot-webflux-handling-errors

工程结构:

├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── Application.java
        │               ├── error
        │               │   ├── GlobalErrorAttributes.java
        │               │   ├── GlobalErrorWebExceptionHandler.java
        │               │   └── GlobalException.java
        │               ├── handler
        │               │   └── CityHandler.java
        │               └── router
        │                   └── CityRouter.java
        └── resources
            └── application.properties

application.properties 无须配置,默认即可
Application Spring Boot 应用启动类,是可以用来启动 Spring Boot 应用。其包含了 @SpringBootApplication 注解和 SpringApplication 类,并调用 SpringApplication 类的 run() 方法,就可以启动该应用。

具体实现类的关系图如下:

file

2.2 CityRouter 路由器类

城市路由器代码如下:

@Configuration
public class CityRouter {

    @Bean
    public RouterFunction<ServerResponse> routeCity(CityHandler cityHandler) {
        return RouterFunctions.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), cityHandler::helloCity);
    }

}

RouterFunctions 对请求路由处理类,即将请求路由到处理器,这将一个 GET 请求 /hello 路由到处理器 cityHandler 的 helloCity 方法上。跟 Spring MVC 模式下的 HandleMapping 类似。

RouterFunctions.route(RequestPredicate, HandlerFunction) 方法,对应的 参是请求参数和处理函数,如果请求匹配,就调 对应的处理器函数。

2.3 CityHandler 服务处理类

城市服务器处理类,代码如下:

@Component
public class CityHandler {

    public Mono<ServerResponse> helloCity(ServerRequest request) {
        return ServerResponse.ok().body(sayHelloCity(request), String.class);
    }

    private Mono<String> sayHelloCity(ServerRequest request) {
        Optional<String> cityParamOptional = request.queryParam("city");
        if (!cityParamOptional.isPresent()) {
            throw new GlobalException(HttpStatus.INTERNAL_SERVER_ERROR, "request param city is ERROR");
        }

        return Mono.just("Hello," + cityParamOptional.get());
    }
}

Mono:实现发布者,并返回 0 或 1 个元素,即单对象。Mono 是响应流 Publisher 具有基础 rx 操作符。可以成功发布元素或者错误。用 Mono 作为返回对象,是因为返回包含了一个 ServerResponse 对象,而不是多个元素。

ServerResponse 是对响应的封装,可以设置响应状态,响应头,响应正文。比如 ok 代表的是 200 响应码、MediaType 枚举是代表这文本内容类型、返回的是 String 的对象。

ServerRequest 是对请求的封装。从请求中拿出 city 的值,如果没有的话则抛出对应的异常。GlobalException 是封装的全局异常。

Mono.justOrEmpty():从一个 Optional 对象或 null 对象中创建 Mono。

2.4 GlobalError 处理类

如图:

file

GlobalException 全局异常类,代码如下:

public class GlobalException extends ResponseStatusException {

    public GlobalException(HttpStatus status, String message) {
        super(status, message);
    }

    public GlobalException(HttpStatus status, String message, Throwable e) {
        super(status, message, e);
    }
}

GlobalErrorAttributes 全局异常属性值类,代码如下:

@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(request, includeStackTrace);

        if (getError(request) instanceof GlobalException) {
            GlobalException ex = (GlobalException) getError(request);
            map.put("exception", ex.getClass().getSimpleName());
            map.put("message", ex.getMessage());
            map.put("status", ex.getStatus().value());
            map.put("error", ex.getStatus().getReasonPhrase());

            return map;
        }

        map.put("exception", "SystemException");
        map.put("message", "System Error , Check logs!");
        map.put("status", "500");
        map.put("error", " System Error ");
        return map;
    }
}

重写了父类 DefaultErrorAttributes 默认错误属性类的 getErrorAttributes 获取错误属性方法,从服务请求封装 ServerRequest 中获取对应的异常。

然后判断是否是 GlobalException,如果是 CityHandler 服务处理类抛出的 GlobalException,则返回对应的异常的信息。

GlobalErrorWebExceptionHandler 全局异常处理类,代码如下:

@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext,
            ServerCodecConfigurer serverCodecConfigurer) {
        super(g, new ResourceProperties(), applicationContext);
        super.setMessageWriters(serverCodecConfigurer.getWriters());
        super.setMessageReaders(serverCodecConfigurer.getReaders());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {

        final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);

        return ServerResponse.status(HttpStatus.BAD_REQUEST)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .body(BodyInserters.fromObject(errorPropertiesMap));
    }

}

代码解析如下:

  • AbstractErrorWebExceptionHandler 抽象类是用来处理全局错误时进行扩展和实现
  • @Order 注解标记 AspectJ 的切面排序,值越小拥有越高的优先级,这里设置优先级偏高。
  • 构造函数将 GlobalErrorAttributes 全局异常属性值类设置到 AbstractErrorWebExceptionHandler 抽象类的局部变量中。
  • 重写 getRoutingFunction 方法,设置对应的 RequestPredicates 和 Mono 服务响应对象
  • 将 GlobalErrorAttributes 的全局异常属性值 map,设置到新的 ServerResponse 即可。

到此基本结束。Spring Boot MVC 错误码如何实战,参考地址:https://www.bysocket.com/archives/1692

2.5 运行验证

在 IDEA 中执行 Application 类启动,任意正常模式或者 Debug 模式。然后打开浏览器访问:

http://localhost:8080/hello

异常界面如下:

file

可见,这是在 CityHandler 城市服务处理类逻辑中抛出的全局异常信息。那么正常情况会是如何?

改下 URL ,访问如下:

http://localhost:8080/hello?city=WenLing

正常界面如下:

file

三、小结

在 Spring 框架中没有代表错误响应的类,只是返回响应对象,一个 Map。如果需要定义业务的错误码返回体,参考错误码如何实战,参考地址:https://www.bysocket.com/archives/1692。

本文重点还是有别于 Spring Boot 传统 MVC 模式统一异常处理,实战了 WebFlux 全局异常处理机制。实战中这块扩展需要考虑:

  • 异常分层,从基类中扩展出来
  • 错误码设计分层,易扩展,比如在错误码中新增调用量字段…

代码示例

本文示例读者可以通过查看下面仓库的中的模块工程名: 2-x-spring-boot-webflux-handling-errors:

如果您对这些感兴趣,欢迎 star、follow、收藏、转发给予支持!

参考资料

  • WebFlux REST API 全局异常处理:https://www.bysocket.com/archives/2100
  • https://dzone.com/articles/exception-handling-in-spring-boot-webflux-reactive

以下专题教程也许您会有兴趣


文章导航


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【API架构】REST API 行业辩论:OData vs GraphQL vs ORDS
【API架构】REST API 行业辩论:OData vs GraphQL vs ORDS
14 0
【API架构】REST API 设计的原则和最佳实践
【API架构】REST API 设计的原则和最佳实践
25 0
了解API相关范式(RPC、REST、GraphQL)
了解API相关范式(RPC、REST、GraphQL) 前言 两个独立的应用程序经常需要相互访问交谈,或则可以是同一个应用程序,但部署在不同的服务器,或者现在常用的前后端分离式架构等等需要经常相互访问交谈,因此开发人员经常搭建桥梁API(Application Programming Interfaces) 关于API的定义,你可以简单看看这篇文章-- What is an API: Definition, Types, Specifications, Documentation
89 0
分布式系统核心:REST风格的架构,REST成熟度模型及REST API管理
正如前文所述,正确、完整地使用REST是困难的,关键在于RoyFielding所定义的REST只是一种架构风格,它并不是规范,所以也就缺乏可以直接参考的依据。好在Leonard Richardson补充了这方面的不足。
123 0
【Elasticsearch】Rest风格API(二)
【Elasticsearch】Rest风格API
38 0
【Elasticsearch】Rest风格API(一)
【Elasticsearch】Rest风格API
56 0
国王小组:数字货币交易所开发中Binance REST API接入方式
秒合约交易所开发详细丨秒合约交易所系统开发详细及规则丨秒合约交易所系统源码部署 数字货币交易所开发源码丨数字货币交易所系统开发(详细及逻辑) 交易所开发正式版丨区块链交易所系统开发实现技术功能及源码 交易所开发案例丨交易所系统开发(详细及流程)丨交易所成熟及源码系统 交易所开发(稳定版)丨交易所系统开发(方案及逻辑)丨 交易所系统源码功能 什么是去中心化交易所系统开发丨浅谈uniswap丨justswap 交易所源码(整体架构演示) 交易所搭建,交易所源码是怎么开发的? 区块链交易所怎么搭建? 区块链交易所平台中常见的开发模式有哪些? 区块链交易所如何开发(介绍区块链应用开发的流程) 区块链
164 0
0Spirng Boot2.5 开发实战 REST API 模拟淘宝订单接口|学习笔记
快速学习0Spirng Boot2.5 开发实战 REST API 模拟淘宝订单接口。
70 0
《Java Spring Boot开发实战系列课程【第15讲】:Spring Boot 2.0 API与Spring REST Docs实战》电子版地址
Java Spring Boot开发实战系列课程【第15讲】:Spring Boot 2.0 API与Spring REST Docs实战
51 0
YARN REST API 总结
YARN REST API 总结
246 0
+关注
二哥聊RPA
公号「二哥聊RPA」。关注我,一起洞察 RPA ◎ RPA创业者 ◎ 现明堂红木商业顾问,为十月结晶、可啦啦、鑫蜂维等多家企业提供 RPA 咨询服务 ◎ 希望帮助大家在 RPA 上做一些力所能及的事情
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
数据资源的基础设施API总线
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多