I'm creating test framework and using RestTemplate class for HTTP request creation.
In general cases I use next code:
Response response = null;
ResponseEntity<String> responseEntity = null;
try{
responseEntity = getRest().exchange(url, httpMethod, httpEntity, String.class);
response = new Response(
responseEntity.getStatusCodeValue(),
responseEntity.getStatusCode().getReasonPhrase(),
responseEntity.getBody(),
responseEntity.getHeaders()
);
} catch (HttpStatusCodeException e){
response = new Response(
e.getRawStatusCode(),
e.getStatusText(),
e.getResponseBodyAsString(),
e.getResponseHeaders()
);
}
It works perfectly for all cases except DELETE HTTP method which receives 204 status code and empty body as a response.
Now, I have to reinitialize RestTemplate to fix it. But I hope that another way should exist.
Could you help me with this?
Please check if you use HttpMethod.Delete for httpMethod and also try when you use delete to put as last parameter of exchange not String.class but Void.class.
I hope it help you.
Related
I need to send get request to example.com/api with query param named test[]
For this i use spring rest tepmlate
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(example.com/api)
.queryParam(test[], "test");
responseEntity = restTemplate.exchange(builder.toUriString(), HttpMethod.GET,
new HttpEntity<>(this.setHttpHeader()),
new ParameterizedTypeReference<List<MyDTO>>() {
});
But builder.toUriString() return example.com/api?test%5B%5D=test
I try to replace srting with my method
private String repairUri(String uri) {
return url.replaceAll("%5B%5D", "[]");
}
and call
responseEntity = restTemplate.exchange(repairUri(builder.toUriString()), HttpMethod.GET,
new HttpEntity<>(this.setHttpHeader()),
new ParameterizedTypeReference<List<MyDTO>>() {
});
But into restTemplate.exchange() this uri convert to example.com/api?test%5B%5D=test again.
Meanwhile i easy send example.com/api?test[]=test request by POSTMan and it's work.
How Can i send request to example.com/api?test[]=test in Spring?
I find one solution.
In my restTemplate bean definition I add this settings:
public RestTemplate myRestTemplate() {
RestTemplate restTemplate = restTemplate(timeout);
DefaultUriBuilderFactory builderFactory = new DefaultUriBuilderFactory();
builderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
restTemplate.setUriTemplateHandler(builderFactory);
restTemplate.setErrorHandler(new RestClientResponseExceptionHandler());
return restTemplate;
}
In this page some guys says that DefaultUriBuilderFactory.EncodingMode.NONE is also suitable.
Read more in link.
Just change your repairUri method to this when you call restTemplate.exchange to this :
responseEntity = restTemplate.exchange(URLDecoder.decode(builder.toUriString(), "UTF-8"), HttpMethod.GET,
new HttpEntity<>(this.setHttpHeader()),
new ParameterizedTypeReference<List<MyDTO>>() {
});
I'm looking for a way how to forward POST request which has been made to endpoint in #RestController class and forward it to external URL with body and headers untouched (and return response from this API of course), is it possible to do it by using some spring features? The only solution which I have found is extracting a body from #RequestBody and headers from HttpServletRequest and use RestTemplate to perform a request. Is there any easier way?
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.set(headerName, request.getHeader(headerName));
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
The above code is taken from this answer.
This is more a matter of the HTTP spec than Spring where the server would be expected to return a 307 redirect status, indicating the client should follow the redirect using the same method and post data.
This is generally avoided in the wild as there's a lot of potential for misuse, and friction if you align with the W3.org spec that states the client should be prompted before re-executing the request at the new location.
One alternative is to have your Spring endpoint act as a proxy instead, making the POST call to the target location instead of issuing any form of redirect.
307 Temporary Redirect (since HTTP/1.1) In this occasion, the request should be repeated with another URI, but future requests can still use the original URI.2 In contrast to 303, the request method should not be changed when reissuing the original request. For instance, a POST request must be repeated using another POST request.
I want to perform a put request in Java Spring using rest template. I know how to do a regular request where the value looks like a traditional JSON:
{
"key":"value"
}
however i want to send the data as raw value:
foobar
At least in Postman that is what the data looks like in the raw option
How can this be emulated in Spring?
EDIT: Additional Info
Here is the code that I am currently using
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response =
restTemplate.exchange(url, HttpMethod.PUT, createHttpEntity(), String.class);
createHttpEntity() adds headers with appropriate authorization etc
The URL consumes a PUT request and accepts a singular link like so:
https://foobar.com
public HttpEntity createHttpEntity()
{
HttpHeaders headers = new HttpHeaders();
headers.set(Constants.AUTHORIZATION, Constants.BEARER + Base64.getEncoder().encodeToString(token.getBytes()));
headers.set(Constants.APP_ID_NAME, Constants.APP_ID);
headers.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity( headers);
}
You need to set the Content-Type:text/plain in order to send plain text as a raw value in a request.
Original (wrong) answer:
Return a String (or whatever type of value you want to return) and annotate your method with #ResponseBody
Use this constructor for your HttpEntity instead.
HttpHeaders headers = new HttpHeaders();
// ...
return new HttpEntity<>("MY REQUEST BODY", headers);
I am trying to consume the following HTTPS endpoints from Yahoo Weather Service:
Yahoo Weather Service API
I am doing some special query according to the API to get the current weather at some parametrized location.
#Service("weatherConditionService")
public class WeatherConditionServiceImpl implements WeatherConditionService {
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public WeatherCondition getCurrentWeatherConditionsFor(Location location) {
RestTemplate restTemplate = new RestTemplate();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(URL);
stringBuilder.append("?q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22");
// TODO: Validate YQL query injection
stringBuilder.append(location.getName());
stringBuilder.append("%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
WeatherQuery weatherQuery = restTemplate.getForObject(stringBuilder.toString(), WeatherQuery.class);
// TODO: Test Json mapping response
Condition condition = weatherQuery.getQuery().getResults().getChannel().getItem().getCondition();
return new WeatherCondition(condition.getDate(), Integer.parseInt(condition.getTemp()), condition.getText());
}
Location is a class that provides the attribute "name" that is a String description of the location, such as "New York" or "Manila".
Condition an other classes just map the returning object.
When executing I get the following HTTP response:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
So this means I am not authorized to access the resource from what I understand.
The URL works great if I just copy & paste it in a web browser:
Yahoo Weather Query
I think that mapping is not a problem since I am not getting "400" (Bad Request) but "403" (Forbidden)
There must be some error on the way I use the RestTemplate object. I am researching but I can't find an answer.
The docs say you need an api key. But when I make a call like this:
fetch('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22nome%2C%20ak%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys')
.then(resp=> resp.json())
.then((res)=>console.log(res.query.results))
https://repl.it/NeoM
It works fine without one. Perhaps you've been blackisted for hitting the api too often.
Your code seems fine.
I finally found the answer. It finally WAS a Bad Request because I needed to pass the parameters differently (not as part of the URL).
I found the answer here. Here goes the code for my particular Yahoo Weather API call return a String (I still will have to do some work to use the mapping).
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public String callYahooWeatherApi() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(URL)
.queryParam("q", "select wind from weather.forecast where woeid=2460286")
.queryParam("format", "json");
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.GET,
entity,
String.class);
return response.getBody();
}
I need to implement robospice for doing the networking part in my Translator app. I previously used async task class and it was working fine, but now i want to improve my application with implementing robospice. I'am trying to execute the following code but it doesn't't throw any exception it just never executes....
#Override
public TranslatedText loadDataFromNetwork() throws Exception {
String jsonString = getJsonString();
String headerValue = getHeaderValue(jsonString);
String text = pair.getWordPairs().getWordFrom();
String languageFrom = pair.getLanguagePairs().getLanguageFrom().getCode();
String languageTo = pair.getLanguagePairs().getLangougateTo().getCode();
String uri = String
.format("http://api.microsofttranslator.com/v2/Http.svc/Translate?text=%s&from=%s&to=%s&contentType=text/html",
URLEncoder.encode(text, "UTF-8"),
URLEncoder.encode(languageFrom, "UTF-8"),
URLEncoder.encode(languageTo, "UTF-8"));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", headerValue);
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the Simple XML message converter
getRestTemplate().getMessageConverters().add(new SimpleXmlHttpMessageConverter());
//set the headerValue in the Entity
org.springframework.http.HttpEntity<?> request = new org.springframework.http.HttpEntity<Object>(headerValue);
// Make the HTTP GET request, marshaling the response from XML to an
// EventList
Log.v("request","Making request!");
//This line never finish execuitng, doesen't throw exception or anything in logCat
ResponseEntity<Object> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, null);
Log.v("request", responseEntity.getBody().toString());
Log.d("Load Data From Network", request.getBody().toString());
return null;
}
The last thing it shows in log cat is Request First!! And nothing after that. It never even gets to The Request Listener onRequestFailure.
Can any 1 tell me what i do wrong ?
This is what look weird to me in your code:
ResponseEntity<Object> and null as 4th parameter of the exchange method are not correct. You need to provide a type which represents the response you get from the server.
The object returned by loadDataFromNetwork() is what you will get in the onRequestSuccess() method. Returning null is not a good idea, in my opinion.
I fixed the problem. So if you need to handle stream you will have to provide the following code
ResponseEntity<byte[]> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, byte[]);