[英]Dagger + Retrofit. Adding auth headers at runtime
我想知道 Dagger 是否有辦法知道它應該在新數據可用時重新創建一個對象。
我所說的實例是我用於改造的請求標頭。 在某些時候(當用戶登錄時)我得到一個令牌,我需要將它添加到改造的標頭中以發出經過身份驗證的請求。 問題是,我留下了相同的未經身份驗證的改造版本。 這是我的注入代碼:
@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.cache(cache).build();
client
.newBuilder()
.addInterceptor(
chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "Application/JSON");
Request request = requestBuilder.build();
return chain.proceed(request);
}).build();
return client;
}
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
@Provides
@Singleton
public NetworkService providesNetworkService(Retrofit retrofit) {
return retrofit.create(NetworkService.class);
}
關於如何使這項工作的任何想法?
我個人創建了一個okhttp3.Interceptor
來為我做這okhttp3.Interceptor
,一旦我擁有所需的令牌,我就會更新它。 它看起來像:
@Singleton
public class MyServiceInterceptor implements Interceptor {
private String sessionToken;
@Inject public MyServiceInterceptor() {
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (request.header(NO_AUTH_HEADER_KEY) == null) {
// needs credentials
if (sessionToken == null) {
throw new RuntimeException("Session token should be defined for auth apis");
} else {
requestBuilder.addHeader("Cookie", sessionToken);
}
}
return chain.proceed(requestBuilder.build());
}
}
在相應的 dagger 組件中,我公開了這個攔截器,以便我可以在需要時設置sessionToken
。
這就是 Jake 在他的演講Make Retrofit Work For You 中談到的一些內容。
請考慮使用@oldergod提到的方法,因為它是“官方”且更好的方法,但不建議使用下面提到的方法,它們可能被視為解決方法。
你有幾個選擇。
okhttp
為您提供Retrofit
實例的組件,創建一個新組件並請求一個新的Retrofit
實例,該實例將使用必要的okhttp
實例進行實例化。SharedPreferences
,創建okHttp
標頭,它將應用從SharedPreferences
讀取的令牌。 如果沒有 - 不發送令牌頭。static volatile String
字段,並執行與步驟 2 中相同的操作。為什么第二個選項不好? 因為在每次請求時,您都會輪詢 SD 卡並從那里獲取數據。
使用 @Inject 構造函數創建自定義 RequestInterceptor
請求攔截器
@Singleton
class
RequestInterceptor @Inject constructor(
private val preferencesHelper: PreferencesHelper,
) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var newRequest: Request = chain.request()
newRequest = newRequest.newBuilder()
.addHeader(
"AccessToken",
preferencesHelper.getAccessTokenFromPreference()
)
.build()
Log.d(
"OkHttp", String.format(
"--> Sending request %s on %s%n%s",
newRequest.url(),
chain.connection(),
newRequest.headers()
)
);
return chain.proceed(newRequest)
}
應用模塊
@Module(includes = [AppUtilityModule::class])
class ApplicationModule(private val application: AppController) {
@Provides
@Singleton
fun provideApplicationContext(): Context = application
@Singleton
@Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application.applicationContext)
}
首選項助手
@Singleton
class PreferencesHelper
@Inject constructor(
private val context: Context,
private val sharedPreferences: SharedPreferences
) {
private val PREF_KEY_ACCESS_TOKEN = "PREF_KEY_ACCESS_TOKEN"
fun getAccessTokenFromPreference(): String? {
return sharedPreferences.getString(PREF_KEY_ACCESS_TOKEN, null)
}
}
基於@oldergod 解決方案 kotlin 版本,具有不同的類和結構
像這樣制作 Retrofit 實例
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
添加如下ServiceInterceptor
類
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
登錄接口和數據類實現
interface Login {
@POST("Login")
@Headers("No-Authentication: true")
fun login(@Body value: LoginModel): Call<LoginResponseModel>
@POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
在任何這樣的活動中調用它
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})
經過良好測試和工作
public OkHttpClient getHttpClient(Context context) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.callTimeout(60,TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.addInterceptor(logging)
.addInterceptor(chain -> {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + Utility.getSharedPreferencesString(context, API.AUTHORIZATION))
.build();
return chain.proceed(newRequest);
})
.build();
}
早些時候我想知道,如果會話過期並且用戶再次登錄,這個攔截器會替換現有的身份驗證,但幸運的是它工作正常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.