使用 HttpComponentsClientHttpRequestFactory 在 RestTemplateBuilder 中添加自定义 ClientHttpRequestInterceptor

Add Custom ClientHttpRequestInterceptor in RestTemplateBuilder with HttpComponentsClientHttpRequestFactory

我需要在所有 RestTemplate 客户端请求中添加一个 自定义 Header。所以我实现了ClientHttpRequestInterceptor。然后我在 RestTemplateBuilder 配置中添加拦截器,如下所示。问题是,当 RestTemplate 进行 HTTP 调用时,它抛出以下异常:

java.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory

RestTemplate Bean 创建:

@Bean
  public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    ClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
    restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
    return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
  }

另外,我稍后会在下面的代码中更新超时:

  protected void setRestTemplateTimeouts() {

    HttpComponentsClientHttpRequestFactory rf =
        (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
    rf.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    rf.setReadTimeout(restTemplateProps.getSocketTimeout());
  }

谁能帮我解决这个问题?

问题是,我在设置 ClientHttpRequestInterceptor.

后尝试设置 连接和读取超时

在我的 setRestTemplateTimeouts() 方法中,当我尝试获取 requestFactory 并将其类型转换为 HttpComponentsClientHttpRequestFactory 时,我得到了 ClassCastException 异常,因为 restTemplate.getRequestFactory() returns InterceptingClientHttpRequestFactory 而不是 HttpComponentsClientHttpRequestFactory。这是因为我在 restTemplate 对象中添加了一个 interceptor

解决方法是在设置之前设置超时拦截器,因为设置拦截器后不能设置超时。参考以下代码:

@Bean
  public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
    clientHttpRequestFactory.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    clientHttpRequestFactory.setReadTimeout(restTemplateProps.getSocketTimeout());
    restTemplateBuilder = restTemplateBuilder.additionalInterceptors(new MyClientHttpRequestInterceptor());
    return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
  }

这就是我设法让拦截器记录请求和响应而不抛出异常的方法 - 尝试从关闭的流中读取。

 @Bean
 public RestTemplate getRestTemplateConfig()
            throws KeyStoreException, IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyManagementException {


        SSLContext context = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("/opt/cert/keystore.jks"), 
                        "password".toCharArray(),
                        "password".toCharArray())
                .build();

        HttpClient client = HttpClients
                .custom()
                .setSSLContext(context)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(client);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

       //Provide a buffer for the outgoing/incoming stream, allowing the response body to be read multiple times
        // (if not configured, the interceptor reads the Response stream, and then returns body=null when responding to the data)
        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
        restTemplate.setInterceptors(Collections.<ClientHttpRequestInterceptor>singletonList(
                new RestTemplateInterceptor()));
        restTemplate.getMessageConverters().add(jacksonSupportsMoreTypes());
        return restTemplate;
    }

    private HttpMessageConverter jacksonSupportsMoreTypes() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList( MediaType.APPLICATION_OCTET_STREAM));
        return converter;
    }