欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

gateWay全局异常配置-----代码+分析思路

程序员文章站 2022-03-26 20:32:40
gateWay全局异常配置每天多学一点点~话不多说,这就开始吧…文章目录gateWay全局异常配置1.前言2.思考思路3.代码实现4.测试5.结语1.前言最近一周在弄spring-cloud的基础架构,试着在网关这一层做统一的异常处理,返回统一信息,这样对前端比较友好。一开始无头绪,试着慢慢分析,这里做一下总结。2.思考思路说到底网关也是个单体的springboot项目,springboot是自动装配,在没有引入网关之前,其处理统一异常的类在org.springframework.boot....

gateWay全局异常配置-----代码+分析思路

每天多学一点点~
话不多说,这就开始吧…

1.前言

最近一周在弄spring-cloud的基础架构,试着在网关这一层做统一的异常处理,返回统一信息,这样对前端比较友好。一开始无头绪,试着慢慢分析,这里做一下总结。

2.思考思路

说到底网关也是个单体的springboot项目,springboot是自动装配,在没有引入网关之前,其处理统一异常的类在
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
gateWay全局异常配置-----代码+分析思路
而 网关 底层 是 webflux 的响应式编程,于是,找到了 reactive是包
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration(接触spring比较多的童鞋,一般都会直接看AutoConfigurationxxxx),

webflux的错误web自动装配,点进去这个类,发现如下方法 errorWebExceptionHandler,

	@Bean
	@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
	@Order(-1)
	public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
	// 用的是DefaultErrorWebExceptionHandler,重写后注入我们自定义的ErrorWebExceptionHandler
		DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
				this.resourceProperties, this.serverProperties.getError(), this.applicationContext);
		exceptionHandler.setViewResolvers(this.viewResolvers);
		exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
		exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
		return exceptionHandler;
	}

打个断点,模拟一下异常,发现 果真 断点进来了,最后一步步调试,发现其调用了
org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler#renderErrorResponse方法。

于是乎便有了思路,如下:

  1. 复制ErrorWebFluxAutoConfiguration 类,名称为 ExceptionAutoConfiguration,重写其errorWebExceptionHandler方法 ,然后定义自己的CustomErrorWebExceptionHandler
  2. 写一个自己的CustomErrorWebExceptionHandler类, 继承 DefaultErrorWebExceptionHandlerrenderErrorResponse()方法和getRoutingFunction() 。(原本DefaultErrorWebExceptionHandler中这两个方法都是返回错误页面的,我们这里只需要重新塞入自定义的handler就行)
  3. 自定义GateWayExceptionHandlerAdvice,返回统一json格式

3.代码实现

GateWayExceptionHandlerAdvice 类

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2020/7/21 16:14
 * @description: TODO  自定义网关异常
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Slf4j
@Component
public class GateWayExceptionHandlerAdvice {

    /**
     * 统一 异常
     *
     * @param throwable
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    public CommonResult handle(Throwable throwable) {

        if (throwable instanceof SignatureException) {
            return signHandle((SignatureException) throwable);
        } else if (throwable instanceof NotFoundException) {
            return notFoundHandle((NotFoundException) throwable);
        } else if (throwable instanceof ResponseStatusException) {
            return handle((ResponseStatusException) throwable);
        } else if (throwable instanceof GateWayException) {
            return badGatewayHandle((GateWayException) throwable);
        } else if (throwable instanceof ConnectTimeoutException) {
            return timeoutHandle((ConnectTimeoutException) throwable);
        } else {
            return CommonResult.failed();
        }
    }

    /**
     * 401 校验 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {SignatureException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public CommonResult signHandle(SignatureException ex) {
        log.error("SignatureException:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.UNAUTHORIZED);
    }

    /**
     * 404 服务未找到 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {NotFoundException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public CommonResult notFoundHandle(NotFoundException ex) {
        log.error("not found exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.NOT_FOUND);
    }

    /**
     * 500   其他服务 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {ResponseStatusException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResult handle(ResponseStatusException ex) {
        log.error("ResponseStatusException:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.UNAUTHORIZED);
    }

    /**
     * 502 错误网关 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {GateWayException.class})
    @ResponseStatus(HttpStatus.BAD_GATEWAY)
    public CommonResult badGatewayHandle(GateWayException ex) {
        log.error("badGateway exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.BAD_GATEWAY);
    }


    /**
     * 504 网关超时 异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = {ConnectTimeoutException.class})
    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
    public CommonResult timeoutHandle(ConnectTimeoutException ex) {
        log.error("connect timeout exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.GATEWAY_CONNECT_TIME_OUT);
    }
}

CustomErrorWebExceptionHandler 类

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2020/7/21 16:14
 * @description: TODO  重写webflux 的 DefaultErrorWebExceptionHandler
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Slf4j
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    @Autowired
    private GateWayExceptionHandlerAdvice gateWayExceptionHandlerAdvice;


    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
                                          ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }


    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    /**
     * 重写 异常方法 塞入自己的 gateWayExceptionHandlerAdvice
     *
     * @param request
     * @return
     */
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
        Throwable throwable = getError(request);
        return ServerResponse.status(super.getHttpStatus(error))
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(gateWayExceptionHandlerAdvice.handle(throwable)));

    }
}

