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

本文介绍了在SpringCloudGateway中,由于WebFlux的特性,不能直接使用ControllerAdvice和ExceptionHandler处理全局异常。作者指导读者如何创建自定义异常类CustomizeInfoException,继承并重写DefaultErrorWebExceptionHandler的renderErrorResponse方法,以根据业务需求定制错误响应内容。
摘要由CSDN通过智能技术生成
  • spring-cloud-tutorials文件夹下有多个子工程,本篇的代码是gateway-change-body,如下图红框所示:

在这里插入图片描述

为何不用常规手段

  • 提到全局异常处理,经验丰富的您应该想到了常用的ControllerAdvice和ExceptionHandler注解修饰的全局异常处理类,但是Spring Cloud Gateway是基于WebFlux的,咱们之前处理异常时用到的HttpServletRequest在Spring Cloud Gateway中并不适用,因此,不能用ControllerAdvice和ExceptionHandler的手段来处理全局异常

基本思路

  • 在动手前做好充足的理论分析,写出的代码才能正常工作

  • 打开DefaultErrorWebExceptionHandler.java,找到renderErrorResponse方法,来看看Spring Cloud Gateway原本是如何构造异常返回内容的:

在这里插入图片描述

  • 此刻聪明的您应该想到怎么做了:做个新的类继承DefaultErrorWebExceptionHandler,覆盖其renderErrorResponse方法,新的renderErrorResponse方法中,按照实际业务需要来设置返回内容,没错,这就是咱们的思路,不过还要细化一下,最终具体的步骤如下:
  1. 新增一个异常类CustomizeInfoException.java,该类有三个字段:http返回码、业务返回码、业务描述信息

  2. 在返回异常的代码位置,使用CustomizeInfoException类来抛出异常,按照实际业务场景设置CustomizeInfoException实例的各个字段

  3. 新增MyErrorWebExceptionHandler.java,继承自DefaultErrorWebExceptionHandler,重写了renderErrorResponse方法,这里面检查异常实例是否是CustomizeInfoException类型,如果是,就从其中取出http返回码、业务返回码、业务描述信息等字段,构造返回body的内容,异常实例若不是CustomizeInfoException类型,就保持之前的处理逻辑不变;

  4. 新增configuration类,用于将MyErrorWebExceptionHandler实例注册到spring环境

  • 分析完毕,开始编码吧,为了简单起见,本篇不再新增maven子工程,而是基于前文创建的子工程gateway-change-body,在这里面继续写代码;

编码

  • 新增异常类CustomizeInfoException.java:

package com.bolingcavalry.changebody.exception;

import lombok.Data;

import org.springframework.http.HttpStatus;

@Data

public class CustomizeInfoException extends Exception {

/**

  • http返回码

*/

private HttpStatus httpStatus;

/**

  • body中的code字段(业务返回码)

*/

private String code;

/**

  • body中的message字段(业务返回信息)

*/

private String message;

}

  • 修改RequestBodyRewrite.java的apply方法,这里面是在处理请求body,如果检查到没有user-id字段,就不将请求转发到服务提供方provider-hello,而是返回错误,这里的错误就用CustomizeInfoException类来处理:

@Override

public Publisher apply(ServerWebExchange exchange, String body) {

try {

Map<String, Object> map = objectMapper.readValue(body, Map.class);

// 如果请求参数中不含user-id,就返回异常

if (!map.containsKey(“user-id”)) {

CustomizeInfoException customizeInfoException = new CustomizeInfoException();

// 这里返回406,您可以按照业务需要自行调整

customizeInfoException.setHttpStatus(HttpStatus.NOT_ACCEPTABLE);

// 这里按照业务需要自行设置code

customizeInfoException.setCode(“010020003”);

// 这里按照业务需要自行设置返回的message

customizeInfoException.setMessage(“请确保请求参数中的user-id字段是有效的”);

return Mono.error(customizeInfoException);

}

// 取得id

int userId = (Integer)map.get(“user-id”);

// 得到nanme后写入map

map.put(“user-name”, mockUserName(userId));

return Mono.just(objectMapper.writeValueAsString(map));

} catch (Exception ex) {

log.error(“1. json process fail”, ex);

return Mono.error(new Exception(“1. json process fail”, ex));

}

}

  • 异常处理类MyErrorWebExceptionHandler.java,这里有一处需要**重点关注的是:**下面的代码仅是参考而已,您无需拘泥于CustomizeInfoException有关的逻辑,完全能按照业务需求自由设置返回的状态码和body:

package com.bolingcavalry.changebody.handler;

import com.bolingcavalry.changebody.exception.CustomizeInfoException;

import org.springframework.boot.autoconfigure.web.ErrorProperties;

import org.springframework.boot.autoconfigure.web.WebProperties;

import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;

import org.springframework.boot.web.reactive.error.ErrorAttributes;

import org.springframework.context.ApplicationContext;

import org.springframework.http.MediaType;

import org.springframework.web.reactive.function.BodyInserters;

import org.springframework.web.reactive.function.server.ServerRequest;

import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

import java.util.HashMap;

import java.util.Map;

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties.Resources resources, ErrorProperties errorProperties, ApplicationContext applicationContext) {

super(errorAttributes, resources, errorProperties, applicationContext);

}

@Override

protected Mono renderErrorResponse(ServerRequest request) {

// 返回码

int status;

// 最终是用responseBodyMap来生成响应body的

Map<String, Object> responseBodyMap = new HashMap<>();

// 这里和父类的做法一样,取得DefaultErrorAttributes整理出来的所有异常信息

Map<String, Object> error = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));

// 原始的异常信息可以用getError方法取得

Throwable throwable = getError(request);

// 如果异常类是咱们定制的,就定制

if (throwable instanceof CustomizeInfoException) {

CustomizeInfoException myGatewayException = (CustomizeInfoException) throwable;

// http返回码、body的code字段、body的message字段,这三个信息都从CustomizeInfoException实例中获取

status = myGatewayException.getHttpStatus().value();

responseBodyMap.put(“code”, myGatewayException.getCode());

responseBodyMap.put(“message”, myGatewayException.getMessage());

responseBodyMap.put(“data”, null);

} else {

// 如果不是咱们定制的异常,就维持和父类一样的逻辑

// 返回码

status = getHttpStatus(error);

// body内容

responseBodyMap.putAll(error);

}

return ServerResponse

// http返回码

.status(status)

// 类型和以前一样

.contentType(MediaType.APPLICATION_JSON)

// 响应body的内容

.body(BodyInserters.fromValue(responseBodyMap));

}

}

  • 最后是配置类MyErrorWebFluxAutoConfiguration.java:

package com.bolingcavalry.changebody.config;

总结

虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。

试官根据自身的理解问的问题也不尽相同,没有规律可循。

[外链图片转存中…(img-JGXqD5Ay-1714057426975)]

[外链图片转存中…(img-WgpWv1x1-1714057426976)]

上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料

有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值