Spring Cloud之OpenFeign异常处理

简易原理图

原理基于请求头传递错误消息,利用aop和全局异常拦截机制实现。

在这里插入图片描述

服务提供者

  1. 远程调用本地方法b,throw异常出来
  2. FeignExceptionAspect AOP拦截处理异常到请求头中,继续throw
  3. GlobalExceptionHandler处理,返回响应ResponseVo

服务消费者

  1. controller方法中的远程调用完毕(异常),被FeignExceptionClient(自定义feign调用处理器)检测到请求头中错误码和错误信息,转化为本地BusinessException throw出来
  2. FeignExceptionAspect AOP拦截处理异常到请求头中,继续throw
  3. GlobalExceptionHandler处理,返回响应ResponseVo

上述异常处理机制与使用Decoder处理的异同优劣

  1. 上述异常处理机制代码简洁程度无法跟解码器处理方式比较,但是处理的细致,可以处理更多自定义的异常,比如:UserLoseException
  2. 上述处理机制不受限于原生feign的机制,解码器在处理void的时候会出现无法生效情况,并且通过寻找其他博文在低版本中可以通过反射修改强制属性执行解码器的方式得到解决,3.0.5版本不支持这种操作。
    博文链接如下:https://blog.csdn.net/m0_37298252/article/details/133690069
  3. ErrorDecoder生效处理http status 非200的

基于AOP和自定义feign执行器代码实现

/**
 * 全局异常处理器
 */
@RestControllerAdvice
@Configuration
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public Object defaultErrorHandler(Exception e) throws Exception {
        log.error("系统异常:{}", e);
        return ResultUtils.error("系统异常", String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()));
    }


    /**
     * 处理运行时异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = RuntimeException.class)
    public Object runtimeException(RuntimeException ex) {
        log.error("系统异常:{}", ex);
        return ResultUtils.error("系统异常!", String.valueOf(HttpStatus.EXPECTATION_FAILED.value()));
    }

    @ExceptionHandler(value = DecodeException.class)
    public Object decodeException(DecodeException ex) {
        log.error("系统异常:{}", ex);
        return ResultUtils.error(ex.getMessage(), String.valueOf(ex.status()));
}

    /**
     * 处理自定义业务异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    public Object exceptionHandler(BusinessException ex) {
        log.error("业务异常详情:{}", ex);
        return ResultUtils.error(ex.getMessage(), ex.getCode());
    }


    @ExceptionHandler(value = UserContextLoseException.class)
    public Object notLoginException(UserContextLoseException e) {
        log.error("当前用户信息不存在异常:{}", e);
        return ResultUtils.notLogin();
    }

    /**
     * 方法入参校验异常处理 content-type!=application/json
     *
     * @param ex 异常
     * @return Object
     */
    @ExceptionHandler({BindException.class})
    public Object bindExceptionException(BindException ex) {
        List<String> validResult = ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
        log.warn("参数非法:{}", ex);
        return ResultUtils.error(validResult.get(0));
    }

    /**
     * 方法入参校验异常处理 content-type=application/json
     *
     * @param ex 异常
     * @return Object
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        List<String> validResult = ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
        log.warn("参数非法:{}", ex);
        return ResultUtils.error(validResult.get(0));
    }

    /**
     * 请求类型不支持
     *
     * @param httpRequestMethodNotSupportedException
     * @return
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Object handleNotSupportedHttpMethodException(HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException) {
        log.error("HttpRequestMethodNotSupportedException:{}", httpRequestMethodNotSupportedException);
        return ResultUtils.error(httpRequestMethodNotSupportedException.getMessage(), String.valueOf(HttpStatus.EXPECTATION_FAILED.value()));
    }

    /**
     * media type not support
     *
     * @param httpMediaTypeNotSupportedException
     * @return
     */
    @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Object handleNotSupportedHttpMethodException(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
        log.error("HttpMediaTypeNotSupportedException:{}", httpMediaTypeNotSupportedException);
        return ResultUtils.error(httpMediaTypeNotSupportedException.getMessage(), String.valueOf(HttpStatus.EXPECTATION_FAILED.value()));
    }
}
@Slf4j
@Aspect
@Order(value = 100)
@Component
public class FeignExceptionAspect {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PutMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            Object proceed = joinPoint.proceed();
            return proceed;
        } catch (BusinessException e) {
            log.error("feign调用异常:{}", e.getMessage());
            if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode());
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8"));
            }
            throw e;
        } catch (UserContextLoseException e) {
            log.error("用户信息缺失:{}", e);
            if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode());
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8"));
            }
            throw e;
        } catch (FeignException e) {
            // 存在未发起远程调用前抛出的FeignException异常
            if (e instanceof FeignException.ServiceUnavailable) {
                FeignException.ServiceUnavailable serviceUnavailable = (FeignException.ServiceUnavailable) e;
                log.error(serviceUnavailable.getMessage());
                throw BusinessException.createException("服务不可用");
            }

            throw e;
        } catch (Throwable throwable) {
            Throwable cause = throwable.getCause();
            while (null != cause && null != cause.getCause()) {
                cause = cause.getCause();
            }

            if (cause instanceof BusinessException) {
                if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((BusinessException) cause).getCode());
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8));
                }
                throw (BusinessException) cause;
            } else if (cause instanceof UserContextLoseException) {
                if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((UserContextLoseException) cause).getCode());
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8));
                }
                throw (UserContextLoseException) cause;
            }
            log.error("接口调用异常:{}", throwable);
            throw new RuntimeException("未知异常");
        }
    }

}
@Slf4j
public class FeignExceptionClient implements Client {

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        Response response = new ApacheHttpClient().execute(request, options);