ExceptionAutoConfiguration 类

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2020/7/21 16:14
 * @description: TODO   观察 网关 ErrorWebFluxAutoConfiguration 的配置 重写
 *                      把该该配置类copy下来,然后定义errorWebExceptionHandler的逻辑
 *                      写一个自定义的异常处理器
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ExceptionAutoConfiguration {

    private ServerProperties serverProperties;

    private ApplicationContext applicationContext;

    private ResourceProperties resourceProperties;

    private List<ViewResolver> viewResolvers;

    private ServerCodecConfigurer serverCodecConfigurer;

    public ExceptionAutoConfiguration(ServerProperties serverProperties,
                                      ResourceProperties resourceProperties,
                                      ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                      ServerCodecConfigurer serverCodecConfigurer,
                                      ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider
                .getIfAvailable(() -> Collections.emptyList());
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)  // 要比 ErrorWebFluxAutoConfiguration 小,表示其优先调用
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        // 注入自己的 CustomErrorWebExceptionHandler 处理类
        DefaultErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
                errorAttributes, this.resourceProperties,
                this.serverProperties.getError(), this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

4.测试

服务 端口
boke-gateway 8888
boke-api(业务工程) 8081

若网关工程这里出现异常,比如(下游工程未开启, System.out.println(1/0); 等等)会返回自定义json格式,出现了自定义异常。
gateWay全局异常配置-----代码+分析思路

但是,下游服务出现异常,依然会出现网关原生的格式,比如

boke-api 工程

@RequestMapping("/api")
@RestController
public class BokeApiController extends BaseController {
    @PostMapping("/getLog")
    public CommonResult getLog(@RequestBody ApiReq req) {
        System.out.println(1/0);
        return CommonResult.success(null, "调用成功");
    }
 }

gateWay全局异常配置-----代码+分析思路

所以,在下游服务 boke-api 工程 中加上 @RestControllerAdvice

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author :zjq
 * @date :2020/7/21 01:39
 * @description: TODO   接口层 是否需要通用接口 后续看
 * @version: V1.0
 * @slogan: 天下风云出我辈,一入代码岁月催
 */
@RestControllerAdvice
public class RequestBadExceptionHandler {

    /**
     * 兜底异常捕捉
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public CommonResult<String> ExepitonHandler(Exception e) {
        if (e instanceof NoHandlerFoundException) {
            return CommonResult.failed(ResultCode.NOT_FOUND);
        } else if (e instanceof BusinessException) {
            return CommonResult.failed("api接口错误: " + e.getMessage());
        }
        return CommonResult.failed();
    }
    
}

再次进行测试,出现自定义异常。即,每个下游微服务,依然要配置springboot的全局异常

gateWay全局异常配置-----代码+分析思路

5.结语

世上无难事,只怕有心人,每天积累一点点,fighting!!!

本文地址:https://blog.csdn.net/weixin_42437633/article/details/107595805