SpringCloud OpenFeign 使用OkHttp,添加响应拦截器

SpringCloud OpenFeign 使用OkHttp,添加响应拦截器

点击加入:

后端技术内卷群,一起学习!

前言

SpringCloud微服务之间的请求一般使用OpenFeign,有时候我们需要在请求或者响应的时候做一些额外的操作。比如请求的时候添加请求头,响应的时候判断token是否过期等等。这时候拦截器就派上用场了!

我们接下来就说一下怎么添加请求和响应拦截器。

一、修改OpenFeign的http客户端

OpenFeign默认的http客户端是javax.net.ssl.HttpsURLConnection,详细信息见feign-core:feign.Client,该http客户端不支持添加拦截器和连接池。所以我们需要添加第三方http客户端,可选的http客户端有httpClientokHttp,对应的依赖如下:

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

OpenFeign默认启用的是HttpClient,但是我使用的是OkHttp,故添加以下配置

feign.httpclient.enabled=false
feign.okhttp.enabled=true

添加以上配置后,OpenFeign的http客户端就自动切换为OkHttp了,详细过程看源码就清楚了,org.springframework.cloud.openfeign.FeignAutoConfigurationorg.springframework.cloud.openfeign.ribbon.OkHttpFeignLoadBalancedConfiguration。在此就不赘述了。

二、添加请求拦截器

OpenFeign官方自带请求拦截器的接口feign.RequestInterceptor,我们只要实现该接口就可以了。然后你就可以做你想做的任何事情了

@Component
public class CustomerFeignRequestInterceptor implements RequestInterceptor {
 
    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            template.header("sessionId", request.getHeader("sessionId"));
        }
    }
}

三、添加响应拦截器

刚开始我以为OpenFeign官方有响应拦截器的接口,但是我发翻了半天源码也没找到,所以就想在OkHttp上打主意。

网上很多文章里的办法都是重新构造OkHttpClient的Bean,

但是通过查看源码org.springframework.cloud.openfeign.FeignAutoConfiguration发现有这个配置:@ConditionalOnMissingBean(okhttp3.OkHttpClient.class),大概意思就是如果没有OkHttpClient这个Bean,就启动这个配置,如果存在OkHttpCLient的Bean,则不启用这个配置。

如果我们自定义OkHttpClient的Bean,将导致该配置不能生效,我们可能就需要添加很多配置,这简直就是费力不讨好嘛,那有没有更好的方法呢??肯定是有的!!

@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty(value 
"feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {

 private okhttp3.OkHttpClient okHttpClient;

 @Bean
 @ConditionalOnMissingBean(ConnectionPool.class)
 public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
               OkHttpClientConnectionPoolFactory connectionPoolFactory
{
  Integer maxTotalConnections = httpClientProperties.getMaxConnections();
  Long timeToLive = httpClientProperties.getTimeToLive();
  TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
  return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
 }

 @Bean
 public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
            ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties)
 
{
  Boolean followRedirects = httpClientProperties.isFollowRedirects();
  Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
  this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
    connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
    followRedirects(followRedirects).
    connectionPool(connectionPool).build();
  return this.okHttpClient;
 }

 @PreDestroy
 public void destroy() {
  if(okHttpClient != null) {
   okHttpClient.dispatcher().executorService().shutdown();
   okHttpClient.connectionPool().evictAll();
  }
 }

 @Bean
 @ConditionalOnMissingBean(Client.class)
 public Client feignClient(okhttp3.OkHttpClient client
{
  return new OkHttpClient(client);
 }
}

我们仔细观察OkHttpFeignConfiguration这个内部类里是怎么配置OkHttpClient的,发现是通过Builer建造者模式创建的对象,OkHttpClient.Builder又是通过OkHttpClientFactory接口来生成的,OkHttpClientFactory接口的默认实现DefaultOkHttpClientFactory的构造参数又是OkHttpClient.Builder

还有很重要的一点,OkHttpClientFactoryFeignAutoConfiguration整个类中并没有实例化,只有在方法参数中引用了,这说明OkHttpClientFactory在其他地方已经实例化了,已经是spring管理的Bean了。接下来我们要找到OkHttpClientFactory是在哪里实例化的。在Idea中打开OkHttpClientFactory这个接口的代码,按住Ctrl加鼠标左键,出现以下内容

SpringCloud OpenFeign 使用OkHttp,添加响应拦截器

一共有7个地方引用了OkHttpClientFactory,但是只有HttpClientConfiguration是我们没有接触过的,其他的上面都有涉及。所以OkHttpClientFactory很有可能就是在这个类里实例化的,打开这个类,果然如此!!

SpringCloud OpenFeign 使用OkHttp,添加响应拦截器

前面说到,OkHttpClient的实例化和OkHttpClient.BuilderOkHttpClientFactory有关,OkHttpClientFactory的实例化又和OkHttpClient.Builder有关。

所以,我们只要自定义OkHttpClient.Builder就能完美解决这个问题!!自定义OkHttpClient.Builder的时候添加响应拦截器就可以了。切记重新构造Response返回,如果直接返回Response,会导致Feign直接走降级。

@Slf4j
@Configuration
public class FeignOkHttpClientConfig {
 
    @Bean
    public OkHttpClient.Builder okHttpClientBuilder() {
        return new OkHttpClient.Builder().addInterceptor(new FeignOkHttpClientResponseInterceptor());
    }
 
    /**
     * okHttp响应拦截器
     */

    public static class FeignOkHttpClientResponseInterceptor implements Interceptor{
 
 
        @Override
        public Response intercept(Chain chain) throws IOException {
 
            Request originalRequest = chain.request();
            Response response = chain.proceed(originalRequest);
          
            MediaType mediaType = response.body().contentType();
            String content = response.body().string();
            
            //解析content,做你想做的事情!!
 
            //生成新的response返回,网络请求的response如果取出之后,直接返回将会抛出异常
            return response.newBuilder()
                    .body(ResponseBody.create(mediaType, content))
                    .build();
        }
    }
 
}

写在最后的话

刚开始我也是直接复制网上的方法,发现不太好用,然后花了一点时间看了下源码,找到了解决方法。所以有时候直接百度也不太好用,还是得靠自己。

来源:blog.csdn.net/WayneLee0809/article/
details/111474951

精彩推荐
最全的java面试题库
开源版的高仿 “ 微信 ”,吊炸天!

与其在网上拼命找题? 不如马上关注我们~

SpringCloud OpenFeign 使用OkHttp,添加响应拦截器


↓点阅读原文,Java面试题库尽情看!

原文始发于微信公众号(Java面试题精选):SpringCloud OpenFeign 使用OkHttp,添加响应拦截器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/43757.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!