Make RestTemplate process response body regardless of status - java

I'm using Spring MVC, and I'm trying to experiment with the Facebook API, just for fun.
The problem I'm having currently, is that Facebook's GRAPH Api returns other status codes than 200 when it encounters an OAuthException. However, the body of the response is still a valid json object, and I would like to parse it into my object.
This way, my restTemplate will invoke the errorhandler, when the status code is anything else than HTTP.2xx, and not parse the response to my object.
Is there any way of configuring the RestTemplate so that it should parse the response body regardless of http status?
Thanks!

you could set a customer ResponseErrorHandler
restTemplate.setErrorHandler(customerErrorHandler)
you'll just need to implement the following two methods
boolean hasError(ClientHttpResponse response) throws IOException;
void handleError(ClientHttpResponse response) throws IOException;
in your case hasErrorcould always return false

Related

how to read body using RestTemplate in case of an error?

I'm using RestTemplate to communicate with one REST service, i use
restTemplate.postForEntity(uri, request, MyResponse.class);
} catch (HttpClientErrorException e)
however when there is an error (4xx or 5xx) REST service returns description in JSON in a body, but HttpClientErrorException.getResponseBodyAsString()
returns null. RestTemplate (if i try to retrieve response as String), doesn't return anything as well.
How to retrieve body in case of error?
You can access the response for errors via a ResponseErrorHandler. The ResponseErrorHandler needs to be set for the RestTemplate with the setErrorHandler method.
The ResponseErrorHandler provides the response via the two methods
boolean hasError(ClientHttpResponse response) throws IOException;
void handleError(ClientHttpResponse response) throws IOException;
Problem was recognised, it's described here
https://jira.spring.io/browse/SPR-16781?focusedCommentId=159473&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-159473
and here
https://jira.spring.io/browse/SPR-9367
you need to use different http library, for example Apache HttpClient (not a default one from JDK).
Add to your project:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
and configure RestTemplate with:
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
you can use ResponseErrorHandler if you need, but it's not necessary.
Now response body (even in error situation) is stored in HttpClientErrorException, read it with getResponseBodyAsString() method.

Spring ResponseErrorHandler without parsing objects

I am using SpringBoot with RestTemplate to communicate with another application. However, I am unable to change its API and this external service always Returns 200OK return code.
By Default, we have ResponseErrorHandler that reacts to all 4xx and 5xx response codes but in my case when there is an exception I get 200OK with one JSON field errors.
I have created a custom error handler and bundled it into my rest template by using:
restTemplate.errorHandler(new MyCustomErrorHandler());
I have also overrided hasError() method but inside I have to parse this object to check whether it contains fields with errors...
Is this a good approach for error handling? Should I parse response twice? I seek for the clean solution for such problems but I want to avoid parsing message twice every time I use external service
Error handler will only be invoked if an error state is returned, 200 is not an error state so it is not handled.
You can change this behavior by overriding hasError method ResponseErrorHandler to check for error message or any indication for error.
public class MyCustomErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse)
throws IOException {
//TODO check your criteria for error
}

Jersey client with null put method

