Search code examples
androidkotlinretrofit

how to get response headers using Retrofit in Kotlin?


Hello I am working on async using retrofit and rxjava2

and I have to get the value from the header while talking to the server developer.

However, I don't know how to get the header from the method I use. I know how to get it from Call Response, but I don't know how to bring the header because the method used is different.

my retrofit2 class

private val retrofit: Retrofit = Retrofit.Builder()
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .client(client())
        .baseUrl(serverIp)
        .build()

    val userApi: UserAPI = retrofit.create(UserAPI::class.java)

my model class

@POST("user")
    fun login(
        @Body loginRequest : LoginRequest
    ) : Single<UserResponse>

data class LoginRequest(
    val phone: String?,
    @SerializedName("gender")
    val gender: String?,
    @SerializedName("age")
    val age: String?,
    @SerializedName("email")
    val email: String?
)

data class UserResponse (
    override var status: Int,
    override var message: String,
    override var timestamp: String,
    var data: Data
) : CommonResponse() {
    data class Data(
        var username: String?,
        var token: String?
    )
}

my viewModel ( rx )

addDisposable(
            model.loginBody(loginRequest)
                .subscribeOn(Schedulers.io())
                .subscribe({
                    _loginResult.postValue(it)
                }, {
                    Timber.d("response error, message : ${it.localizedMessage}")
                })
        )

My current situation is as follows. I need the headers returned by the server after login, I can see it from the okhttp log, but I don't know how to get a specific header


Solution

  • Word of advice rather than a solution

    Not a question of solving your problem but even if it's late, as you're using Kotlin, a better solution would be to migrate from rxJava to Kotlin Flow. It allows you to use suspend functions to do your retrofit calls, then use Kotlin Flow to do the rxJava's job on the IO thread.

    It also allows you to use the Response<T> retrofit object in a simpler way.

    Example:

    The retrofit request

        @POST(YOUR_ROAD) // Retrofit road
            suspend fun connect(
                @Body credentials: CredentialsObjectBody
            ): Response<ConnectionObjectResponse>
    

    The repository call to the retrofit request

        // The function is suspending the thread with suspend keyword
        suspend fun yourFunction(): Flow<DataState<TypeForEmitter>> = flow {
            try {
                body.clientId = sessionPrefs.deviceName!!
                val connectionResponse =
                    majorApi.connect(
                        // what you need to put in the body
                    )
                        .apply {
                            if (isSuccessful) {
                                body()?.let {
                                    // Here you can use what's in the request body
                                    emit(DataState.Success(it))
                                } ?: throw Exception("Request body was null")
                            } else {
                                throw Exception(headers()["message"]) // use throw to handle the error in the catch
                            }
                        }
            } catch (e: Exception) {
                Log.e(TAG, "error: ${e.message}")
                emit(DataState.Error(e))
            }
        }
    
    

    DataState:

    DataState is a sealed class that allows to differentiate emitted status
        sealed class DataState<out R> {
            data class Success<out T>(val data: T) : DataState<T>()
            data class Error(val exception: Exception) : DataState<Nothing>()
            object Loading : DataState<Nothing>()
        }
    
    

    How to call the Kotlin flow to launch it on IO thread to prevent blocking the Main (or UI) thread

        withContext(Dispatchers.IO) {
            yourFunction().onEach {
                /*
                    onEach is like onNext, but emits every type of error, compared to rxJava
                    that differentiates next/success, complete and error events
                */
            }
        }