为 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(); }
我在为我的 @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(); }