Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning error from OKHttp interceptor (using retrofit)

Tags:

I am using OkHttp with Retrofit to make my app's network requests. I am also using Interceptors for Authentication and retrying requests if necessary.

The server sometimes has temporary problems, and returns an empty body although the response status is 200 OK. This causes my app to crash, because the success block of the Retrofit Callback is called, the custom object returned (and parsed with GSON) is null, and the code in success callback assumes an object is returned.

I have already reported this to the server team, but I want to fix it as well, without having to wrap all success callback code all over the app with null checks.

Currenty I am inclined to two options, although any other ideas are most welcome: 1) Not returning from the interceptor (is this even possible?) and just displaying an error dialog 2) Returning something that will make Retrofit call the failure part of the callback.

My code is below. As you may see, I retry the request for a maximum of 3 times when an empty body is received.

@Override public Response intercept(Chain chain) throws IOException {     // First     Request request = chain.request();     Response response = chain.proceed(request);      ....     ....     ....      // Retry empty body response requests for a maximum of 3 times     Integer retryMaxCount = 3;     MediaType contentType = response.body().contentType();     String bodyString = response.body().string();      while (bodyString.length() == 0 && retryMaxCount > 0)     {         //Empty body received!, Retrying...          retryMaxCount--;         response = chain.proceed(request);         bodyString = response.body().string();     }      if (bodyString.length() != 0)     {         // Create and return new response because it was consumed         ResponseBody newResponseBody = ResponseBody.create(contentType, bodyString);         return response.newBuilder().body(newResponseBody).build();     }     else     {         // WHAT TO WRITE HERE???     } } 

Thanks a lot.

like image 574
Murat Ögat Avatar asked Sep 19 '15 14:09

Murat Ögat


People also ask

Does retrofit uses OkHttp?

Retrofit is basically architecture above the OKHTTP, it internally uses OkHttp to make any request , earlier in jave if we want to make any request we have HTTPUrl connection or HTTPS Url connect know retrofit okHttp handles everything ( it divides into packages it marks headers )for us if we need to send some ...

Which is better retrofit or OkHttp?

OkHttp is a pure HTTP/SPDY client responsible for any low-level network operations, caching, requests and responses manipulation. In contrast, Retrofit is a high-level REST abstraction build on top of OkHttp. Retrofit is strongly coupled with OkHttp and makes intensive use of it.

How do I add OkHttp to retrofit?

Solution 1: Sharing Default OkHttp Instance In order to make them share a single OkHttp instance, you can simply pass it explicitly on the builder: OkHttpClient okHttpClient = new OkHttpClient(); Retrofit retrofitApiV1 = new Retrofit. Builder() . baseUrl("https://futurestud.io/v1/") .


1 Answers

Just had the same scenario and this post helped me implementing the solution. Thanks to @mastov to point to the right direction.

Working with a back-end api that always returns HTTP 200 even if there was an error. This was my response sample of an error

{"status":403,"message":"Bad User credentials","time":1495597740061,"version":"1.0"} 

Here is a simple implementation to complement this answer.

public Response intercept(Chain chain) throws IOException {         Request request   = chain.request();         Response response = chain.proceed(request);         ResponseBody body = response.body();         // Only intercept JSON type responses and ignore the rest.         if (body != null && body.contentType() != null && body.contentType().subtype() != null && body.contentType().subtype().toLowerCase().equals("json")) {             String errorMessage = "";             int errorCode       = 200; // Assume default OK             try {                 BufferedSource source = body.source();                 source.request(Long.MAX_VALUE); // Buffer the entire body.                 Buffer buffer   = source.buffer();                 Charset charset = body.contentType().charset(Charset.forName("UTF-8"));                 // Clone the existing buffer is they can only read once so we still want to pass the original one to the chain.                 String json     = buffer.clone().readString(charset);                 JsonElement obj = new JsonParser().parse(json);                 // Capture error code an message.                 if (obj instanceof JsonObject && ((JsonObject) obj).has("status")) {                     errorCode   = ((JsonObject) obj).get("status").getAsInt();                 }                 if (obj instanceof JsonObject && ((JsonObject) obj).has("message")) {                     errorMessage= ((JsonObject) obj).get("message").getAsString();                 }             } catch (Exception e) {                 Log.e(TAG, "Error: " + e.getMessage());             }             // Check if status has an error code then throw and exception so retrofit can trigger the onFailure callback method.             // Anything above 400 is treated as a server error.             if(errorCode > 399){                 throw new Exception("Server error code: " + errorCode + " with error message: " + errorMessage);             }         }          return response;     } 
like image 166
velval Avatar answered Sep 22 '22 21:09

velval