RestTemplate添加LogInterceptor后因为被多次执行导致response.getBody报Attempted read from closed stream错误

经过

为RestTemplate添加LoggingRequestInterceptor后 发送请求 报Attempted read from closed stream错误
根据错误日志得知是执行到IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);报错
debug后发现LoggingRequestInterceptor的intercept方法被执行了两次 从而导致报错

RestTempalteConfig浓缩

@Configuration
@Slf4j
public class RestTemplateConfig {
    @Resource
    private RestTemplateBuilder restTemplateBuilder;
    
    @Bean
    public RestTemplate restTemplate() {

        RestTemplate restTemplate = restTemplateBuilder.build();

        restTemplate.setRequestFactory(getRequestFactory(restTemplate));

        restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor()));

        return restTemplate;
    }

    private ClientHttpRequestFactory getRequestFactory(RestTemplate restTemplate) {
        ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();
        return requestFactory instanceof BufferingClientHttpRequestFactory ? requestFactory : new BufferingClientHttpRequestFactory(requestFactory);
    }
}

错误代码

private ClientHttpRequestFactory getRequestFactory(RestTemplate restTemplate) {
 	ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();
    return requestFactory instanceof BufferingClientHttpRequestFactory ? requestFactory : new BufferingClientHttpRequestFactory(requestFactory);
}

修复代码

private ClientHttpRequestFactory getRequestFactory(RestTemplate restTemplate) {
    restTemplate.setInterceptors(Collections.emptyList());
    ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();
    return requestFactory instanceof BufferingClientHttpRequestFactory ? requestFactory : new BufferingClientHttpRequestFactory(requestFactory);
}

问题分析

请求流程

HttpComponentsClientHttpRequest为默认的也有可能是SimpleClientHttpRequestFactory或其它ClientHttpRequestFactory的实现类

@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
	// 是否有下一个插件 责任链
	if (this.iterator.hasNext()) {
		ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
		return nextInterceptor.intercept(request, body, this);
	}else {
		// 没有插件了  delegate为被代理的ClientHttpRequestFactory如HttpComponentsClientHttpRequest
		ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
		return delegate.execute();
	}
}

问题定位

通过debug发现delegate.execute()中的delegate为InterceptingClientHttpRequest类型 而通过RestTemplateBuilder创建的restTemplate为HttpComponentsClientHttpRequest类型 后发现restTemplate.getRequestFactory() 返回的是InterceptingClientHttpRequest类型 显然不是我们想要的 这就是多执行一次的原因 套了一层娃

@Override
public ClientHttpRequestFactory getRequestFactory() {
	List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
	if (!CollectionUtils.isEmpty(interceptors)) {
		ClientHttpRequestFactory factory = this.interceptingRequestFactory;
		if (factory == null) {
			factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
			this.interceptingRequestFactory = factory;
		}
		return factory;
	}
	else {
		return super.getRequestFactory();
	}
}

发现当getInterceptors不为空时会构造interceptingRequestFactory返回
针对这点在修复代码中把Interceptors清空后 在获取 返回真实类型
当然也可以直接创建一个HttpComponentsClientHttpRequest或SimpleClientHttpRequestFactory塞进去