Retrofit does not call failure and response.code() is 400 - java

I'm sure there's something wrong with what I'm doing here. Retrofit does not go to failure even if the code that I'm getting is 400.
AuthenticationService authService = retrofit.create(AuthenticationService.class);
Call<ValidateTokenResponseMessage> request = authService.validateToken(token);
request.enqueue(new Callback<ValidateTokenResponseMessage>() {
#Override
public void onResponse(Call<ValidateTokenResponseMessage> call, retrofit2.Response<ValidateTokenResponseMessage> response) {
// When I try to put a breakpoint here I can see that response.code() is 400.
ValidateTokenResponseMessage body = response.body();
if (!body.getValidToken()) {
// do success
}
}
#Override
public void onFailure(Call<ValidateTokenResponseMessage> call, Throwable t) {
// do failure
}
});

Callback.onFailure only gets called for exceptions like missing internet connection or invalid https etc.
Invoked when a network exception occurred talking to the server or when an unexpected exception occurred creating the request or processing the response.
To check for errors you have to use Response.isSuccessful()
Returns true if code() is in the range [200..300).

Related

Why handleResponse(URI url, HttpMethod method, ClientHttpResponse response) method of RestTemplate.class is getting called for a 200 Succeess response

I am calling an external API from my code using RestTemplate like below:
try {
responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity,
UploadResonse.class);
} catch (BusinessException ex) {
fetchErrorResponseEntity = ex.getResponseEntity();
if (fetchErrorResponseEntity.getStatusCodeValue() == 404) {
throw new BusinessException(ex.getMessage(), ErrorResponse.NOT_FOUND);
} else if (fetchErrorResponseEntity.getStatusCodeValue() == 500) {
throw new BusinessException(ex.getMessage(),
ErrorResponse.INTERNAL_SERVER_ERROR);
} else if (fetchErrorResponseEntity.getStatusCodeValue() == 400) {
throw new BusinessException(ex.getMessage(), ErrorResponse.INVALID_REQUEST);
}
}
This API call is returning 200 Success but when I debug it, it still goes to handleResponse(URI url, HttpMethod method, ClientHttpResponse response) method of RestTemplate.class
And then it's coming to my RestTemplateErrorHandler.java file
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse)
throws IOException {
return clientHttpResponse.getStatusCode() != HttpStatus.OK;
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse)
throws IOException {
String errMessage = getErrMessage(clientHttpResponse);
HttpStatus status = clientHttpResponse.getStatusCode();
switch (status) {
case BAD_REQUEST: // 400
throw new BusinessException(errMessage,
ErrorResponse.INVALID_REQUEST);
case NOT_FOUND:
throw new BusinessException(errMessage, ErrorResponse.NOT_FOUND);
case SERVICE_UNAVAILABLE: // 503
throw new BusinessException(errMessage, ErrorResponse.TIME_OUT);
case METHOD_NOT_ALLOWED: // 405
case INTERNAL_SERVER_ERROR: // 500
default:
throw new BusinessException(errMessage,
ErrorResponse.INTERNAL_SERVER_ERROR);
}
}
Can someone lease help me to understand if it's the correct behaviour.
I suspect that if the response is 200 Success it should not go to the RestTemlate.class and RestTemplateErrorHandler.class
This behaviour is creating problem when API return 201 Created status, that time it goes to handleError() method and return the default case INTERNAL_SERVER_ERROR
Can someone please help me here
The following code will call the error handler every time the response is not 200 OK, event if it is successful like 201 Created.
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse)
throws IOException {
return clientHttpResponse.getStatusCode() != HttpStatus.OK;
}
Try changing the implementation to the following:
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse)
throws IOException {
return !clientHttpResponse.getStatusCode().is2xxSuccessful();
}
This is better suited for your needs as it will consider all 2xx status as successful requests instead of only 200 OK.
According to documentation method handleResponse() as it name suggests will handle the given response, perform appropriate logging and invoke the ResponseErrorHandler (if needed) which is interface used by the RestTemplate to determine whether a particular response has an error or not.
RestTemplateErrorHandler class implements implements ResponseErrorHandler.
If the hasError() method returns true then Spring will automatically call the handleError() method. This is the flow.
If you check implementation for handleResponse method, given below, you will see that there is a call to hasError method to check if the response has any errors. Default implementation of hasError method will return true is response code is 4XX or 5XX. If there is no errors, method will proceed execution and handleError method won't be invoked, as I explained above.
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
ResponseErrorHandler errorHandler = this.getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (this.logger.isDebugEnabled()) {
try {
this.logger.debug(method.name() + " request for \"" + url + "\" resulted in " + response.getRawStatusCode() + " (" + response.getStatusText() + ")" + (hasError ? "; invoking error handler" : ""));
} catch (IOException var7) {
;
}
}
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
In code you posted hasError will return true for all response codes that are different from 200. That's why handleError is invoked.

