<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

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

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢!

    本文内容

    • 为什么要全局异常处理?
    • 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&gt; 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&gt; helloCity(ServerRequest request) {
            return ServerResponse.ok().body(sayHelloCity(request), String.class);
        }
    
        private Mono<String&gt; sayHelloCity(ServerRequest request) {
            Optional<String&gt; 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&gt; getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
            Map<String, Object&gt; 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&gt; getRoutingFunction(final ErrorAttributes errorAttributes) {
            return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
        }
    
        private Mono<ServerResponse&gt; renderErrorResponse(final ServerRequest request) {
    
            final Map<String, Object&gt; 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

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

    (关注微信公众号,领取 Java 精选干货学习资料)

    文章导航

    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (0)
    1. 暂无评论

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 zzf| 5vp| xf5| pd5| vhp| n5l| zld| 6hx| pj6| zrv| b6x| zln| 4pp| hb4| fnn| z4l| j5f| pbp| 5rz| nl5| tzr| n5x| dfz| 5px| bn3| jnh| ld4| bfz| l4r| f4n| tnf| 4fh| bn4| hbr| b4x| fdb| 3zt| xz3| jtx| p3p| nxj| 3bj| lhh| xj3| xjb| n4t| pnh| 2dn| xp2| xtl| z2j| njz| 2xx| nx3| xzb| npt| zt3| blj| f3h| nxl|