OkHttp Interceptors in Android

May 25 2021 · Kotlin 1.4, Android 5, Android Studio 4.1

Part 1: Implementing OkHttp Interceptors

05. Implement an Authorization Interceptor

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 04. Implement a Custom Logger & Redact Headers Next episode: 06. Implement an Analytics Interceptor

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 05. Implement an Authorization Interceptor

When working on an app that makes multiple API calls, it can be tedious to add the API key to every API call. Open MovieApi inside the network package. You will notice that the api_key query parameter is present in every method. This can get cumbersome as the number of API calls increases.

Instead, you can use an Authentication Interceptor to decouple the API keys from the methods.Create a new package named interceptors inside the network package. Add a new file name ApiKeyInterceptor inside the package.

Add the following code to it:

class ApiKeyInterceptor: Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {

    }
}

ApiKeyInterceptor implements the Interceptor interface and overrides the method named intercept. Next, get access to the original URL of the request.

class ApiKeyInterceptor: Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val originalUrl = originalRequest.url
    }
}

Here, chain is the current request chain that you have intercepted. From the chain, you get the request object. Using the request object, you fetch the URL the request is pointing to.

Now create a new URL by adding the api_key query parameter to the original URL as follows:

class ApiKeyInterceptor: Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val originalUrl = originalRequest.url

        val url = originalUrl.newBuilder()
            .addQueryParameter("api_key", BuildConfig.THE_MOVIE_DB_API_TOKEN)
            .build()
    }
}

A URL cannot be changed and thus you have to create a new URL using the newBuilder() method. You then add a query parameter using addQueryParameter() and the pass the query name and value as parameters.

Since it is not a good practice to use hardcoded strings without explanation, extract “api_key” to a constant.

class ApiKeyInterceptor: Interceptor {

    private val apiKeyQueryParameterKey = "api_key"

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val originalUrl = originalRequest.url

        val url = originalUrl.newBuilder()
            .addQueryParameter(apiKeyQueryParameterKey, BuildConfig.THE_MOVIE_DB_API_TOKEN)
            .build()
    }
}

Next, create a new request with the modified URL you just created and proceed with the new request.

class ApiKeyInterceptor: Interceptor {

    private val apiKeyQueryParameterKey = "api_key"

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val originalUrl = originalRequest.url

        val url = originalUrl.newBuilder()
            .addQueryParameter(apiKeyQueryParameterKey, BuildConfig.THE_MOVIE_DB_API_TOKEN)
            .build()

        val newRequest = originalRequest.newBuilder()
            .url(url)
            .build()

        return chain.proceed(newRequest)
    }
}

Similar to a URL, a request instance cannot be modified. You have to create a new request using the new URL. You then ask the chain to proceed by using the new request instead of the old one.

Finally, add an instance of ApiKeyInterceptor to the OkHttp client. Go back to OkHttpProvider and add the ApiKeyInterceptor as follows:

val okHttpClient = OkHttpClient.Builder()
    .readTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
    .connectTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
    .addInterceptor(loggingInterceptor)
    .addInterceptor(ApiKeyInterceptor())
    .build()

Since ApiKeyInterceptor will now take care of adding the API keys, it is safe to remove the API from individual API call methods. Open MovieApi and remove the api_key parameter from both the methods.

Build and run the app. The data loads successfully indicating that the API keys are correctly added to the requests. But if you open the Logcat tab, you will notice that the API key is missing from the URL in the logs.

The cause of this is in the OkHttp client. Go back to OkHttpProvider. You can see that the ApiKeyInterceptoris added after the HttpLoggingInterceptor.

The order of the interceptors matter. In this case, the API keys are added to the requests but only after they have been logged. To fix this, add the ApiKeyInterceptor before HttpLoggingInterceptor as follows:

val okHttpClient = OkHttpClient.Builder()
    .readTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
    .connectTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
    .addInterceptor(ApiKeyInterceptor())
    .addInterceptor(loggingInterceptor)
    .build()

Build and run the app. Open the Logcat tab and you can see that the API keys are also logged correctly now.