I am working on a Jersey service client for one of my services and am having trouble determining the best way to pass a null entity through the client's put. On the service side of things this is my endpoint:
#PUT
#Path("/rule/disable/key/{key}")
#Produces(MediaType.APPLICATION_JSON)
public Response disableRuleByKey(#PathParam("key") String key)
throws Exception {
try {
DAL.getWriter().disableRuleByKey(key);
return Response.ok().build();
} catch (BlahException bla) {
throw de;
}
Basically all the method does in the backend is flip a toggle for other parts of the application to use. I'm not sure if put is the correct call to use here (but this was written by a teammate). I know it doesn't even have a JSON payload.
Anyways, on the client side I have this generic putItem() code for all of my clients to use via extends:
public static <T> boolean putItem(Client client, String uri, T item)
throws InterruptedException,
ExecutionException {
Invocation putConfig = client.target(uri).request()
.buildPut(Entity.entity(item, MediaType.APPLICATION_JSON));
Future<Response> asyncResponse = putConfig.submit();
Response response = asyncResponse.get();
return response.getStatus() == Status.OK.getStatusCode();
}
This PUTs into the database fine with a JSON payload, but since the method above doesn't specifically have a payload I was wondering what the best course of action would be. Would modifying the Invocation's .buildPut() to have null in it be okay since I am not passing in a payload.
I am open to modifying the endpoint too but this is what I currently have and can't figure out the best way to send this value to the backend. Should I just modify the endpoint to consume a JSON object rather than passing the key as a #PathParam?
When replacing the state of a resource with a PUT request, you should send the new representation in the request payload.
Have a look the the RFC 7231, the current reference for semantics and content in HTTP/1.1:
4.3.4. PUT
The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. [...]

Spring Boot how to ignore HttpStatus Exceptions

I'm building an Application using Spring Boot. This application is distributed, which means I have multiple API's that call each others.
One of my underlying services interacts with a database and responds with the requested data. If a request to an unexisting ID is made, I response with a 404 HttpStatus:
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
(Same with 400 error on certain operations, or 204 for deleting an entry etc).
The problem is that I have some other Spring Boot applications that call these API's, throw an org.springframework.web.client.HttpClientErrorException: 404 Not Found Exception when they request, in this example, an unexisting entry. But the 404 status code is intended and should not return this exception (causing my Hystrix circuit breaker to call its fallback function).
How can I solve this problem?
The call to the service is implemented like this in my code: ResponseEntity<Object> data = restTemplate.getForEntity(url, Object.class);
My RestTemplate is set up like this:
private RestTemplate restTemplate = new RestTemplate();
Spring's RestTemplate uses a ResponseErrorHandler to handle errors in responses. This interface provides both a way to determine if the response has an error (ResponseErrorHandler#hasError(ClientHttpResponse)) and how to handle it (ResponseErrorHandler#handleError(ClientHttpResponse)).
You can set the RestTemplate's ResponseErrorHandler with RestTemplate#setErrorHandler(ResponseErrorHandler) whose javadoc states
By default, RestTemplate uses a DefaultResponseErrorHandler.
This default implementation
[...] checks for the status code on the
ClientHttpResponse: any code with series
HttpStatus.Series.CLIENT_ERROR or HttpStatus.Series.SERVER_ERROR is
considered to be an error. This behavior can be changed by overriding
the hasError(HttpStatus) method.
In case of an error, it throws the exception you are seeing.
If you want to change this behavior, you can provide your own ResponseErrorHandler implementation (maybe by overriding DefaultResponseErrorHandler) which doesn't consider 4xx as an error or that doesn't throw an exception.
For example
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return false; // or whatever you consider an error
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// do nothing, or something
}
});
You can then check the status code of the ResponseEntity returned by getForEntity and handle it yourself.

get a new request.getInputStream() from #Context HttpServletRequest request in grails

I am having trouble seeing how i can read form the stream when its being read up in the chain somewhere.
here is my function
#POST
#Consumes(["application/json"])
public Response addGift(#Context HttpServletRequest request,#QueryParam("from") int from,
#ApiParam(value = "Indiviual Gift object to be inserted", required = true) posiba.api.v1.IndividualGift gift) {
return Response.ok().entity("{\"Submit\": \"Success\"}").build();
}
I have a json data POST (payload , body) and a queryparam(for testing)
the request variable can access the params but when i try and read from the stream
stream.available() returns 0 and if i try and get anything out of the stream it closes it.
is there a way to inline override the input stream reader so whatever is up the chain does not read from it and i can use it locally.
solved it myself -- a bit of a hack way tho ----
the goal was to use swagger UI in my grails app so i have one method( as seen above) to tell swagger about the parameters and required etc...and then i made a separate function that accepts the request and does my custom handling of it
#POST
#Consumes(["application/json"])
public Response addGiftt(#Context HttpServletRequest request, InputStream requestBody){
//stuff...
}

Categories