web-dev-qa-db-fra.com

Dague + Retrofit. Ajout d'en-têtes d'authentification au moment de l'exécution

Je me demande s'il existe un moyen pour Dagger de savoir qu'il devrait recréer un objet lorsque de nouvelles données sont disponibles.

Le cas dont je parle concerne les en-têtes de demande de modification. À un moment donné (lorsque l'utilisateur se connecte), j'obtiens un jeton que je dois ajouter aux en-têtes de modification pour effectuer des demandes authentifiées. Le problème est que je me retrouve avec la même version non authentifiée de mise à niveau. Voici mon code d'injection:

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

Des idées sur la façon de faire fonctionner cela?

24
AIntel

Veuillez envisager d'utiliser l'approche mentionnée par @ oldgod car c'est le "officiel" et bien mieux, alors que les approches mentionnées ci-dessous ne sont pas déconseillées , elles peuvent être considérées comme des solutions de contournement.


Vous avez plusieurs options.

  1. Dès que vous obtenez le jeton, vous devez annuler le composant qui vous a fourni l'instance Retrofit, créer un nouveau composant et demander une nouvelle instance Retrofit, qui sera instanciée avec le nécessaire okhttp instance.
  2. Rapide et mauvais - Enregistrez le jeton dans SharedPreferences, créez l'en-tête okHttp, qui appliquera la lecture du jeton à partir de SharedPreferences. S'il n'y en a pas - n'envoyez aucun en-tête de jeton.
  3. Solution encore plus laide - déclarer un static volatile String champ, et faites la même chose qu'à l'étape 2.

Pourquoi la deuxième option est mauvaise? Parce qu'à chaque demande, vous seriez en train d'interroger la carte SD et de récupérer des données à partir de là.

9
azizbekian

J'ai personnellement créé un okhttp3.Interceptor qui fait ça pour moi, que je mets à jour une fois que j'ai le jeton requis. Cela ressemble à quelque chose comme:

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

Dans le composant poignard correspondant, j'expose cet intercepteur afin que je puisse définir le sessionToken quand j'en ai besoin.

C'est quelque chose dont Jake en a parlé dans son discours Faire fonctionner la rénovation pour vous .

33
oldergod

Basé sur la version @oldergod de la solution kotlin avec différentes classes et structure

Faire une instance de Retrofit comme celle-ci

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
      }

}

Ajoutez la classe ServiceInterceptor comme ci-dessous

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 de connexion et implémentation de la classe de données

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)

appeler cela dans une activité comme celle-ci

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
                }
            }

        })
0
Faraz Ahmed