为 Feign 客户端添加 OkHttp 自定义拦截器

Adding OkHttp custom interceptor to Feign client

我在为我的 @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 对我的其余客户端进行负载平衡。我还使用 Hystrix 来启用后备响应,并且我已将 feign.okhttp.enabled 属性 设置为 true.


下面是关于依赖配置和版本的信息...

Spring 启动版本为 2.0.3.RELEASE,Spring 云版本为 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 引导和 Spring 云依赖项,以及 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>

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

The OkHttpClient and ApacheHttpClient feign clients can be used by setting feign.okhttp.enabled or feign.httpclient.enabled to true, respectively, and having them on the classpath. You can customize the HTTP client used by providing a bean of either ClosableHttpClient when using Apache or OkHttpClient whe using OK HTTP.

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

解决方案是让 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>

完成此操作后,一切都会按照提供的配置正常运行。

解决方法是使用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 client 会尝试获取 errorStream 作为 body 因为有一个 check

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