簡體   English   中英

如何刪除 Dagger Hilt 依賴注入循環

[英]How to remove Dagger Hilt dependency Injection cycle

我有一個提供ApiService實例的網絡模塊 class。 有一個Authenticator class,它在過期時刷新訪問令牌。 身份驗證器需要ApiService實例來進行 API 調用。 這會導致循環依賴。 如何避免這種情況?

現在我在TokenExpiryAuthenticator class 中創建一個新的ApiService來進行 API 調用,以打破循環依賴。 如何正確地將ApiService注入TokenExpiryAuthenticator而不會導致循環依賴?

@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {

@Provides
@Singleton
@Named("Other")
fun provideRetrofitWithoutInterceptor(@Named("Other") client: OkHttpClient, gson: Gson): Retrofit {
    return Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
}

@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient, gson: Gson): Retrofit {
    return Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
}

@Provides
@Singleton
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor, supportInterceptor: SupportInterceptor, tokenExpiryAuthenticator: TokenExpiryAuthenticator): OkHttpClient {
    return OkHttpClient.Builder().writeTimeout(1, TimeUnit.MINUTES)
        .readTimeout(1, TimeUnit.MINUTES)
        .callTimeout(1, TimeUnit.MINUTES)
        .addInterceptor(httpLoggingInterceptor)
        .addInterceptor(supportInterceptor)
        .authenticator(tokenExpiryAuthenticator)
        .build()
}

@Named("Other")
@Provides
@Singleton
fun providesOkHttpClientWithoutInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
    return OkHttpClient.Builder().writeTimeout(1, TimeUnit.MINUTES)
        .readTimeout(1, TimeUnit.MINUTES)
        .callTimeout(1, TimeUnit.MINUTES)
        .addInterceptor(httpLoggingInterceptor)
        .build()
}

@Provides
@Singleton
fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
    return if (BuildConfig.DEBUG)
        HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
    else
        HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.NONE
        }
}

@Provides
@Singleton
fun providesGson(): Gson {
    return GsonBuilder().create()
}

@Provides
@Singleton
fun providesRestApiService(retrofit: Retrofit): ApiService {
    return retrofit.create(ApiService::class.java)
}

@Provides
@Singleton
@Named("Other")
fun providesRestApiServiceWithoutInterceptor(@Named("Other") retrofit: Retrofit): ApiService{
    return retrofit.create(ApiService::class.java)
}

}

您可以拆分 ApiService 並創建一個新的 AuthenticationApi,它僅包含用於驗證或刷新訪問令牌的端點。 此 AuthenticationApi 是使用沒有 Authenticator 的 OkHttp 實例創建的。

這樣你的身份驗證器只需要參考這個苗條的 Retrofit api。這也保證你不會得到 HTTP 401 身份驗證循環,以防在使用身份驗證端點時出現錯誤的憑據。

最簡單的解決方案是延遲注入依賴項。

class TokenExpiryAuthenticator @Inject constructor(
  private val api: Lazy<ApiService>,
  private val persistence: TokenPersistenceRepository
) : Authenticator {

  override fun authenticate(route: Route?, response: Response): Request? {

    // If HTTP 401 error was raised while trying to refresh token it means
    // the token is invalid (or expired). Sign out user and do not
    // proceed with further requests.
    if (response.request.url.encodedPath == REFRESH_TOKEN_PATH) {

      // Perform any cleanup needed.
      
      // Return null so no further requests will be performed.
      return null
    }

    // Request failed for old token. It's about time to refresh it.
    if (response.request.header(AUTH_HEADER) != null) {
      
      // Refresh access token using your lazily injected service.
      persistence
        .retrieveRefreshToken()
        .flatMap(api.get()::refreshToken)
        .flatMapCompletable {
          Completable.concatArray(
            persistence.saveAccessToken(it.accessToken),
            persistence.saveRefreshToken(it.refreshToken),
          )
        }
        .blockingAwait()
        // You can use blocking await as the [authenticate] method is
        // run on I/O thread anyway.
    }

    // Load recently refreshed access token.
    val token = (...)
    
    // Format authorization header value.
    val header = (...)
    
    // Modify and proceed with a request with refreshed access token.
    return response.request.newBuilder()
      .removeHeader(AUTH_HEADER)
      .addHeader(AUTH_HEADER, header)
      .build()
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM