fps
fps

Reputation: 34450

Adding OkHttp custom interceptor to Feign client

I'm having problems to set up a global OkHttp interceptor for my @FeignClient beans. I'm not experiencing any error, but the interceptor is being ignored.

My understanding is that Spring Cloud's auto configuration should pick the OkHttpClient.Builder bean that I'm declaring and use it to create the underlying OkHttpClient instances, but I might be wrong about this.

Here are the relevant parts of my Spring app:

@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;
    }

}

The intercept method above is never called. I need MyInterceptor to be a Spring bean, because I need to inject other dependencies to it.


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

    // method declarations
}

@Component
public class MyClientFallback implements MyClient {

    // method fallback implementations
}

Here's the relevant part of my application.properties file:

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

As you see from the properties declared above, I'm not using Eureka and I'm using Ribbon to load balance my rest client. I'm also using Hystrix to enable fallback responses and I have set the feign.okhttp.enabled property to true.


Below is the info about dependecies config and versions...

Spring Boot version is 2.0.3.RELEASE and Spring Cloud version is Finchley.SR1, while OkHttp version is 3.11.0.

In my pom.xml file, I have this spring-cloud-dependencies config:

<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>

I have also included the following Spring Boot and Spring Cloud dependencies, along with the OkHttp dependency:

<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>

Upvotes: 3

Views: 22272

Answers (3)

Zon
Zon

Reputation: 19880

Solution is to use OkHttpClient. Add pom.xml dependencies:

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

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

and configure a bean:

@Configuration
public class FeignConfiguration {

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

Explanation: For 401, 407 and some other HTTP-status responses, bodies are replaced with null by HTTP clients used in Open Feign by default.

From OpenFeign: Currently in the feign.Default client there is a streaming mode enabled. You can see in the sun.net.www.protocol.http.HttpURLConnection following lines of code :

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

So if the streaming is enabled and you have 401 HTTP response code you will get empty errorStream, because there is no initialization. The feign client will try to get the errorStream as a body because there is a check

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

Upvotes: 0

fps
fps

Reputation: 34450

The solution is to let Spring auto configuration do its job.

In order for that to happen, the following dependency must be removed from the pom.xml file:

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

And the following one must be manually included:

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

Once this is done, everything works as expected with the provided configuration.

Upvotes: 3

Mạnh Quyết Nguyễn
Mạnh Quyết Nguyễn

Reputation: 18235

You should provide an OkHttpClient bean as 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

Upvotes: 4

Related Questions