        RequestTemplate requestTemplate = response.request().requestTemplate();
        Target<?> target = requestTemplate.feignTarget();

        String serviceName = target.name();
        Class<?> type = target.type();
        String api = type.getName();
        String url = requestTemplate.url();

        Collection<String> errorCodes = response.headers().get(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY);
        Collection<String> errormessage = response.headers().get(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY);
        String errorMessage = null;
        if (null != errormessage && !errormessage.isEmpty()) {
            errorMessage = (String) ((List) errormessage).get(0);
            errorMessage = Base64.decodeStr(errorMessage, CommonConsts.UTF8);
        }

        if (CollectionUtils.isNotEmpty(errorCodes)) {
            logInvokeError(serviceName, api, url);

            Object errorCode = ((List) errorCodes).get(0);
            if (ResultConsts.NOT_LOGIN.toString().equals(errorCode)) {
                throw UserContextLoseException.createException();
            }

            if (String.valueOf(HttpStatus.EXPECTATION_FAILED.value()).equals(errorCode)) {
                throw BusinessException.createException("系统异常");
            }

            if (String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()).equals(errorCode)) {
                throw BusinessException.createException("系统异常");
            }

            if (StringUtils.isNotEmpty(errorMessage)) {
                throw BusinessException.createException(errorMessage);
            }
            throw BusinessException.createException("系统异常");
        }

        if (StringUtils.isNotEmpty(errorMessage)) {
            logInvokeError(serviceName, api, url);
            throw BusinessException.createException(errorMessage);
        }

        return response;
    }

    private void logInvokeError(String serviceName, String api, String method) {
        log.error("调用微服务[{}]-Api[{}]-Uri[{}]异常", serviceName, api, method);
    }
}

基于Decoder处理

public class FeignDecoder implements Decoder {
    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
        String typeName = type.getTypeName();//   class  void
        if (StringUtils.isNotEmpty(typeName)) {
            Response.Body body = response.body();
            String resultString = Util.toString(body.asReader(Util.UTF_8));

            ResponseVO responseVO = JSONObject.parseObject(resultString, ResponseVO.class);
            if (null != responseVO) {
                if (null != responseVO.getCode() && !responseVO.getCode().toString().endsWith(String.valueOf(ResultConsts.SUCCESS_STATUS))) {
                    // 2002000100   417 not-login  100  business
                    throw new DecodeException(responseVO.getCode(), responseVO.getMessage(), response.request());
                }
            }
            Class<?> responseCls;
            try {
                responseCls = Class.forName(typeName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            return JSONObject.parseObject(resultString, responseCls);
        }
        return Util.emptyValueOf(type);
    }
}
public class FeignErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        return new ErrorDecoder.Default().decode(methodKey, response);
    }
}

