关于云计算:Spring-Cloud-Gateway过滤器精确控制异常返回分析篇

6次阅读

共计 4930 个字符,预计需要花费 13 分钟才能阅读完成。

欢送拜访我的 GitHub

这里分类和汇总了欣宸的全副原创 (含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 在《Spring Cloud Gateway 批改申请和响应 body 的内容》一文中,咱们通过 filter 胜利批改申请 body 的内容,过后留下个问题:在 filter 中如果产生异样(例如申请参数不非法),抛出异样信息的时候,调用方收到的返回码和 body 都是 Spring Cloud Gateway 框架解决后的,调用方无奈依据这些内容晓得真正的谬误起因,如下图:

  • 本篇工作就是剖析上述景象的起因,通过浏览源码搞清楚返回码和响应 body 生成的具体逻辑

提前小结

  • 这里将剖析后果提前小结进去,如果您很繁忙没太多工夫却又想晓得最终起因,间接关注以下小结即可:
  1. Spring Cloud Gateway 利用中,有个 ErrorAttributes 类型的 bean,它的 getErrorAttributes 办法返回了一个 map
  2. 利用抛出异样时,返回码来自上述 map 的 status 的值,返回 body 是整个 map 序列化的后果
  3. 默认状况下 ErrorAttributes 的实现类是 DefaultErrorAttributes
  • 再看上述 map 的 status 值(也就是 response 的返回码),在 DefaultErrorAttributes 是如何生成的:
  1. 先看异样对象是不是 ResponseStatusException 类型
  2. 如果是 ResponseStatusException 类型,就调用异样对象的 getStatus 办法作为返回值
  3. 如果不是 ResponseStatusException 类型,再看异样类有没有 ResponseStatus 注解,
  4. 如果有,就取注解的 code 属性作为返回值
  5. 如果异样对象既不是 ResponseStatusException 类型,也没有 ResponseStatus 注解,就返回 500
  • 最初看 map 的 message 字段(也就是 response body 的 message 字段),在 DefaultErrorAttributes 是如何生成的:
  1. 异样对象是不是 BindingResult 类型
  2. 如果不是 BindingResult 类型,就看是不是 ResponseStatusException 类型
  3. 如果是,就用 getReason 作为返回值
  4. 如果也不是 ResponseStatusException 类型,就看异样类有没有 ResponseStatus 注解,如果有就取该注解的 reason 属性作为返回值
  5. 如果通过注解获得的 reason 也有效,就返回异样的 getMessage 字段
  • 上述内容就是本篇精髓,然而并未蕴含剖析过程,如果您对 Spring Cloud 源码感兴趣,请容许欣宸陪伴您来一次短暂的源码浏览之旅

Spring Cloud Gateway 错误处理源码

  • 首先要看的是配置类 ErrorWebFluxAutoConfiguration.java,这里面向 spring 注册了两个实例,<font color=”red”> 每个都十分重要 </font>,咱们先关注第一个,也就是说 ErrorWebExceptionHandler 的实现类是 DefaultErrorWebExceptionHandler:

  • 解决异样时,会通过 FluxOnErrorResume 调用到这个 ErrorWebExceptionHandler 的 handle 办法解决,该办法在其父类 AbstractErrorWebExceptionHandler.java 中,如下图,红框地位的代码是要害,异样返回内容就是在这里决定的:

  • 开展这个 getRoutingFunction 办法,可见会调用 renderErrorResponse 来解决响应:
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
    }
  • 关上 renderErrorResponse 办法,如下所示,水落石出了!
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
  // 取出所有错误信息
  Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
  
  // 结构返回的所有信息 
  return ServerResponse
           // 管制返回码
           .status(getHttpStatus(error))
           // 管制返回 ContentType
           .contentType(MediaType.APPLICATION_JSON)
           // 管制返回内容
           .body(BodyInserters.fromValue(error));
}
  • 通过上述代码,咱们失去两个重要论断:
  1. 返回给调用方的状态码,取决于 getHttpStatus 办法的返回值
  2. 返回给调用方的 body,取决于 error 的内容
  • 都曾经读到了这里,天然要看看 getHttpStatus 的外部,如下所示,status 来自入参:
