Can we use @RestClientTest when the rest template has interceptors using Spring boot 1.5.x?

huangapple go评论71阅读模式
英文:

Can we use @RestClientTest when the rest template has interceptors using Spring boot 1.5.x?

问题

以下是翻译好的内容:

我正在使用 Spring Boot 1.5.xSpring 4.2.x),并创建了一个 RestClientSdk 的 Spring 组件类如下所示

    @Component
    public class RestClientSdkImpl implements RestClientSdk {
    	
    	private RestTemplate restTemplate;
    	
    	@Autowired
    	public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
    		this.restTemplate = restTemplateBuilder.build();
    	}
        ...
        //出于简洁起见,省略了其他方法
    }

我还定义了一个 DefaultRestTemplateCustomizer 的 Spring 组件如下所示

    @Component
    public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
    	
    	private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
    	
    	@Autowired
    	public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
    		this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
    	}
    	
    	@Override
    	public void customize(RestTemplate restTemplate) {
    		restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
    		restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
    	}
    
    }

有了这些我定义了一个测试类如下所示它使用了 @RestClientTest 注解

    @RunWith(SpringRunner.class)
    @RestClientTest(RestClientSdk.class)
    @ActiveProfiles("test")
    /*
     * RestClientTest 仅包括了最基本的配置,包括一个 rest template builder,
     * 所以我们在测试的范围内包含了 rest sdk 的自动配置 
     */ 
    @ImportAutoConfiguration(RestSdkAutoConfiguration.class)
    public class RestClientApplicationBehaviourTest{
    
    	@Autowired
    	private RestClientSdk restClientSdk;
    	
    	@Autowired
    	private MockRestServiceServer mockRestServiceServer;
    	
    	/**
    	 * 一个简单的 HTTP Get 请求,从一个 REST 服务器检索 JSON 文档,
    	 * 并将其作为响应生成一个普通的 Java 对象。
    	 */	
    	@Test
    	public void testPlainHttpGet() throws IOException{
    		//准备
    		RestClientDto<?> simpleGetRequest = simpleHttpGet();
    		mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
    								   .andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"), MediaType.APPLICATION_JSON));
    		//执行
    		Account account = restClientSdk.send(simpleGetRequest, Account.class);
    		//断言
    		mockRestServiceServer.verify();		
    		Assert.assertNotNull(account);
    		Assert.assertNotNull(account.getAccountId());
    		Assert.assertNotNull(account.getFirstName());
    		Assert.assertNotNull(account.getLastName());
    	}
    ...
    //为了简洁起见,省略了其他方法
    }


----------

**问题**

----------


由于 MockRestServiceServer 构建器用 MockClientHttpRequestFactory 覆盖了我的 rest template 中的 BufferingClientHttpRequestFactory我从响应中得到了一个空的响应体这是因为日志拦截器正在读取来自响应的输入流因此流不再有内容可读取BufferingClientHttpRequestFactory 将防止这种情况发生现在我知道从 Spring 5.0.5 开始MockRestServiceServer 构建器中有一个名为 *bufferContent* 的额外选项但我不能选择迁移到 Spring 5.xSpring Boot 2.x),所以我想知道是否有办法在 Spring Boot 1.5.x / Spring 4.2.x 中进行配置

提前谢谢您
Juan
英文:

I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:

@Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
@Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}

I have also defined a DefaultRestTemplateCustomizer spring component as shown here:

@Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
@Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
@Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}

With that, I've defined a test class as shown below that uses the @RestClientTest annotation as shown below.

@RunWith(SpringRunner.class)
@RestClientTest(RestClientSdk.class)
@ActiveProfiles(&quot;test&quot;)
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder, 
* so we include the rest sdk auto config within the scope of the test 
*/ 
@ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
@Autowired
private RestClientSdk restClientSdk;
@Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and 
* produces a plain old java object as a response.
*/	
@Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto&lt;?&gt; simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo(&quot;http://localhost:8080/account/1234567890&quot;))
.andRespond(withSuccess(IOUtils.getInputAsString(&quot;/stubs/account.json&quot;),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();		
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity 
}

PROBLEM


Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.

I thank you in advance!

Juan

答案1

得分: 1

为了解决这个问题,我需要定义一个测试配置,允许我覆盖客户端请求工厂。请参阅下面的代码。这有点巧妙,但我想这里的真正解决方案应该是升级到Spring 5.x / Spring Boot 2.x。

@Configuration
@Profile("test")
public class MockRestServiceServerConfiguration {

    /**
     * Wrap the Mock Rest client factory in the buffered one.  
     * @param restClientSdk The rest client SDK containing the rest template to use.
     * @return The mock rest service server to use.
     */
    @Bean
    public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
        RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
        MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
        
        //need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
        List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
        restTemplate.setInterceptors(null);
        
        //now we wrap the delegate, which should be the mock rest service server request factory 
        BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
        restTemplate.setRequestFactory(bufferingFact);
        restTemplate.setInterceptors(templateInterceptors);
        return server;
    }
}
英文:

In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.

@Configuration
@Profile(&quot;test&quot;)
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.  
* @param restClientSdk The rest client SDK containing the rest template to use.
* @return The mock rest service server to use.
*/
@Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List&lt;ClientHttpRequestInterceptor&gt; templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory 
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}

huangapple
  • 本文由 发表于 2020年4月5日 14:36:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/61038912.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定