【问题标题】:spring feign client exception handlingspring feign 客户端异常处理
【发布时间】:2019-07-27 22:59:52
【问题描述】:

我有一些 fiegn 客户端发送请求其他微服务。

@FeignClient(name="userservice")
public interface UserClient {

    @RequestMapping(
            method= RequestMethod.GET,
                      path = "/userlist")
    String getUserByid(@RequestParam(value ="id") String id);

}

现在我正在发送这样的请求

try {
    String responseData = userClient.getUserByid(id);
    return responseData;
    }

catch(FeignException e)
 {
 logger.error("Failed to get user", id);
}

catch (Exception e) 
{
 logger.error("Failed to get user", id);
}

这里的问题是如果发生任何 FeignException 我没有得到任何错误代码。

我需要在其他APIS中发送相应的错误代码发送给调用者

那么如何提取错误码呢?我想提取错误代码并构建一个 responseEntity

我得到了this 代码,但不知道如何在我的函数中使用。

【问题讨论】:

    标签: exception spring-cloud-feign feign


    【解决方案1】:

    不是同一个问题,但这对我的情况有所帮助。 OpenFeign 的 FeignException 不绑定到特定的 HTTP 状态(即不使用 Spring 的 @ResponseStatus 注释),这使得 Spring 在遇到 FeignException 时默认为 500。没关系,因为 FeignException 可能有许多与特定 HTTP 状态无关的原因。

    但是,您可以更改 Spring 处理 FeignExceptions 的方式。只需定义一个处理 FeignException 的 ExceptionHandler

    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(FeignException.class)
        public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
            response.setStatus(e.status());
            return "feignError";
        }
    
    }
    

    此示例使 Spring 返回与您收到的相同的 HTTP 状态

    【讨论】:

    • 你是如何配置的?它不适合我。我需要为配置添加额外的代码来获取这个>
    • 2xx以外的http状态如何返回FeignException的响应体?
    • 这是唯一对我有用的答案,谢谢!
    • 尽管这样可行,但它会促进一种不好的做法,即会将低级实现细节泄漏到堆栈中。 FeignClient 在应用层中用于为域提供一些价值,它与传输层(即控制器、http statutes 等)无关。如果您想采用这种方式,我建议首先将 FeignException 包装到适当的域中,例如UserNotFoundException 然后才使用你在后者上提出的方法。
    【解决方案2】:

    我迟到了,但这是我的 2 美分。我们有相同的用例来处理基于错误代码的异常,我们使用了custom ErrorDecoder

    public class CustomErrorDecoder implements ErrorDecoder {
    
        @Override
        public Exception decode(String methodKey, Response response) {
            String requestUrl = response.request().url();
            Response.Body responseBody = response.body();
            HttpStatus responseStatus = HttpStatus.valueOf(response.status());
    
            if (responseStatus.is5xxServerError()) {
                return new RestApiServerException(requestUrl, responseBody);
            } else if (responseStatus.is4xxClientError()) {
                return new RestApiClientException(requestUrl, responseBody);
            } else {
                return new Exception("Generic exception");
            }
        }
    }
    

    FeignClientConfiguration类中返回上述类的@Bean

    public class MyFeignClientConfiguration {
    
        @Bean
        public ErrorDecoder errorDecoder() {
            return new CustomErrorDecoder();
        }
    }
    

    使用它作为 FeignClient 的配置类。

    @FeignClient(value = "myFeignClient", configuration = MyFeignClientConfiguration.class)
    

    然后您可以使用GlobalExceptionHandler 处理这些异常。

    【讨论】:

    • 您可以删除MyFeignClientConfiguration 上的@Configuration,因为该类是通过configuration = MyFeignClientConfiguration.class 实例化的。
    【解决方案3】:

    您是否尝试在您的 feign 客户端上实现 FallbackFactory ?

    https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#spring-cloud-feign-hystrix-fallback

    在create方法上,在return之前,可以用这个sn -p检索http状态码:

    String httpStatus = cause instanceof FeignException ? Integer.toString(((FeignException) cause).status()) : "";
    

    示例:

    @FeignClient(name="userservice", fallbackFactory = UserClientFallbackFactory.class)
    public interface UserClient {
    
        @RequestMapping(
                method= RequestMethod.GET,
                          path = "/userlist")
        String getUserByid(@RequestParam(value ="id") String id);
    
    }
    
    
    @Component
    static class UserClientFallbackFactory implements FallbackFactory<UserClient> {
        @Override
        public UserClient create(Throwable cause) {
    
         String httpStatus = cause instanceof FeignException ? Integer.toString(((FeignException) cause).status()) : "";
    
         return new UserClient() {
            @Override
            public String getUserByid() {
                logger.error(httpStatus);
                // what you want to answer back (logger, exception catch by a ControllerAdvice, etc)
            }
        };
    }
    

    }

    【讨论】:

    • 异常,女巫已被扔进后备工厂,不会被控制器建议捕获
    • 在一般情况下,控制器中会有多个方法,所以我们是否继续实现每个方法,即使我想做的只是针对少数方法而不是全部处理它
    【解决方案4】:

    就像documentation 中所说的那样,有一个 ErrorDecored 接口

    上面关于 FallbackFactory 的答案是可行的,但属于其他抽象层。

    【讨论】:

    • 那个 URL 只是指向一般的 Feign 客户端文档,不包括异常处理。
    【解决方案5】:

    我也有点晚了,但我想确保您也检查一下其他潜在的解决方案。

    Feign with Spring 在处理异常方面出了名的糟糕,所以我想出了一个自定义解决方案,它可以创建这个健壮的环境来按照你想要的方式定义业务异常。

    它使用注册到 Feign 客户端的自定义 ErrorDecoder,并增加了基于方法或类级别自定义异常处理的可能性。

    查看:Maintainable error handling with Feign clients? Not a dream anymore

    【讨论】:

      猜你喜欢
      • 2015-04-26
      • 2018-03-21
      • 2019-02-18
      • 2015-08-18
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 2019-04-21
      • 2018-04-19
      相关资源
      最近更新 更多