HttpClientErrorException 400 null using RestTemplate in microServices

I have two microservices. The first one receives a call from the Frontend and then it calls to the second uService to receive some data. The last is returning an error response (Bad Request, this is ok - it is a use-case). However, I am losing the body (message) returned from the second microservice, as the first is throwing a HttpClientErrorException 400 null in the call
This is my code:
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
I am not able to do entityResponse.getStatusCode() as an exception is thrown.
Handled it in the ControllerAdvice, my exception message is "400 null" even I return a custom message from the service.
So, I would like to get the response message sent in the called uservice to manage it.
Thanks in advance.
The answers here that explain how to catch the exception and access the body are correct. However, you may use a different approach. You can use a 3-d party library that sends Http request and handles the response. One of the well-known products would be Apache commons HTTPClient: HttpClient javadoc, HttpClient Maven artifact. There is by far less known but much simpler HTTPClient (part of an open source MgntUtils library written by me): MgntUtils HttpClient javadoc, MgntUtils maven artifact, MgntUtils Github. Using either of those libraries you can send your REST request and receive response independently from Spring as part of your business logic
What I'm doing in my project is the following.
MicroService_2 calls MicroService_1.
MicroService_1
MicroService_1 returns for example a HTTP 404 exception if the entity isn't found.
#RestController
#RequestMapping(value = "/api/v1/")
public class Service1Controller {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody MyEntity getMyEntity(#PathVariable String id) throws NotFoundException {
MyEntity result = ...
if(result == null) {
throw new NotFoundException("MyEntity [id: "+id+"] not found");
}
return result;
}
#ControllerAdvice
public class RestEndpointExceptionHandler extends RestExceptionHandler {
#ExceptionHandler(NotFoundException.class)
public ResponseEntity<String> handleNotFoundException(HttpServletRequest req, NotFoundException ex) throws NotFoundException {
return new ResponseEntity<String>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
MicroService_2
The MicroService_2 calls MicroService_1 and catches the exception by HTTP code and regenerate the NotFoundException.
#Override
public MyEntity getMyEntity(Principal principal) {
try {
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
return entityResponse.getBody();
} catch(HttpClientErrorException e) {
HttpStatus status = e.getStatusCode();
if (status == HttpStatus.NOT_FOUND) {
throw new NotFoundException(e.getResponseBodyAsString()); // should be "MyEntity [id: {id}] not found"
} else {
throw new UnexpectedServerException(e.getResponseBodyAsString());
}
}
}
The Spring RestTemplate throws an error in case of 500 or 400 status codes. So if your second service responds with an error an exception will be thrown by the RestTemplate call in your first service.
HttpClientErrorException: in case of HTTP status 4xx
HttpServerErrorException: in case of HTTP status 5xx
UnknownHttpStatusCodeException: in case of an unknown HTTP status
To get the response message you could either catch the exception. E.g:
try {
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
} catch(HttpStatusCodeException e) {
// e.getResponseBodyAsString();
}
or define a ResponseErrorHandler. The ResponseErrorHandler can be set during the instantiation of the RestTemplate. In the handleError method you will also be able to access the response message.
#Override
public void handleError(ClientHttpResponse httpResponse)
throws IOException {
}

Catch all Exceptions and also return custom Errors in Jersey

I want to catch all unexpected Exceptions in a jersey rest service.
Therefore i wrote an ExceptionMapper:
#Provider
public class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
private static Logger logger = LogManager.getLogManager().getLogger(ExceptionMapper.class.getName());
#Override
public Response toResponse(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Internal error").type("text/plain").build();
}
}
The mapper catches really all exceptions. Therefore i can't write:
public MyResult getById(#PathParam("id")) {
if (checkAnyThing) {
return new MyResult();
}
else {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
This is catched by the Mapper. Now i have to write:
public Response getById(#PathParam("id") {
if (checkAnyThing) { {
return Response.ok().entity(new MyResult()).build();
}
else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Is this the correct way to catch all unexpected exceptions and also return errors (error codes) in jersey? Or is there any other (more correct) way?
WebApplicationException has a getResponse from which we can get the Response. So you can check for a WebApplicationException in your mapper. Maybe something like
#Override
public Response toResponse(Throwable error) {
Response response;
if (error instanceof WebApplicationException) {
WebApplicationException webEx = (WebApplicationException)error;
response = webEx.getResponse();
} else {
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal error").type("text/plain").build();
}
return response;
}
That way an instance of WebApplicationException thrown will just return the default response. This will actually handle some other exceptions also, not thrown explictly by your application. WebApplicationException has a few other exception under its hierarchy that are thrown by JAX-RS, for which predefined response/status codes are wrapped.
Exception Status code Description
-------------------------------------------------------------------------------
BadRequestException 400 Malformed message
NotAuthorizedException 401 Authentication failure
ForbiddenException 403 Not permitted to access
NotFoundException 404 Couldn’t find resource
NotAllowedException 405 HTTP method not supported
NotAcceptableException 406 Client media type requested
not supported
NotSupportedException 415 Client posted media type
not supported
InternalServerErrorException 500 General server error
ServiceUnavailableException 503 Server is temporarily unavailable
or busy
That being said, we could explicitly throw any of these exceptions in our code, just to give it more semantic value.
Generally speaking though, the example above may be unnecessary, unless you want to alter the response message/status code, as one can from the table above, the hierarchy of exceptions already have some general mapping. And in most cases, unexpected exceptions will already be mapped to InternalServerErrorException

Redo previous request when the first fails with 401 Unauthorized

I'm building an Android application that will fetch data from a REST API.
To make the requests I'm using Retrofit together with Otto.
For all my requests I add a RequestInterceptor that will add a header (Authorization) to all my requests.
In the RequestInterceptor I'm calling a method to the my current access_token then I populate the header to the request.
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
Token token = TokenPreferences.getToken();
request.addHeader("Authorization", token.getTokenType() + " " + token.getAccessToken());
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://example.com")
.setRequestInterceptor(requestInterceptor)
.build();
...
This works fine until the access_token has expired, then the request will fail with HTTP status 401 Unauthorized.
When this happens, I want to make a new request to get a new access_token from my refresh_token I got and then do the first request again.
I'm not really sure how to make that work.
Try a com.squareup.okhttp.Authenticator. As far as I can tell, this is preferable to com.squareup.okhttp.Interceptor (which you'll find suggested elsewhere), because it will only kick in for unauthorized requests. Here's a basic example:
public class ApiAuthenticator implements Authenticator {
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
for (Challenge challenge : response.challenges()) {
if (challenge.getScheme().equals("Bearer")) {
String authToken = // Refresh the token here
if (authToken != null) {
return response.request().newBuilder()
.header("Authorization", "Bearer " + authToken)
.build();
}
}
}
return null;
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
}
You can attach it to your client like this:
okHttpClient.setAuthenticator(new ApiAuthenticator());
Be aware that if you're using Retrofit to refresh your token and the token is invalid, you might get unexpected, hard-to-debug behavior for 403 codes, but the solution is just to use a try/catch block.
try {
token = oauthService.refreshAccessToken(args);
} catch (RetrofitError error) {
// Do something that leads to login
}
Retry mechanizm is not implemented in Retrofit. It will be in v2. (Retrying requests manually)
You should implement retrying by recursive call from onFailure() callback as Deepack suggested.
I am facing the same issue and I currently don't know how to retry my request after it failed due to a Unauthorized error.
Although #Yuriy Ashaev mentioned that the retry mechanism should be part of the v2.0 Retrofit version, this should be dedicated only to 5xx errors (see Request Object draft description here) and attempting to retry a request that failed for another reason will raise an exception.
As of now, you can still add your own ErrorHandler to your RestAdapter and catch the Unauthorized error to fetch a refresh token. Here is a way to achieve this:
class RestErrorHandler implements retrofit.ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
Response r = cause.getResponse();
if (r != null && r.getStatus() == 401) {
Token token = mTokenProvider.fetchToken();
if (token != null) {
addHeader(HEADER_AUTHORIZATION, token.getToken_type() + " " + token.getAccess_token());
}
// Todo: Relaunch previous request
return cause;
}
return cause;
}
}
However I don't see any way to retry the failed request from this point other than in all your failure() request callback which will be called when returning the RetrofitError from your ErrorHandler...
I would really appreciate if someone could point us how you could retry the failed request from the ErrorHandler as the response only point to f-the request url.

Handling Exception on Asynchronous Webservice Request

I am still new to implement web service request using Play!Framework 2.1 WS library. Now, I have problem on understanding the WS library behaviour.
Firstly, I have code like this :
public static Result espnRss() {
try {
// do request
return async(
WS.url("http://espnfc.com/rss/news?section=premierleague").get().map(
new F.Function<WS.Response, Result>() {
#Override
public Result apply(WS.Response response) throws Throwable {
return ok("Success!"); // success request
}
}
)
);
} catch (Exception e) {
// exception occured
return internalServerError("Oops, connect exception occured!");
}
}
When I try to request the espnRss action, I got SUCCESS response.
Then, I want to set WS timeout on the request. So, I changed my previous code like this :
public static Result espnRss() {
try {
// set request timeout for 1000 ms and do request
return async(
WS.url("http://espnfc.com/rss/news?section=premierleague").setTimeout(1000).get().map(
... // same as previous
)
);
} catch (Exception e) {
// exception occured
return internalServerError("Oops, connect exception occured!");
}
}
My internet connection is not fast (Download speed is about 40 KB/s) and I do that on purpose (set request time out for 1 second) to make exception handling code is executed.
But, I get default response from framework, not internalServerError response the code provided.
Execution Exception
[TimeoutException: No response received after 1000]
Can anyone explain me why the exception on WS request cannot be caught using my code above? How is the best way to handle exception using Play!Framework WS library?
To handle exception that occur on asynchronous request such as WS request with Play!Framework 2.1.0, there is method on Promise named recover(F.Function<java.lang.Throwable,A> function).
The method should be called when we want to handle all exception occured while requesting using WS library. So, I solved the problem using code that looked like following:
public static Result espnRss() {
// do request
return async(
WS.url("http://espnfc.com/rss/news?section=premierleague").setTimeout(100).get().map(
new F.Function<WS.Response, Result>() {
#Override
public Result apply(WS.Response response) throws Throwable {
return ok("Success!"); // success request
}
}
).recover( // to handle error occured on redeemed PROMISE
new F.Function<Throwable, Result>() {
#Override
public Result apply(Throwable throwable) throws Throwable {
// option to distinguish exception
if (throwable instanceof TimeoutException) {
return internalServerError("Oops, time out exception occured!");
} else {
return internalServerError("Oops, other exception occured!");
}
}
}
)
);
}
I am not familiar with the Play framework but async must be returning/using some kind of future. The request is actually performed in a separate Thread which Exceptions are obviously not caught by your try..catch handler.
There must be some function/method like onComplete that you can apply to async allowing you to test the result of running the request.

Categories

Resources