I am trying to add common request parameters to every request using RestTemplate.
For example if my url is http://something.com/countries/US then I want to add common request param ?id=12345. This common request parameter needs to be added on all request. I don't want to add this on each call and want something common.
this post has answer that was marked correct, but I am not sure how you can add request parameters on org.springframework.http.HttpRequest
Any other way I can achieve this ?
To add request parameters to the HttpRequest , you can first use UriComponentsBuilder to build an new URI based on the existing URI but adding the query parameters that you want to add.
Then use HttpRequestWrapper to wrap the existing request but only override its URI with the updated URI.
Code wise it looks like:
public class AddQueryParamterInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
URI uri = UriComponentsBuilder.fromHttpRequest(request)
.queryParam("id", 12345)
.build().toUri();
HttpRequest modifiedRequest = new HttpRequestWrapper(request) {
#Override
public URI getURI() {
return uri;
}
};
return execution.execute(modifiedRequest, body);
}
}
And set this interceptor to the RestTemplate:
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new AddQueryParamterInterceptor());
restTemplate.setInterceptors(interceptors);
Two things are required to add common request parameters to every request using RestTemplate.
Create a prototype bean RestTemplate
#Configuration
public class RestTemplateConfig {
#Bean
#Scope(
value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public RestTemplate restTemplate() {
RestTemplate localRestTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = localRestTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new AddQueryParamterInterceptor());
localRestTemplate.setInterceptors(interceptors);
return localRestTemplate;
}
}
Use the Interceptor code as suggested by #ken-chan to add the request parameters. A new instance of Resttemaple will be created and for each and every request.
You can achieve this by adding interceptor to rest template
Related
I am using spring boot. I have written a ClientInterceptor for a SOAP web service and i would like to log the URL that i am sending my request to, and i would like to do it in the handleResponse method. However i could not find a way to do it. Is it possible? Any help would be great.
public class SoapClientHttpRequestInterceptor implements ClientInterceptor {
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
// I would like to get the URL and log it here.
}
}
The way i create the restTemplate
#Bean
#Qualifier("testRestTemplate")
public RestTemplate testRestTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setOutputStreaming(false);
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(requestFactory);
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setInterceptors(Collections.singletonList(restClientHttpRequestInterceptor));
restTemplate.setErrorHandler(testErrorHandler);
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
You can do this in your handleResponse method:
TransportContext context = TransportContextHolder.getTransportContext();
context.getConnection().getUri().toString()
i hope this helps you
I have one situation where I need to intercept the request and I need to set authorization header into that request.
So I got the solution that I can use interceptors to set that header but when I check AsyncRestTemplate then It doesn't have that property like RestTemplate.
Is there any specific reason to not include that property?
The AsyncRestTemplate extends the InterceptingAsyncHttpAccessor abstract class, which exposes the method setInterceptors. So of course you can set Interceptors, just like you would do with the non async RestTemplate. Note that your interceptor needs to implement the AsyncClientHttpRequestInterceptor instead:
public class AsyncFooBarInterceptor implements AsyncClientHttpRequestInterceptor {
#Override
public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException {
return null; // do your thing instead
}
}
Then use it:
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
asyncRestTemplate.setInterceptors(Collections.singletonList(new AsyncFooBarInterceptor()));
This is my Configuration for Rest Template,
#Bean
#Qualifier("myRestService")
public RestTemplate createRestTemplate(#Value("${connection.timeout}") String maxConn) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotalConn);
connectionManager.setDefaultMaxPerRoute(maxPerChannel);
RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
.setDefaultRequestConfig(config).build();
ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
restTemplate.setMessageConverters(createMessageConverters());
return restTemplate;
}
Am using PoolingHttpClientConnectionManager for managing the connections.
Its being accessed by the following code,
ResponseEntity<String> response = restClient.exchange( url, HttpMethod.GET, entity , String.class );
Do i need to release the connection after the above call or is it taken care by RestTemplate. If we need to take care of releasing connection.
Please can some one explain/show how to release the connection.
You should declare the ClientHttpRequestFactory as a bean. By declaring it as a bean, it becomes managed by the Spring bean factory, which will call the factory's destroy method when the application is closed, or the bean goes out of scope. The destroy method of the ClientHttpRequestFactory will close the underlying ClientConnectionManager's connection pool. You can check the Spring API docs for this.
#Bean
public ClientHttpRequestFactory createRequestFactory(#Value("${connection.timeout}") String maxConn) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotalConn);
connectionManager.setDefaultMaxPerRoute(maxPerChannel);
RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
.setDefaultRequestConfig(config).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
Then you can use this bean to create your RestTemplate:
#Bean
#Qualifier("myRestService")
public RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
restTemplate.setMessageConverters(createMessageConverters());
return restTemplate;
}
The question which you have asked:
Do i need to release the connection after the above call or is it taken care by RestTemplate. If we need to take care of releasing connection.
No, you do not need to close the connection on the response, if you use resttemplate.
From the apache httpclient, you need to consume the complete response (EntityUtils.consume(HttpEntity) and close the response.
This can be verified in the ClientConnectionRelease.java
But RestTemplate does this for you, to verify the same have a look into
RestTemplate.java
Look for method
protected <T> T doExecute(URI url,...) {
try {
ClientHttpRequest request = this.createRequest(url, method);
...
response = request.execute();
...
if(responseExtractor != null) {
var7 = responseExtractor.extractData(response);
return var7;
}
...
...
} finally {
if(response != null) {
response.close();
}
}
}
Where response extractor does the work for you by consuming the response using
responseExtractor.extractData(response);
And after extracting the data completely it is closing response.close() as well.
I think the answer is here: org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#doExecuteRequest
#Override
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
HttpPost postMethod = createHttpPost(config);
setRequestBody(config, postMethod, baos);
try {
HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
validateResponse(config, response);
InputStream responseBody = getResponseBody(config, response);
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
finally {
postMethod.releaseConnection();
}
}
I am trying to call a RESTfull web service resource, this resource is provided by a third party, the resource is exposed with OPTIONS http verb.
To integrate with the service, I should send a request with a specific body, which identities by a provider, but when I did that I got a bad request. After that I trace my code then I recognized that the body of the request is ignored by rest template based on the below code:
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
my question, is there a standard way to override this behavior or I should use another tool?
The code you've pasted is from
SimpleClientHttpRequestFactory.prepareConnection(HttpURLConnection connection, String httpMethod)
I know because I've debugged that code few hours ago.
I had to do a HTTP GET with body using restTemplate. So I've extend SimpleClientHttpRequestFactory, override prepareConnection and create a new RestTemplate using the new factory.
public class SimpleClientHttpRequestWithGetBodyFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
Create a new RestTemplate based on this factory
new RestTemplate(new SimpleClientHttpRequestWithGetBodyFactory());
A test to prove the solution is working using spring boot (#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT))
public class TestRestTemplateTests extends AbstractIntegrationTests {
#Test
public void testMethod() {
RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestWithBodyForGetFactory());
HttpEntity<String> requestEntity = new HttpEntity<>("expected body");
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:18181/test", HttpMethod.GET, requestEntity, String.class);
assertThat(responseEntity.getBody()).isEqualTo(requestEntity.getBody());
}
#Controller("/test")
static class TestController {
#RequestMapping
public #ResponseBody String testMethod(HttpServletRequest request) throws IOException {
return request.getReader().readLine();
}
}
}
My goal is to call web service, which is require authentification (when I opne it's wsdl in my browser, browser asks me login+password).
As a base, I use the sample from this tutorial.
And now I have to add authentification configurations.
Accoding to the documentation something like configuring WebServiceTemplate bean may help.
But with Spring Boot there are no applicationContext.xml or any other configuration xml's in a project.
So, how to configure WebServiceTemplate using Spring Boot, or what else can solve such task?
In Spring Boot you are able to configure your beans with the #Bean annotation. You can use configuration classes for different beans. In those classes you need the #Configuaration annotation.
This tutorial describes the "second part" of the Spring tutorial. The main things of provided tutorial is: (based on the Spring tutorial)
The problem
The SOAP webservice I consume requires basic http authentication, so I
need to add authentication header to the request.
Without authentication
First of all you need to have implemented a request without the
authentication like in the tutorial on the spring.io. Then I will
modify the http request with the authentication header.
Get the http request in custom WebServiceMessageSender
The raw http connection is accessible in the WeatherConfiguration
class. There in the weatherClient you can set the message sender in
the WebServiceTemplate. The message sender has access to the raw http
connection. So now it’s time to extend the
HttpUrlConnectionMessageSender and write custom implementation of it
that will add the authentication header to the request. My custom
sender is as follows:
public class WebServiceMessageSenderWithAuth extends HttpUrlConnectionMessageSender{
#Override
protected void prepareConnection(HttpURLConnection connection)
throws IOException {
BASE64Encoder enc = new sun.misc.BASE64Encoder();
String userpassword = "yourLogin:yourPassword";
String encodedAuthorization = enc.encode( userpassword.getBytes() );
connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
super.prepareConnection(connection);
}
#Bean
public WeatherClient weatherClient(Jaxb2Marshaller marshaller){
WebServiceTemplate template = client.getWebServiceTemplate();
template.setMessageSender(new WebServiceMessageSenderWithAuth());
return client;
}
I faced the same issue and solved by following.
Basic idea was to create CredentialsProvider with basic username and password along with AuthScope.ANY:
#Bean
public WebServiceMessageSender showReqMessageSender(#Value("${ws.username}") String username,
#Value("${ws.passowrd}") String password) throws Exception {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
return new HttpComponentsMessageSender(
HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider)
.addInterceptorFirst(new RemoveSoapHeadersInterceptor()).build());
}
Just for further info, this message sender bean is further used (set using class extedning WebServiceGatewaySupport)
void org.springframework.ws.client.core.support.WebServiceGatewaySupport.setMessageSender(WebServiceMessageSender messageSender)
Another walk-around is to add an interceptor and add the requestHeader within the handleRequest() method from which the HttpUrlConnection can be easily derived from the TransportContextHolder;
here is the code of the interceptor class:
public class SecurityInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
try {
connection.addRequestHeader("Authorization","Basic VVNFUk5BTUU6cGFzc3dvcmQ=");
} catch (IOException e) {
log.error(e.getMessage());
}
return true;
}
//TODO:: other methods and constructor..
}
and of course add the interceptor to the WebTemplate:
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[]{new SecurityInterceptor()};
webServiceTemplate.setInterceptors(interceptors);
webServiceTemplate.marshalSendAndReceive(uriWebService, request)