Spring Cloud Gateway过滤器精确控制异常返回(分析篇)

  • 再看上述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 getRoutingFunction(ErrorAttributes errorAttributes) {

return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);

}

  • 打开renderErrorResponse方法,如下所示,真相大白了!

protected Mono 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 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 responseStatusAnnotation) {

// 异常对象是不是BindingResult类型
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
**

  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-t0qYQGwo-1713298978705)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-QtSPBL5G-1713298978705)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值