本文内容

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

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

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

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

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

全局异常处理的好处:

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

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

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

2.1 工程信息

2.2 CityRouter 路由器类

城市路由器代码如下:

@Configuration
public class CityRouter {@Beanpublic 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 处理类

如图:

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 {@Overridepublic 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());}@Overrideprotected 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:8091/hello

异常界面如下:

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

改下 URL ,访问如下:

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

正常界面如下:

三、小结

在 Spring 框架中没有代表错误响应的类,只是返回响应对象,一个 Map。如果需要定义业务的错误码返回体,参考错误码如何实战

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

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

Spring Boot 2.1.5(27)---WebFlux REST API 全局异常处理 Error Handling相关推荐

  1. java flux api,SpringBoot学习系列-WebFlux REST API 全局异常处理

    本文内容 为什么要全局异常处理? WebFlux REST 全局异常处理实战 小结 摘录:只有不断培养好习惯,同时不断打破坏习惯,我们的行为举止才能够自始至终都是正确的. 一.为什么要全局异常处理? ...

  2. spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获

    spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获 当你的某个控制器内的某个方法报错,基本上回显示出java错误代码,非常不友好,这 ...

  3. 解决Spring Boot报错Mapped Statements collection already contains value for...Error while adding the mapp

    解决Spring Boot报错Mapped Statements collection already contains value for...Error while adding the mapp ...

  4. spring boot之http,页面状态跳转与异常处理实战

    spring boot之http,页面状态跳转与异常处理实战 参考文章: (1)spring boot之http,页面状态跳转与异常处理实战 (2)https://www.cnblogs.com/yu ...

  5. SpringBoot 系列教程(八十五):Spring Boot使用MD5加盐验签Api接口之前后端分离架构设计

    加密算法参考: 浅谈常见的七种加密算法及实现 加密算法参考: 加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用 目的: 通过对API接口请求报文签名,后端进行验签处理, ...

  6. Spring Boot 2 快速教程:WebFlux 快速入门(二)

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘 ...

  7. Spring Boot 2 快速教程:WebFlux Restful CRUD 实践(三)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第102篇原创 03:WebFlux Web CR ...

  8. Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf 、 Mongodb 实践(六)

    摘要:首先说明一下该博客教程是转载泥瓦匠BYSocket的文章 原创出处 https://www.bysocket.com 「作者公众号:泥瓦匠BYSocket 」 原文地址:https://www. ...

  9. Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf(五)

    摘要:首先说明一下该博客教程是转载泥瓦匠BYSocket的文章 原创出处 https://www.bysocket.com「作者公众号:泥瓦匠BYSocket 」 原文地址:https://www.b ...

最新文章

  1. android 多个属性值,android布局属性值fill_parent和match_parent
  2. leetcode算法题--LRU缓存机制
  3. 2021高考成绩查询时间 新闻,教育资讯:2021高考成绩一般公布时间 多久出成绩...
  4. Python知识:opencv实现的直方图
  5. (寒假CF)Choosing Symbol Pairs
  6. Keepalived实现双机热备
  7. java21天打卡Day13-正则表达式
  8. python 线程-threding示例使用
  9. MTK modem编译
  10. VS Code unins000尝试在目标目录创建文件时出错
  11. Redis下载部署配置运行及修改登录地址、端口和密码
  12. html编写在线打字通,前端代码练习 - 在线打字测试(dazi.kukuw.com)
  13. 利用HY-SRF05 超声波模块实现超声波的测距避障
  14. 小程序如何自定义组件
  15. 明明是那么好的人,却又是那么伤人的人
  16. HTTP传输协议详解(传输过程及数据格式详细)
  17. DeadException
  18. Nuke Essential Training Nuke基本训练 Lynda课程中文字幕
  19. c语言 编写 简单万年历
  20. iOS 中横竖屏切换

热门文章

  1. 1006.arm 板中杀死进程
  2. makefile文件管理
  3. 【STM32】STM32F4 CAN2只能发送无法接收问题解决
  4. 进程的调度策略与进程的状态
  5. bomb android实战,android 仿qq app源码下载(bmob)
  6. java保护表格_读密码保护的工作表(版本 - Excel中95,97-2003)的Java
  7. Quartz.NET开源作业调度框架系列(二):CronTrigger-转
  8. BZOJ 1567: [JSOI2008]Blue Mary的战役地图 矩阵二维hash
  9. Spring MVC实现Junit Case
  10. 代理模式——结构型模式(7)