Return error from OKHttp interceptor (using modification)

I am using OkHttp with Retrofit to create network applications for my application. I also use Interceptors for authentication and request requests if necessary.

The server sometimes has temporary problems and returns an empty body, although the response status is 200 OK. This causes my application to crash because the Retrofit callback success block is called, the returned user object (and parsed using GSON) is zero, and the callback with the successful code assumes the object is being returned.

I already reported this to the server team, but I also want to fix it without going around the entire success callback code throughout the application with zero checks.

Currently, I am inclined to two options, although any other ideas are welcome: 1) Do not return from the interceptor (is this possible?) And just display a dialog box with an error 2) Returning something that Retrofit will do will cause some failure in the opposite call.

My code is below. As you can see, I repeat the request 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??? } } 

Thank you very much.

+9
source share
2 answers

I had the same scenario and this post helped me implement the solution. Thanks @mastov for pointing in the right direction.

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

 {"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; } 
+3
source

My solution is taken from okhttp3.logging.HttpLoggingInterceptor

 class ErrorResponseInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) val code = response.code() if (code in 400..500) { responseBody(response)?.also { errorString -> // error string here is a body of server error } } return response } private fun responseBody(response: Response): String? { val responseBody = response.body() ?: return null val contentLength = responseBody.contentLength() if (contentLength == 0L) { return null } val source = responseBody.source() source.request(Long.MAX_VALUE) // Buffer the entire body. var buffer = source.buffer() val headers = response.headers() if ("gzip".equals(headers.get("Content-Encoding"), ignoreCase = true)) { var gzippedResponseBody: GzipSource? = null try { gzippedResponseBody = GzipSource(buffer.clone()) buffer = okio.Buffer() buffer.writeAll(gzippedResponseBody) } finally { gzippedResponseBody?.close() } } val charset: Charset = responseBody.contentType()?.charset(UTF8) ?: UTF8 return buffer.clone().readString(charset) } private companion object { val UTF8: Charset = Charset.forName("UTF-8") } } 
0
source

All Articles