欢迎访问我的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注册了两个实例,每个都非常重要,咱们先关注第一个,也就是说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

Spring Cloud Gateway过滤器精确控制异常返回(分析篇)的更多相关文章

  1. Spring Cloud Gateway过滤器精确控制异常返回(实战,控制http返回码和message字段)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 前文<Spring Cloud Gat ...

  2. Spring Cloud Gateway过滤器精确控制异常返回(实战,完全定制返回body)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 Spring Cloud Gateway应用 ...

  3. Spring Cloud Gateway之全局异常拦截器

    /** * @version 2019/8/14 * @description: 异常拦截器 * @modified: */ @Slf4j public class JsonExceptionHand ...

  4. Spring Cloud Gateway修改请求和响应body的内容

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. Spring Cloud Gateway实战之二:更多路由配置方式

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. Spring Cloud gateway 网关服务二 断言、过滤器

    微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...

  7. Spring Cloud Alibaba学习笔记(18) - Spring Cloud Gateway 内置的过滤器工厂

    参考:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-clou ...

  8. Spring Cloud Gateway(十):网关过滤器工厂 GatewayFilterFactory

    本文基于 spring cloud gateway 2.0.1 1.GatewayFilterFactory 简介 路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应. 路径过滤器的范 ...

  9. Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

随机推荐

  1. Tomcat 源码环境搭建

    Tomcat 源码搭建 下载源码 下载地址 :https://tomcat.apache.org/download-80.cgi#8.5.35 下载之后解压缩 导入Idea 添加pom.xml文件 & ...

  2. UML快速概述 - All you need to know about UML

    UML 是统一建模语言的缩写,就像使用一组图表来可视化软件建模的蓝图(或设计计划).它不仅可以让您彻底评估整个概念,还可以确保团队中的每个人都在同一页面上.   UML 图可以组织成两个不同的组. 结 ...

  3. 安装多个版本的MySQL

    安装多个版本的MySQL 之前在PC机上安装了 MySQL 5.5 后续发现了窗口函数,而窗口函数是 MySQL8 以后才支持的,故在本地又安装了一个 MySQL 8 安装MySQL 5.5 进入my ...

  4. Java编程开发学习路线图(附所有免费课程+在线自测)

    转自  https://yq.aliyun.com/articles/134286?spm=5176.100239.0.0.1UfveS 摘要: 长期以来,Java一直占据TIOBE编程语言排行版第一 ...

  5. OO第四单元作业总结及课程总结

    一.本单元作业架构设计 1.第一次作业 本单元首次接触到UML以及相关概念,在面对第一次作业时首先花了很大功夫去阅读官方接口中各种UmlElement的代码,才理解了输入的模型元素中各属性的含义.总的 ...

  6. Gitflow branch与Docker image tag命名冲突怎么办?

    谷歌还是比必应要好用一点. 在前公司,我根据主流的git flow 给团队搭建了一套devops流程,运行在 docker & k8s上. 在现代devops流程中,一般推荐使用git分支名或 ...

  7. 线路由器频段带宽是是20M好还是40M好

    无线路由器频段带宽还是40M好. 40M的信号强,速度快.   1.20MHz在11n的情况下能达到144Mbps带宽.穿透性不错.传输距离较远 40MHz在11n的情况下能达到300Mbps带宽.穿 ...

  8. 国产Linux服务器-Jexus的初步使用

    题记:年末研究了一些关于Net跨平台的东西,没错,就是Jexus,就是Windows下面的IIS. 官网:https://www.jexus.org/ 先看看官网的解释再说其他的问题,Jexus就是L ...

  9. Vulnhub实战-dr4g0n b4ll靶机👻

    Vulnhub实战-dr4g0n b4ll靶机 地址:http://www.vulnhub.com/entry/dr4g0n-b4ll-1,646/ 描述:这篇其实没有什么新奇的技巧,用到的提权方式就 ...

  10. 实验7:基于REST API的SDN北向应用实践

    一.实验目的 1.能够编写程序调用OpenDaylight REST API实现特定网络功能: 2.能够编写程序调用Ryu REST API实现特定网络功能. 二.实验环境 下载虚拟机软件Oracle ...