Java 匕首&x2B;改装。在运行时添加auth头

Java 匕首&x2B;改装。在运行时添加auth头,java,android,retrofit2,dagger-2,dagger,Java,Android,Retrofit2,Dagger 2,Dagger,我想知道Dagger是否有办法知道,当新数据可用时,它应该重新创建一个对象 我所说的例子是关于改装的请求头。在某个时刻(当用户登录时),我会得到一个令牌,我需要将它添加到改造的头中,以发出经过身份验证的请求。问题是,我留下的是相同的未经认证的改装版本。这是我的注入代码: @Provides @Singleton OkHttpClient provideOkHttpClient(Cache cache) { HttpLoggingInterceptor interc

我想知道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);
    }
关于如何使这项工作有任何想法?

< P>请考虑使用<强> > OLDREST < <强> >,因为它是“官方的”和更好的方式,而下面提到的方法是<强>不>强>建议,它们可以被视为解决方案。


你有两个选择

  • 一旦您获得令牌,您就必须清空为您提供
    改型
    实例的组件,创建一个新组件并请求一个新的
    改型
    实例,该实例将通过必要的
    okhttp
    实例进行实例化
  • 一个快速而糟糕的方法-将令牌保存在
    SharedReferences
    中,创建
    okHttp
    头,该头将应用从
    SharedReferences
    读取的令牌。如果没有-不发送令牌头
  • 甚至更丑陋的解决方案-声明一个
    静态易失性字符串
    字段,并执行与步骤2中相同的操作

  • 为什么第二种选择不好?因为在每次请求时,您都会轮询SD卡并从那里获取数据。

    我个人创建了一个
    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在他的演讲中谈到的一些东西。

    基于@oldergod solution kotlin版本,具有不同的类和结构

    这样做改装实例

    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
          }
    
    }
    
    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
                    }
                }
    
            })
    
    添加
    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)
    
    界面登录{
    @发布(“登录”)
    @标题(“无身份验证:true”)
    有趣的登录(@Body value:LoginModel):调用
    @POST(“刷新令牌”)
    乐趣刷新令牌(刷新令牌:字符串):
    呼叫
    }
    数据类登录模型(val电子邮件:字符串,val密码:字符串)
    数据类LoginResponseModel(val标记:字符串,val
    refreshToken:字符串)
    
    在任何类似这样的活动中调用此函数

    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
          }
    
    }
    
    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
                    }
                }
    
            })
    
    val service=RefughtClientInstance.RefughtInstance?.create(Login::class.java)
    val refreshttoken=“yourfreshttoken”
    val call=服务?.refreshToken(refreshToken)
    call?.enqueue(对象:Callback){
    覆盖失效时的乐趣(调用:调用,t:可丢弃){
    打印(“抛出消息”+t.Message)
    makeText(applicationContext,“读取JSON时出错”,Toast.LENGTH\u LONG.show())
    }
    覆盖fun onResponse(调用:调用,响应:响应){
    val body=响应?.body()
    if(body!=null){
    //做你的工作
    }
    }
    })
    
    使用@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)
        }
    
    }
    

    良好的测试和工作

    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();
    
    }
    

    早些时候我想知道,如果会话过期,用户再次登录,这个拦截器是否会替换现有的身份验证,但幸运的是,它工作正常。

    对于选项1。如何请求新的改装实例?假设您有一个提供改装实例的
    FooComponent FooComponent
    。现在您将
    null
    取出该组件并创建一个新的
    FooComponent
    。将您的答案标记为正确,因为我了解并了解您的解决方案将如何工作。我的设置存在的问题是,我向演示者注入了改造,而演示者本身就是在我的视图中注入的。我在我的视图中这样做,其中一些视图有自己的组件,这些组件依赖于我的网络组件。我希望有一个神奇的解决方案,可以强制我的演示者在我的所有视图中重新注入。无需清空组件,因为您可以在成功登录/保存到不需要令牌的登录用户的数据库后将令牌设置到拦截器中@奥尔德戈德的答案应该标记为更正确。杰克的谈话也很有趣。@Davidea,完全同意。在写这篇文章的时候,我还没有看到Jake的演讲。有没有这样的例子。@RachitMishra你想将拦截器暴露在你的应用程序中更新你的令牌的部分,并在你为你的应用程序提供的翻新实例中使用拦截器的这个实例。这样,您就可以从应用程序中的一个点更新整个应用程序的sessionToken。@RachitMishra您能提供一个完整的示例吗?我不知道如何将
    MyServiceInterceptor
    实例使用到我需要的
    模块中Interceptor@matio拦截器已经是可注入的。因此,在函数中,您可以从中提供Okhttp,并将其作为参数包含。并将其作为拦截器添加到OkHttp实例中。@RachitMishra但在我想公开它的
    组件中,它仍然未使用,并给出null异常。如果没有
    @提供
    注释,我们将如何公开?