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

本文内容

为什么要全局异常处理?

WebFlux REST 全局异常处理实战

小结

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

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

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

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

全局异常处理的好处:

异常错误码等统一维护

避免一些重复代码

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

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

2.1 工程信息

运行环境:JDK 7 或 8,Maven 3.0+

技术栈:SpringBoot 2.1.3

模块工程名: 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() 方法,就可以启动该应用。

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

39a133bcd343f5c1b1a563142a5a9bd9.png

2.2 CityRouter 路由器类

城市路由器代码如下:

@Configuration

public class CityRouter {

@Bean

public RouterFunction 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 helloCity(ServerRequest request) {

return ServerResponse.ok().body(sayHelloCity(request), String.class);

}

private Mono sayHelloCity(ServerRequest request) {

Optional 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 处理类

如图:

011913bb979a6d174831776179adfa5e.png

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 getErrorAttributes(ServerRequest request, boolean includeStackTrace) {

Map 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 getRoutingFunction(final ErrorAttributes errorAttributes) {

return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);

}

private Mono renderErrorResponse(final ServerRequest request) {

final Map 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 错误码如何实战,参考地址:www.bysocket.com/archives/16…

2.5 运行验证

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

http://localhost:8080/hello

复制代码

异常界面如下:

851d281e23c00d9d6edd86eb40f755ee.png

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

改下 URL ,访问如下:

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

复制代码

正常界面如下:

e3cd20d0888784e150f47709a2f70ff2.png

三、小结

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

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

异常分层,从基类中扩展出来

错误码设计分层,易扩展,比如在错误码中新增调用量字段…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值