Let me also comment a little bit (actually a lot) about adding headers in Kotlin focusing on Dependency Injection.
The best approach would be to provide both OkHttpClient and HttpLoggingInterceptor on the same di method making use of the handy Kotlin Scoping function in this case also
and apply
.
We will be needing these Retrofit (2.9)
and OkHttpClient
dependencies - this example uses Kotlin DSL but should be more or less the same in Groovy. Of-course you will be needing other dependencies like Hilt if you are using Dependency Injection.
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.7")
implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.7")
Next stop is to create the @Provide function which returns OkHttpClient
.
@Provides
@Singleton
fun provideOkHttpClient():OkHttpClient { ...}
Background theory about Interceptors is very vital; to use an interceptor, you need to create a class that implements the Interceptor interface
and override the intercept()
method.
intercept()
receives an Interceptor.Chain
object - which represents the current request and allows you to proceed with the request by calling the proceed()
method, or cancel the request by throwing an exception
. intercept()
override function returns a Response
object which is exactly what chain.proceed(request)
returns.
class MyInterceptor : Interceptor {
//throw an exception to cancel request
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
.newBuilder() // returns Request.Builder
.addHeader("Header_1", "value_1")
.build()
//proceed with the request
return chain.proceed(request)
}
}
Thanks to Kotlin Anonymous Function Syntax and Builder Pattern we can skip the above theory steps and start to build OkHttpClient
which has the addInterceptor()
function.
fun provideOkHttpClient(): OkHttpClient {
//build client
return OkHttpClient.Builder()
//create anonymous interceptor in the lambda and override intercept
// passing in Interceptor.Chain parameter
.addInterceptor { chain ->
//return response
chain.proceed(
//create request
chain.request()
.newBuilder()
//add headers to the request builder
.also {
it.addHeader("Header_1", "value_1")
it.addHeader("Header_2", "value_2")
}
.build()
)
}
.also { okHttpClient ->.... }
In the above code addInterceptor()
opens up a lambda where we anonymously override intercept()
passing in a chain parameter.
We use chain.proceed(request)
to return a Response
. It is when constructing the request to pass to chain.proceed()
that we modify the actual request to add the headers.
You can also proceed to build up on the OkHttpClient to add timeouts etc.
.also { okHttpClient ->
okHttpClient.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
okHttpClient.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
if (BuildConfig.DEBUG) {
val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
okHttpClient.addInterceptor(httpLoggingInterceptor)
}
}
.build()
This is the final code.
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
//build client
return OkHttpClient.Builder()
//create anonymous interceptor in the lambda and override intercept
// passing in Interceptor.Chain parameter
.addInterceptor { chain ->
//return response
chain.proceed(
//create request
chain.request()
.newBuilder()
//add headers to the request builder
.also {
it.addHeader("Header_1", "value_1")
it.addHeader("Header_2", "value_2")
}.build()
)
}
//add timeouts, logging
.also { okHttpClient ->
okHttpClient.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
okHttpClient.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
//log if in debugging phase
if (BuildConfig.DEBUG) {
val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
okHttpClient.addInterceptor(httpLoggingInterceptor)
}
}
.build()
}
This marks my StackOverflow's longest ever post, I'm sorry guys.