utf-8 encode url parameters for resttemplate - java

I'm using spring RestTemplate.exchange(URI).
The uri was assembled with UriComponentsBuilder.
one of my query params was
query=München
If I take the url generated from the logs, and use it with curl, it works.
When I let rest template run the query, it does not.
Something about the umlaut not getting encoded?
How do I get UriCompenentsBuilder and/or RestTemplate to cope with the umlaut?
I know I can use a string url with RestTemplate that looks like this:
query=M%C3%BCnchen
and that works.

It happens when RestTemplate is initialized with Spring default request factory (SimpleClientHttpRequestFactory) , e.g.:
RestTemplate restTemplate = new RestTemplate();
Replace default request factory with HttpComponentsClientHttpRequestFactory and Apache HttpClient:
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate = new RestTemplate(factory);
Then call restTemplate.exchange() as you would normally.

Related

Spring Boot - How to send a POST request via a url with query parameters and store the response inside a method?

I have a url generated by a GET method which is somewhat of this format:
https://service-name/api?param1=<value1>&param2=<value2>&param3=<value3>.....
I need to hit this url and store the response (which will be of type application/x-www-form-urlencoded) into a variable, which will be used further.
The issue is that it needs to be done inside the method (get the url and pass it to get a response).
How to go about it?
For a spring boot application to consume an external API, you can use RestTemplate.
An example of usage is below. The response you receive is of type String.
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("Header", "header1");
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://foo/api/v3/projects/1/labels")
.queryParam("param1", param1)
.queryParam("param2", param2);
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.toUriString(),
HttpMethod.GET,
entity,
String.class);

RestTemplate seems to keep Authorization header

I'm using Spring 4 RestTemplate to do some server-side API calls.
The RestTemplate instance is a custom one (not Spring Boot default) using Apache HttpClient created as follows:
PoolingHttpClientConnectionManager cm;
...
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
Some of the API calls use HTTP Basic Authentication and thus need to have an Authorization header. I'm adding this on a RequestEntity and then performing an exchange() call on the RestTemplate and this works fine.
RequestEntity<Void> requestEntity;
requestEntity = RequestEntity.get(uri)
.accept(MediaType.valueOf("application/repository.folder+json"))
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.header("Authorization", apiBasicAuthHeader())
.build();
Some other API calls (to the same backend server) should not use HTTP Basic Authentication and instead use a pre-authenticated token supplied as a request parameter.
RequestEntity<Void> requestEntity = RequestEntity.get(uriWithToken)
.accept(APPLICATION_JSON)
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.build();
operations.exchange(requestEntity, ResourceLookupResults.class)
This also works fine by itself.
However, if I do an API call using the Authorization header first and then try to do one with the pre-authenticated token (with the same RestTemplate), it seems that the Authorization header is still sent on the 2nd request. I'd expect the header added to the RequestEntity to be added only for that specific request and not for subsequent requests that don't need it. Why is this and what is the best way to avoid this (like using separate RestTemplate instances)?

How to get the HTTP status of an API in spring boot when we hit it internally using Rest template

I want to get the object as well as the HTTP response of one api into another in Spring code. For that I am using a rest template and I am getting the desired Object from it successfully.
But I want to fetch HTTP response also for the respective api.
What should I do to get this?
RestTemplate restTemplate = new RestTemplate();
Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
System.out.println("quote "+quote);
System.out.println(quote.getType());
log.info(quote.toString());
getForObject method of RestTemplate only gets the result. If you're interest in the status code you should invode exchange which returns a ResponseEntity which has a getStatusCode method.
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Quote> response= restTemplate.exchange ("http://gturnquist-quoters.cfapps.io/api/random", HttpMethod.GET, null, Quote.class);
Quote quote = response..getBody();
System.out.println("status "+response..getStatusCode());
System.out.println("quote "+quote);
System.out.println(quote.getType());

RestTemplate for local file testing