bean注入

   @Bean
    public Decoder decoder() {
        return new FeignDecoder()::decode;
    }

    @Bean
    public ErrorDecoder errorDecoder() {
        return new FeignErrorDecoder()::decode;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/299367.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1868_C语言单向链表的实现

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/c_basic: little bits of c. 1868_C语言中简单的链表实现 简单整理一下链表的实现&#xff0c;这一次结合前面看到的一些代码简单修改做一个小结。 主题由来介绍 以前工作之中链表的使用其实不多&#xff0c;主要是…

C++动态内存分配(动态内存分配函数)栈区

内存拷贝函数&#xff1a; void *memcpy(void *dest,const void *src,size_tn);#include<string.h>功能&#xff1a;从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest内存地址的起始位置 分配内存使用new 例如&#xff1a;salarynew int[num]; 最后需要释放…

三、Qt核心与Qt类库

一、Qt核心&#xff1a;元对象系统 1、Qt核心特点 Qt对标准C进行了扩展&#xff0c;引入了一些新的概念和功能元对象编译器&#xff08;MOC&#xff09;是一个预处理器&#xff0c;先将Qt的特性程序转为标准C程序&#xff0c;再由标准C编译器进行编译Qt为C语言增加的特性在Qt…

Android学习(三):在Android虚拟机中运行项目

Android学习&#xff08;三&#xff09;&#xff1a;在Android虚拟机中运行项目 一、前期准备 在系统环境变量中添加ANDROID_SDK_HOME&#xff0c;把值指定到一个其他目录。就可以把Android AVD(虚拟机)创建在指定目录下。修改环境变量后&#xff0c;如果Android Studio是在运…

Java Swing手搓童年坦克大战游戏(I)

前言 业余偶尔对游戏有些兴趣&#xff0c;不过这样的时代&#xff0c;硬件软件飞速进步&#xff0c;2D游戏画面都无比精美&#xff0c;之前的8bit像素游戏时代早就过去了&#xff0c;不过那时候有许多让人印象深刻的游戏比如魂斗罗、超级玛丽、坦克大战(Battle City)等等。 学…

1866_FreeRTOS的存储管理方案heap_4分析

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/g_FreeRTOS: learning notes about FreeRTOS. 1866_FreeRTOS的存储管理方案heap_4分析 对FreeRTOS的heap_4进行分析拆解&#xff0c;按照文学式编程的方式重新组织成个人笔记。 主题由来介绍 free以及malloc这样的存…

辗转相除法求最大公约数

介绍 辗转相除法&#xff08;又称欧几里德算法&#xff09;是一种求最大公约数的算法。它基于这样一个事实&#xff1a;两个数的最大公约数等于较大数和较小数余数的最大公约数。即两个数相除&#xff0c;再将除数和余数反复相除&#xff0c;当余数为0时&#xff0c;取当前除法…

Python异常捕获和处理语句 try-except-else-finally

目录 try-except-else-finally语句 1. 基本用法 2. 多个异常处理 3. 处理所有其他异常 4. 多个except子句 5. 使用else子句 6. 使用finally子句 7. 使用as关键字 实例 例1 例2 例3 例4 例5 例6 例7 例8 结论 try-except-finally语句 在Python中&#xff0c;try-e…

概率论与数理统计 知识点+课后习题

文章目录 &#x1f496; [学习资源整合](https://www.cnblogs.com/duisheng/p/17872980.html)&#x1f4da; 总复习&#x1f4d9; 选择题&#x1f4d9; 填空题&#x1f4d9; 大题1. 概率2. 概率3. 概率4. P5. 概率6. 概率密度函数 F ( X ) F(X) F(X)7. 分布列求方差 V ( X ) …

【STM32】STM32学习笔记-DMA直接存储器存储(23)

00. 目录 文章目录 00. 目录01. DMA简介02. DMA主要特性03. 存储器映像04. DMA框图05. DMA基本结构06. DMA请求07. 数据宽度与对齐08. 数据转运DMA09. ADC扫描模式DMA10. 附录 01. DMA简介 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101xx、STM32F102xx和STM32F…

Hive11_Rank函数

Rank 1&#xff09;函数说明 RANK() 排序相同时会重复&#xff0c;总数不会变 DENSE_RANK() 排序相同时会重复&#xff0c;总数会减少 ROW_NUMBER() 会根据顺序计算 2&#xff09;数据准备 3&#xff09;需求 计算每门学科成绩排名。 4&#xff09;创建本地 score.txt&…

C语言程序设计——数学运算

基本运算符 运算符说明例子赋值运算符a b;、-、*、/、()基本四则运算a (a c) * d;%取余运算符a b % 2&、^、~、l位运算a ~b l c>>、<<左移和右移a b >> 2 在c语言的数学运算中&#xff0c;所涉及到的符号如图所示&#xff0c;在使用过程中应该了…

编程语言的发展趋势和未来方向

1、编程语言的未来&#xff1f; 随着科技的飞速发展&#xff0c;编程语言在计算机领域中扮演着至关重要的角色。它们是软件开发的核心&#xff0c;为程序员提供了与机器沟通的桥梁。那么&#xff0c;在技术不断进步的未来&#xff0c;编程语言的走向又将如何呢&#xff1f; 方…

宋仕强论道之华强北后山寨手机时代(三十六)

今天继续讲华强北山寨手机&#xff0c;跟手机配套周边产品。华强北&#xff0c;作为中国电子产品的集散地和创新中心&#xff0c;一直以来都是电子产品和数码产品的聚集地。在早期&#xff0c;赛格市场以其走私、翻新的电脑和电脑周边产品而闻名。赛格大厦以前5楼以上都是做电脑…

Hive实战:网址去重

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、基于HDFS数据文件创建Hive外部表4、利用Hive SQL实…

11.3编写Linux串口驱动

编写串口驱动主要步骤 构建并初始化 struct console 对象&#xff0c;若串口无需支持 console 可省略此步骤 //UART驱动的console static struct uart_driver virt_uart_drv; static struct console virt_uart_console {//console 的名称&#xff0c;配合index字段使用&…

uniapp 解决安卓App使用uni.requestPayment实现沙箱环境支付宝支付报错

背景&#xff1a;uniapp与Java实现的安卓端app支付宝支付&#xff0c;本想先在沙箱测试环境测支付&#xff0c;但一直提示“商家订单参数异常&#xff0c;请重新发起付款。”&#xff0c;接着报错信息就是&#xff1a;{ "errMsg": "requestPayment:fail [pa…

Archlinux下自启动rclone mount

路径&#xff1a; /etc/systemd/system/rclonemount.service [Unit] Descriptionrclonemount Requiresnetwork-online.target.wants Afteralist.service[Service] Typesimple ExecStartPre/bin/mkdir -p /media ExecStart/usr/bin/rclone mount \aliyun: /media \--config /ro…

Video-GroundingDino论文解读

文章目录 前言一、摘要二、引言三、贡献四、模型结构1、模型定义与问题数据少问题模型解决问题模型模块 2、模型结构模型结构图Cross-Modality Spatio-Temporal EncoderLanguage-Guided Query SelectionCross-Modality Spatio-Temporal DecoderPrediction Heads 总结 前言 之前…

冬天夺去的清爽,可爱,春天都会还给你

这款外套上身可太时尚好看了 春天日常穿着或者出行游玩 应对早晚温差&#xff0c;兼具时尚和温度两不误 干净率性闲适的洒脱范整件衣服干净不失细节 下摆有橡筋收紧更加保暖了工艺方面也毫不逊色&#xff0c;防水拉链 四合扣、猪鼻扣一应俱全简直就是一件实用与时尚并存的…