Returning error from OKHttp interceptor (using retrofit)
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; }
My solution 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") }}
You should throw IOException
. In this case retrofit2 will use onFailure
path.