Since I want to use the RestTemplate from Spring I want to use the same class as well for Unit-Testing. The idea would be to download a JSON-File and save it locally for the purpose of testing. Therefore I would like to change the URI from a HTTP to a File address. When it as File-address I get an Excpetion
Exception in thread "main" java.lang.IllegalArgumentException: Object of class [sun.net.www.protocol.file.FileURLConnection] must be an instance of class java.net.HttpURLConnection
urlGETList = "http://api.geonames.org/countryInfoJSON?username=volodiaL";
RestTemplate restTemplate = new RestTemplate();
CountryInfoResponse results = restTemplate.getForObject(urlGETList, CountryInfoResponse.class);
Any ideas how I can use the same classes for Unit-Testing?
I think you could look into Wiremock.
Wiremock allows subbing of requests. The advantage is that you really test the complete stack and your tests make real requests against a server responding with mock responses. These mock response bodies can be files (there are other possibilities as well.)
In your unit test you set up wiremock server like this:
#Rule
public WireMockRule wireMockRule = new WireMockRule(port);
Then you can setup a stub with a file response like this:
public void givenResponse(int statusCode, MediaType contentType, String bodyPath) {
String responseBody;
try (InputStream data = new ClassPathResource(bodyPath).getInputStream()) {
responseBody = copyToString(data, UTF_8);
}
stubFor(any(urlPathEqualTo(getWireMockUri().getPath()))
.willReturn(aResponse()
.withStatus(statusCode)
.withHeader("Content-Type", contentType.toString())
.withHeader("Content-Length", String.valueOf(responseBody.length()))
.withBody(responseBody)
));
}
You could also put the complete stub into a stub file like described here
Afterwards you can check if a certain request has been made:
verify(postRequestedFor(urlEqualTo("/form"))
.withHeader("Content-Type", containing(MULTIPART_FORM_DATA_VALUE)));
You can find out more about verification here
You would use the RestTemplate to make requests. You just need to have host and port configurable so you can use localhost and the wiremock port in your tests.

Basic authentication once time for all

I developped a spring-boot application. When I need to do REST requests (POST, PUT...) I use every time basic authentication like that
HttpEntity<PostJql> httpEntity = new HttpEntity<PostJql>(post, jiraUtils.getHeader());
ResponseEntity<IssueDataWrapper> response = restTemplate.exchange(urlJiraResponse, HttpMethod.POST, httpEntity, IssueDataWrapper.class);
Is there another way to do basic authentication once time in the application and then use RestTemplate or httpPost {Put/Delete...} without basic authentication ?
Best regards
Yes. Configure your RestTemplate with a ClientHttpRequestFactory that does what you need.
You can just set it in the constructor:
restTemplate = new RestTemplate(requestFactory);
... or you can make all of these things beans and let Spring wire it.
You could extend SimpleClientHttpRequestFactory, overriding createRequest():
public class BasicAuthSimpleClientHttpRequestFactory
extends SimpleClientHttpRequestFactory {
#Override
public HttpClientRequest createRequest(URI uri, HttpMethod httpMethod) {
HttpClientRequest request = super.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.add(... your auth header ...);
return request;
}
}
... or you could bring in Apache HttpComponents as a dependency and use an HttpComponentsClientHttpRequestFactory, and configure the underlying Apache HttpClient to do the authentication you need. Apache documents this in detail, but to get you started:
BasicCredentialsProvider credentialsProvider =
new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("user", "password"));
HttpClient client = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
RestTemplate restTemplate = new RestTemplate(
new HttpComponentsClientHttpRequestFactory(client));
In my experience it's worth bringing in Apache HttpComponents if you can -- it is more reliable and configurable than the HttpUrlConnection based HTTP client that RestTemplate otherwise uses by default. When your requirements broaden to things like other authentication methods, timeouts, retry strategies etc., you'll be glad you have an HttpClient to configure.

Categories

Resources