Spring Boot 2.1.5(27)---WebFlux REST API 全局异常处理 Error Handling
本文内容
- 为什么要全局异常处理?
- 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相关推荐
- java flux api,SpringBoot学习系列-WebFlux REST API 全局异常处理
本文内容 为什么要全局异常处理? WebFlux REST 全局异常处理实战 小结 摘录:只有不断培养好习惯,同时不断打破坏习惯,我们的行为举止才能够自始至终都是正确的. 一.为什么要全局异常处理? ...
- spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获
spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获 当你的某个控制器内的某个方法报错,基本上回显示出java错误代码,非常不友好,这 ...
- 解决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 ...
- spring boot之http,页面状态跳转与异常处理实战
spring boot之http,页面状态跳转与异常处理实战 参考文章: (1)spring boot之http,页面状态跳转与异常处理实战 (2)https://www.cnblogs.com/yu ...
- SpringBoot 系列教程(八十五):Spring Boot使用MD5加盐验签Api接口之前后端分离架构设计
加密算法参考: 浅谈常见的七种加密算法及实现 加密算法参考: 加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用 目的: 通过对API接口请求报文签名,后端进行验签处理, ...
- Spring Boot 2 快速教程:WebFlux 快速入门(二)
2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘 ...
- Spring Boot 2 快速教程:WebFlux Restful CRUD 实践(三)
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第102篇原创 03:WebFlux Web CR ...
- Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf 、 Mongodb 实践(六)
摘要:首先说明一下该博客教程是转载泥瓦匠BYSocket的文章 原创出处 https://www.bysocket.com 「作者公众号:泥瓦匠BYSocket 」 原文地址:https://www. ...
- Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf(五)
摘要:首先说明一下该博客教程是转载泥瓦匠BYSocket的文章 原创出处 https://www.bysocket.com「作者公众号:泥瓦匠BYSocket 」 原文地址:https://www.b ...
最新文章
- android 多个属性值,android布局属性值fill_parent和match_parent
- leetcode算法题--LRU缓存机制
- 2021高考成绩查询时间 新闻,教育资讯:2021高考成绩一般公布时间 多久出成绩...
- Python知识:opencv实现的直方图
- (寒假CF)Choosing Symbol Pairs
- Keepalived实现双机热备
- java21天打卡Day13-正则表达式
- python 线程-threding示例使用
- MTK modem编译
- VS Code unins000尝试在目标目录创建文件时出错
- Redis下载部署配置运行及修改登录地址、端口和密码
- html编写在线打字通,前端代码练习 - 在线打字测试(dazi.kukuw.com)
- 利用HY-SRF05 超声波模块实现超声波的测距避障
- 小程序如何自定义组件
- 明明是那么好的人,却又是那么伤人的人
- HTTP传输协议详解(传输过程及数据格式详细)
- DeadException
- Nuke Essential Training Nuke基本训练 Lynda课程中文字幕
- c语言 编写 简单万年历
- iOS 中横竖屏切换
热门文章
- 1006.arm 板中杀死进程
- makefile文件管理
- 【STM32】STM32F4 CAN2只能发送无法接收问题解决
- 进程的调度策略与进程的状态
- bomb android实战,android 仿qq app源码下载(bmob)
- java保护表格_读密码保护的工作表(版本 - Excel中95,97-2003)的Java
- Quartz.NET开源作业调度框架系列(二):CronTrigger-转
- BZOJ 1567: [JSOI2008]Blue Mary的战役地图 矩阵二维hash
- Spring MVC实现Junit Case
- 代理模式——结构型模式(7)