OkHttp Interceptor
Tags: android
Published: 2022-11-11 08:14:46

---
title: OkHttp Interceptor
published: true
description: In this blog, we will learn how to work with the OkHttp Interceptors.
tags: android
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arsavb3rdioalu4veg9r.png
canonical_url: https://amitshekhar.me/blog/okhttp-interceptor
---

I am [**Amit Shekhar**](https://amitshekhar.me), a mentor helping developers in getting high-paying tech jobs.

In this blog, we will learn how to work with the **OkHttp Interceptors**. We will also see the real use cases where we can use it and how we can use it to get the most out of it. In Android, we have many use cases that can be done using the OkHttp Interceptors.

**This article was originally published at [amitshekhar.me](https://amitshekhar.me/blog/okhttp-interceptor).**

Today, we will cover the following topics to master it:

- What are Interceptors?
- Types of Interceptors.
- How to add Interceptors in OkHttpClient?
- Creating the Interceptor.
- Real use cases using the Interceptors.

## What are Interceptors?

> According to documentation, Interceptors are a powerful mechanism that can monitor, rewrite, and retry the API call. So basically, when we do some API call, we can monitor the call or perform some tasks.

In simple words, Interceptors are like the security personnel in the security check process at the Airport. They check our boarding pass, put a stamp on it, and then allow us to pass.

![okhttp interceptor explain](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/syzbnlytynnrh5vkrmsd.png)

We can use the interceptors to do so many things, for example, centrally monitor API calls. Generally, we need to add the logger for each network call, but by using the interceptor, we can add one logger centrally and that will work for all the network calls. Another use case can be caching the response of network calls to build the offline-first app, we will learn it later in this blog in detail.

## Types of Interceptors

We have two types of interceptors as follows:

- **Application Interceptors**: These are interceptors that are added between the Application Code(our written code) and the OkHttp Core Library. These are the ones that we add using `addInterceptor(`).

- **Network Interceptors**: These are interceptors that are added between the OkHttp Core Library and the Server. These can be added to OkHttpClient using `addNetworkInterceptor()`.

## How to add interceptors in OkHttpClient?

While building the `OkHttpClient` object, we can add the interceptor as below:

```kotlin
fun myHttpClient(): OkHttpClient {
    val builder = OkHttpClient().newBuilder()
        .addInterceptor(/*our interceptor*/)
    return builder.build()
}
```

Here, in `addInterceptor`, we pass the interceptor that we have created. Now, let's see how to create the Interceptor.

## Creating the Interceptor

To create the interceptor, we need to create a class by implementing the Interceptor interface as below:

```kotlin
class MyInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        /**
         * Our API Call will be intercepted here
         */
    }
}
```

Here, in the `intercept()`, we can perform any action which we want inside it.

And to use the interceptor, we can use as below:

```kotlin
fun myHttpClient(): OkHttpClient {
    val builder = OkHttpClient().newBuilder()
        .addInterceptor(MyInterceptor())
    return builder.build()
}
```

We can add the `MyInterceptor` in `addInterceptor()`.

Now, let's discuss more real use cases where we can use the Interceptors.

## Real use-cases using the Interceptors

The following are the common use cases in Android:

### Logging the errors centrally

First, we need to create the `ErrorInterceptor` as below:

```kotlin
class ErrorInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request: Request = chain.request()
        val response = chain.proceed(request)
        when (response.code()) {
            400 -> {
                //Show Bad Request Error Message
            }
            401 -> {
                //Show UnauthorizedError Message
            }
            403 -> {
                //Show Forbidden Message
            }
            404 -> {
                //Show NotFound Message
            }
            // ... and so on
        }
        return response
    }
}
```

- First, we get the request from the `chain.request()`
- Then, we get the response returned from the server, by passing the request in the `chain.proceed(request)`
- Now, we can check for the response code and perform an action.
- We can pass the error to the view.
- Let's say we get a **401 error** i.e. **Unauthorized** then we can perform an action to clear the app data/log out the user or any action which we want to perform.

Now, to use this `ErrorInterceptor` in our OkHttpClient, we can add as below:

```kotlin
.addInterceptor(ErrorInterceptor())
```

This is how we can create a centralized Error Logger using the Interceptor.

OkHttp has a built-in logger which is very useful for debugging.

Note: If we want to log the details for the redirection of the URL, consider using the interceptor at the network layer using the `addNetworkInterceptor()`.

### Caching the response

If we want to cache the response of the API call so that if we call the API again, the response comes out from Cache.

Let's say we have the API call from Client to Server and Cache-Control header is enabled from the server, then OkHttp Core will respect that header and cache the response for a certain time being which was sent from the server.

But what if the Cache-Control is not enabled from the server? We still can cache the response from OkHttp Client using Interceptor.

![okhttp cache control explain](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6t74f5715bg3ggjhnyut.png)

Just see the above image. Here, what we have to do is that we have to intercept the Response before going inside the OkHttp Core and add the header (Cache-Control), so it will be treated as if the response(with the Cache-Control header) has come from the server. Okhttp Core will respect that and cache the response.

We will create an interceptor as below:

```kotlin
class CacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response: Response = chain.proceed(chain.request())
        val cacheControl = CacheControl.Builder()
            .maxAge(10, TimeUnit.DAYS)
            .build()
        return response.newBuilder()
            .header("Cache-Control", cacheControl.toString())
            .build()
    }
}
```

Here, we have a CacheControl which is used to provide the header for Cache-Control.

Finally, we can add as below:

```kotlin
.addNetworkInterceptor(CacheInterceptor())
```

Here, if we see, we are not using the `addInterceptor()` but using `addNetworkInterceptor()` for the use case. This is because in this case, the operation is happening at the network layer.

**But, there is something important, we need to consider while building the offline-first app.**

The cached response will be returned only when the Internet is available as OkHttp is designed like that.

- When the Internet is available and data is cached, it returns the data from the cache.
- Even when the data is cached and the Internet is not available, it returns the error "no internet available".

What to do now?

We can use the following `ForceCacheInterceptor` at the Application layer in addition to the above one **(CacheInterceptor, only if not enabled from the server)**. To implement in the code, we will create a `ForceCacheInterceptor` as below:

```kotlin
class ForceCacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val builder: Request.Builder = chain.request().newBuilder()
        if (!IsInternetAvailable()) {
            builder.cacheControl(CacheControl.FORCE_CACHE);
        }
        return chain.proceed(builder.build());
    }
}
```

We can add the Interceptor in OkHttpClient as below:

```kotlin
.addNetworkInterceptor(CacheInterceptor()) // only if not enabled from the server
.addInterceptor(ForceCacheInterceptor())
```

Here, we are adding the `ForceCacheInterceptor` to OkHttpClient using `addInterceptor()` and not `addNetworkInterceptor()` as we want it to work on the Application layer.

### Adding the Header like Access Token centrally

Let's say that we have to make the API calls and we have to add the Authorization Header in all the API calls. Either we can use it individually or we can centralize that using the Interceptor.

```kotlin
class AuthTokenInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val requestBuilder = originalRequest.newBuilder()
            .header("Authorization", "AuthToken")
        val request = requestBuilder.build()
        return chain.proceed(request)
    }
}
```

- First, we get the token for the header from our local storage like a SharedPreference.
- Here, we intercept the original request which we triggered from the application using `chain.request()` and set it to `originalRequest`.
- Then, we build the request again by adding the **Header** with the key and value which is required to make the network call.
- Then, we will build the request again and return the response using `chain.proceed(request)` by passing the new request which is having the Authorization header.

This is how we can centralize the Header which is common in all the API Calls. Now to use it in the OkHttpClient, we will do as below:

```kotlin
.addInterceptor(AuthTokenInterceptor())
```

Let's go to another use case.

### Refreshing the Access Token at Single Place

Let us say we have a use-case that when we get a **401 error** in the Error Interceptor and we need to refresh the auth token as we have an Unauthorized error. We can do that using the below:

```kotlin
override fun intercept(chain: Interceptor.Chain): Response {
    val accessToken = //our access Token
    val request = chain.request().newBuilder()
        .addHeader("Authorization", accessToken)
        .build()
    val response = chain.proceed(request)
    if (response.code() == 401) {
            val newToken: String = //fetch from some other source
            if (newToken != null) {
               val newRequest =  chain.request().newBuilder()
                    .addHeader("Authorization", newToken)
                    .build()
                return chain.proceed(newRequest)
            }
    }
    return response
}
```

If we get the `response.code()` as **401** i.e. **Unauthorized**, we will refresh the token here, then modify the request by adding the new header and make the new request to the server.

And, this should be synchronized to avoid the concurrency issue.

Note: Another way that is more flexible when it comes to refreshing the Access Token is to use the Authenticator interface of OkHttp.

Now, let's move to another use case.

### Enabling Gzip at Android End

Gzip is used for data compression. In Android as well, we can use the Gzip for the compression using the interceptor.

So, while getting a response, OkHttp automatically respects the header (**content encoding**) and decompresses the data and returns, but let's say when we have to send compressed data to a server, then we have to write our own interceptor.

We can create the `GzipRequestInterceptor` class as below (taken from the OkHttp repository).

```java
public class GzipRequestInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
            return chain.proceed(originalRequest);
        }

        Request compressedRequest = originalRequest.newBuilder()
                .header("Content-Encoding", "gzip")
                .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body())))
                .build();
        return chain.proceed(compressedRequest);
    }

    private RequestBody forceContentLength(final RequestBody requestBody) throws IOException {
        final Buffer buffer = new Buffer();
        requestBody.writeTo(buffer);
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return requestBody.contentType();
            }

            @Override
            public long contentLength() {
                return buffer.size();
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.write(buffer.snapshot());
            }
        };
    }


    private RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }

            @Override
            public long contentLength() {
                return -1; // We don't know the compressed length in advance!
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }
        };
    }
}
```

To use the interceptor we can use as below:

```kotlin
.addInterceptor(GzipRequestInterceptor())
```

So, these are the real use cases, how can we use the interceptors in our Android App. We can do a lot of things with interceptors. Let's start making the most of it.

Show your love by sharing this blog with your fellow developers.

Master Kotlin Coroutines from here: [Mastering Kotlin Coroutines](https://amitshekhar.me/blog/kotlin-coroutines)

That's it for now.

Thanks

[**Amit Shekhar**](https://amitshekhar.me)

You can connect with me on:

- [Twitter](https://twitter.com/amitiitbhu)
- [LinkedIn](https://www.linkedin.com/in/amit-shekhar-iitbhu)
- [GitHub](https://github.com/amitshekhariitbhu)
- [Facebook](https://www.facebook.com/amit.shekhar.iitbhu)

[**Read all of my high-quality blogs here.**](https://amitshekhar.me/blog)