protected int getHttpStatus(Map<String, Object> errorAttributes) {return (int) errorAttributes.get("status");
}
  • 至此,咱们能够得出一个论断:getErrorAttributes 办法的返回值是决定返回码和返回 body 的要害!
  • 来看看这个 getErrorAttributes 办法的庐山真面吧,在 DefaultErrorAttributes.java 中(回顾方才看 ErrorWebFluxAutoConfiguration.java 的时候,后面曾提到外面的货色都很重要,也包含 errorAttributes 办法):
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {Map<String, Object> errorAttributes = this.getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
        if (Boolean.TRUE.equals(this.includeException)) {options = options.including(new Include[]{Include.EXCEPTION});
        }

        if (!options.isIncluded(Include.EXCEPTION)) {errorAttributes.remove("exception");
        }

        if (!options.isIncluded(Include.STACK_TRACE)) {errorAttributes.remove("trace");
        }

        if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {errorAttributes.put("message", "");
        }

        if (!options.isIncluded(Include.BINDING_ERRORS)) {errorAttributes.remove("errors");
        }

        return errorAttributes;
    }
  • 篇幅所限,就不再开展上述代码了,间接上后果吧:
  1. 返回码来自 determineHttpStatus 的返回
  2. message 字段来自 determineMessage 的返回
  • 关上 determineHttpStatus 办法,终极答案揭晓,请关注中文正文:
private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // 异样对象是不是 ResponseStatusException 类型
        return error instanceof ResponseStatusException 
        // 如果是 ResponseStatusException 类型,就调用异样对象的 getStatus 办法作为返回值
        ? ((ResponseStatusException)error).getStatus() 
        // 如果不是 ResponseStatusException 类型,再看异样类有没有 ResponseStatus 注解,// 如果有,就取注解的 code 属性作为返回值
        : (HttpStatus)responseStatusAnnotation.getValue("code", HttpStatus.class)
        // 如果异样对象既不是 ResponseStatusException 类型,也没有 ResponseStatus 注解,就返回 500
        .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  • 另外,message 字段的内容也确定了:
    private String determineMessage(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        // 异样对象是不是 BindingResult 类型
        if (error instanceof BindingResult) {
            // 如果是,就用 getMessage 作为返回值
            return error.getMessage();} 
        // 如果不是 BindingResult 类型,就看是不是 ResponseStatusException 类型
        else if (error instanceof ResponseStatusException) {
            // 如果是,就用 getReason 作为返回值
            return ((ResponseStatusException)error).getReason();} else {
            // 如果也不是 ResponseStatusException 类型,// 就看异样类有没有 ResponseStatus 注解,如果有就取该注解的 reason 属性作为返回值
            String reason = (String)responseStatusAnnotation.getValue("reason", String.class).orElse("");
            if (StringUtils.hasText(reason)) {return reason;} else {
                // 如果通过注解获得的 reason 也有效,就返回异样的 getMessage 字段
                return error.getMessage() != null ? error.getMessage() : "";
            }
        }
    }
  • 至此,源码剖析已实现,最终的返回码和返回内容到底如何管制,置信聪慧的您心里应该无数了,下一篇《实战篇》咱们趁热打铁,写代码试试准确管制返回码和返回内容
  • 提前剧透,接下来的《实战篇》会有以下内容出现:
  1. 直接了当,管制返回码和 body 中的 error 字段
  2. 小小拦路虎,见招拆招
  3. 简略易用,通过注解管制返回信息
  4. 终极计划,齐全定制返回内容
  • 以上内容敬请期待,欣宸原创必不辜负您

你不孤独,欣宸原创一路相伴

  1. Java 系列
  2. Spring 系列
  3. Docker 系列
  4. kubernetes 系列
  5. 数据库 + 中间件系列
  6. DevOps 系列

欢送关注公众号:程序员欣宸

微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游 Java 世界 …
https://github.com/zq2599/blog_demos

正文完
 0