【问题标题】:Adding OkHttp custom interceptor to Feign client将 OkHttp 自定义拦截器添加到 Feign 客户端
【发布时间】:2019-01-20 09:00:05
【问题描述】:

我在为我的 @FeignClient bean 设置全局 OkHttp 拦截器时遇到问题。我没有遇到任何错误,但拦截器被忽略了。

我的理解是 Spring Cloud 的自动配置应该选择我声明的 OkHttpClient.Builder bean 并使用它来创建底层的 OkHttpClient 实例,但我可能对此有误。

这是我的 Spring 应用程序的相关部分:

@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignConfig.class)    
@EnableCircuitBreaker
public class MyApp {

    public static void main(String[] args) {
        SpringApplication.run(MyApp.class);
    }
}

@Configuration
public class FeignConfig {

    @Bean
    public MyInterceptor myInterceptor() {
        return new MyInterceptor();
    }

    @Bean
    public OkHttpClient.Builder okHttpClientBuilder(MyInterceptor interceptor) {
        return new OkHttpClient.Builder().addInterceptor(interceptor);
    }
}

public class MyInterceptor implements okhttp3.Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();

        System.out.println("Hey there, this is my request: " + request);

        Response response = chain.proceed(request);

        System.out.println("Hey there, this is my response: " + response);

        return response;
    }

}

上面的intercept 方法永远不会被调用。我需要 MyInterceptor 成为 Spring bean,因为我需要向它注入其他依赖项。


@FeignClient(name = "myClient", fallback = MyClientFallback.class)
public interface MyClient {

    // method declarations
}

@Component
public class MyClientFallback implements MyClient {

    // method fallback implementations
}

这是我的application.properties 文件的相关部分:

feign.hystrix.enabled = true
feign.okhttp.enabled = true

ribbon.eureka.enabled = false
ribbon.eager-load.enabled = true
ribbon.eager-load.clients = myClient

myClient.ribbon.listOfServers = <IP_LIST>
myClient.ribbon.ServerListRefreshInterval = 10000

正如您从上面声明的属性中看到的,我没有使用 Eureka,而是使用 Ribbon 来对我的 rest 客户端进行负载平衡。我还使用 Hystrix 来启用回退响应,并且我已将 feign.okhttp.enabled 属性设置为 true


以下是有关依赖配置和版本的信息...

Spring Boot 版本为2.0.3.RELEASE,Spring Cloud 版本为Finchley.SR1,而OkHttp 版本为3.11.0

在我的pom.xml 文件中,我有这个spring-cloud-dependencies 配置:

<dependencyManagement>
    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        ...

    </dependencies>
</dependencyManagement>

我还包含了以下 Spring Boot 和 Spring Cloud 依赖项,以及 OkHttp 依赖项:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.11.0</version>
    </dependency>

    ...

</dependencies>

【问题讨论】:

  • 如何使用提供的构建器创建 feign 客户端?我认为您的构建器未使用。
  • @MạnhQuyếtNguyễn 我认为 Spring 自动配置应该选择我声明的构建器并使用它来创建底层 OkHttp 客户端。
  • 你的 Feign 客户端是如何创建的?
  • @MạnhQuyếtNguyễn 我要把那部分交给 Spring Cloud
  • 是类 MyInterceptor 编译。我看到缺少回报

标签: java rest spring-cloud spring-cloud-feign feign


【解决方案1】:

您应该提供一个OkHttpClient bean 作为stated in the doc

可以通过将 feign.okhttp.enabled 或 feign.httpclient.enabled 分别设置为 true 并将它们放在类路径中来使用 OkHttpClient 和 ApacheHttpClient feign 客户端。您可以通过在使用 Apache 时提供 ClosableHttpClient 的 bean 或在使用 OK HTTP 时提供 OkHttpClient 的 bean 来自定义使用的 HTTP 客户端。

https://github.com/OpenFeign/feign/blob/master/okhttp/src/main/java/feign/okhttp/OkHttpClient.java

【讨论】:

  • 你用过Feign OkHttpClient吗? (不是普通的 OkHttpClient)。另外,尝试用@Primary注释你的OkHttpClient
  • 您的解决方案是正确的,我为我之前的 cmets 道歉。包装类 feign.okhttp.OkHttpClient 不在类路径中,因此没有触发 Spring 自动配置。一旦我发现我必须包含哪个依赖项,就像我所做的那样,提供一个 okhttp3.OkHttpClient.Builder 类的 bean 就足够了,Spring 会处理所有事情。非常感谢您的帮助。
【解决方案2】:

解决方案是让 Spring 自动配置完成它的工作。

为了实现这一点,必须从 pom.xml 文件中删除以下依赖项:

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.11.0</version>
</dependency>

并且必须手动包含以下一项:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

完成此操作后,所提供的配置一切正常。

【讨论】:

  • 嗨 Federico,我也面临同样的问题。对我来说,对外部服务的请求和响应不是按照相同的代码打印的。
  • @TheNightsWatch 你好!我刚刚包含了缺少的依赖项,一切正常。
  • 好的。而不是 com.squareup.okhttp3okhttp3.11.0 我们需要添加该依赖项?或者我们需要两个 Okhttp 依赖项?
  • @TheNightsWatch 我已经仔细检查了我的应用程序。你只需要 &lt;groupId&gt;io.github.openfeign&lt;/groupId&gt; &lt;artifactId&gt;feign-okhttp&lt;/artifactId&gt; 依赖,&lt;groupId&gt;com.squareup.okhttp3&lt;/groupId&gt; &lt;artifactId&gt;okhttp&lt;/artifactId&gt; 不是必需的。感谢您提出这个问题,我将在上面编辑我的答案。
  • 有什么方法可以检查我们是否成功“启用”okhttp?它没有在日志中显示okhttp mode on 或任何明显的信息
【解决方案3】:

解决方案是使用 OkHttpClient。添加 pom.xml 依赖:

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

并配置一个bean:

@Configuration
public class FeignConfiguration {

  @Bean
  public OkHttpClient client() {
    return new OkHttpClient();
  }
}

说明:对于 401、407 和其他一些 HTTP 状态响应,Open Feign 中使用的 HTTP 客户端默认将正文替换为 null

From OpenFeign:目前在 feign.Default 客户端中启用了流模式。您可以在sun.net.www.protocol.http.HttpURLConnection 中看到以下代码行:

if (respCode == HTTP_UNAUTHORIZED) { 
  if (streaming()) { 
    disconnectInternal(); 
    throw new HttpRetryException (RETRY_MSG2, HTTP_UNAUTHORIZED); 
  }
}

因此,如果启用了流式传输并且您有 401 HTTP 响应 代码,您将得到空的 errorStream,因为没有初始化。 feign客户端会尝试获取errorStream作为body,因为有check

if (status >= 400) { 
  stream = connection.getErrorStream(); 
} else { stream = connection.getInputStream(); }

【讨论】:

    猜你喜欢
    • 2017-03-08
    • 2018-07-19
    • 2019-04-17
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 2018-03-17
    • 2019-09-05
    • 1970-01-01
    相关资源
    最近更新 更多