7

i wanna add a token to request headers in Interceptor . but now i dont now what should i do.

here is my datastore


val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

class SettingsManager(context: Context) {
    private val dataStore = context.dataStore

    val getToken = dataStore.data
        .catch {
            if (it is IOException) {
                it.printStackTrace()
                emit(emptyPreferences())
            } else {
                throw it
            }
        }.map { preference ->
            preference[token] ?: ""
        }

    suspend fun setToken(tokenStr: String) {
        dataStore.edit { preferences ->
            preferences[token] = tokenStr
        }
    }

    companion object {
        val token = stringPreferencesKey("token")
    }
}

i can not get the context object in Interceptor 。

so i try to use hilt to fix it

class RequestInterceptor(private val settingsManager: SettingsManager) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        runBlocking {
            val first = settingsManager.getToken.first()
            LogUtil.i("token " + first)
            request.newBuilder().addHeader("token", first).build();
        }
        return chain.proceed(request)
    }
}

i dont know the next step @AndroidEntryPoint cant use on Interceptor class .

plz guys , give me some solutions

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
  • I want to do the same and I'm running out of ideas. Just thinking about using SharedPrefs instead. – Alberto Mar 17 '22 at 09:12

3 Answers3

3

I just used this way.

  suspend fun getToken() : String? {
    val preference = dataStore.data.first()
    return preference[PREF_JWT_TOKEN]
} // this is inside the datastore local repository.

and inside the intercepter you can get runBlocking UI as this.

val builder = Retrofit.Builder()
        .baseUrl(Constants.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
    val client = OkHttpClient.Builder()
        .addInterceptor { chain ->
            val original = chain.request()
            var token: String? = null
            runBlocking {
                token = dataStorePreferenceRepository.getToken()
                if (!token.isNullOrEmpty()) {
                    val authorized = original.newBuilder()
                        .addHeader("Authorization", "Bearer $token")
                        .build()
                    chain.proceed(authorized)
                } else {
                    chain.proceed(original)
                }
            }
        }.addNetworkInterceptor(StethoInterceptor()).build()
    return builder.client(client).build().create(ApiInterface::class.java)

and inside the Module class use like this.

@Module                                                                                                            
@InstallIn(SingletonComponent::class)                                                                              
object AppModule {                                                                                                 
                                                                                                                   
                                                                                                                   
    @Provides                                                                                                      
    @Singleton                                                                                                     
    fun getDataStorePreferenceRepository(@ApplicationContext appContext: Context): DataStorePreferenceRepository { 
        return DataStorePreferenceRepository(context = appContext)                                                 
    }                                                                                                              
                                                                                                               }
  • runBlocking works but not recommend from official docs, offers poor performance and can cause unexpected behaviours like ANR issues, – erik.aortiz May 16 '23 at 17:47
1

You should be able to see it in Hilt if you just add the @Inject constructor to your RequestInterceptor.

E.g.

class RequestInterceptor @Inject constructor(private val settingsManager: SettingsManager) : Interceptor {...

And I'm not sure if you can do your SettingsManager like that but you can try class SettingsManager @Inject constructor(@ApplicationContext context: Context) {

Barry Irvine
  • 13,858
  • 3
  • 25
  • 36
1

This works for me

    override fun intercept(chain: Interceptor.Chain): Response {
    return runBlocking {
        val token = dataStorePreferences.getAuthToken().first()
        val request = chain.request().newBuilder().addHeader(AUTHORIZATION_HEADER, "$BEARER $token").build()
        val response = chain.proceed(request)
        if (response.code == UNAUTHORIZED_CODE) {
            try {
                dataStorePreferences.setAuthToken(token)
                chain.proceed(request.newBuilder().addHeader(AUTHORIZATION_HEADER, "$BEARER $token").build())
            } catch (exeption: Exception) {
                dataStorePreferences.setAuthToken("")
                response
            }
        } else {
            response
        }
    }
}