2016-06-10 42 views
11

My Retrofit 2 (2.0.2 attualmente) il client deve aggiungere intestazioni personalizzate alle richieste.Retrofit 2: un modo elegante per aggiungere intestazioni a livello di API

sto usando un Interceptor per aggiungere queste intestazioni a tutte le richieste:

OkHttpClient httpClient = new OkHttpClient(); 
httpClient.networkInterceptors().add(new Interceptor() { 
    @Override 
    public Response intercept(Chain chain) throws IOException { 
     final Request request = chain.request().newBuilder() 
       .addHeader("CUSTOM_HEADER_NAME_1", "CUSTOM_HEADER_VALUE_1") 
       .addHeader("CUSTOM_HEADER_NAME_2", "CUSTOM_HEADER_VALUE_2") 
       ... 
       .addHeader("CUSTOM_HEADER_NAME_N", "CUSTOM_HEADER_VALUE_N") 
       .build(); 

     return chain.proceed(request); 
    } 
}); 


Retrofit retrofitClient = new Retrofit.Builder() 
     .baseUrl(baseUrl) 
     .client(httpClient) 
     .build(); 

Alcune intestazioni ho sempre voglia di aggiungere, ma alcune intestazioni Ho solo bisogno di aggiungere in base ai requisiti di quello specifico endpoint, per esempio se l'utente deve essere autenticato o meno.

Mi piacerebbe avere la possibilità di controllare che a livello di API, ad esempio utilizzando un'annotazione, qualcosa di simile:

public interface MyApi { 
    @NO_AUTH 
    @POST("register") 
    Call<RegisterResponse> register(@Body RegisterRequest data); 

    @GET("user/{userId}") 
    Call<GetUserResponse> getUser(@Path("userId") String userId); 
} 

Quando si invia una richiesta di register non c'è bisogno di aggiungere il token di autenticazione , ma richiede a chi manca l'annotazione @NO_AUTH l'intestazione del token.

Da quello che ho capito Retrofit 2 non supporta annotazioni personalizzate e mentre ho trovato questa soluzione alternativa per Custom Annotations with Retrofit 2, sembra un po 'troppo.

vorrei evitare la necessità di superare queste intestazioni per ogni richiesta, come:

public interface MyApi { 
    @POST("register") 
    Call<RegisterResponse> register(@Body RegisterRequest data); 

    @GET("user/{userId}") 
    Call<GetUserResponse> getUser(@Header("AuthToken") String token, @Path("userId") String userId); 
} 

Si sente solo ridondante di farlo ogni volta che chiamo il metodo invece di farlo nel intercettore (dal Ho accesso ai valori di intestazione staticamente).
Ho solo in qualche modo bisogno di sapere nella mia implementazione Interceptor.intercept se questa specifica richiesta dovrebbe avere o meno una (e) intestazione specifica.

Qualche idea su come posso farlo funzionare?
Preferisco una soluzione generica e non solo per il caso del token auth, ma è anche benvenuta una soluzione specifica. Grazie

risposta

22

Ho trovato una soluzione molto semplice ed elegante (a mio avviso) per il mio problema e probabilmente per altri scenari.

Io uso l'annotazione Headers per passare le mie annotazioni personalizzate e poiché OkHttp richiede che seguano il formato Name: Value, ho deciso che il mio formato sarà: @: ANNOTATION_NAME.

Quindi, fondamentalmente:

public interface MyApi { 
    @POST("register") 
    @HEADERS("@: NoAuth") 
    Call<RegisterResponse> register(@Body RegisterRequest data); 

    @GET("user/{userId}") 
    Call<GetUserResponse> getUser(@Path("userId") String userId); 
} 

allora posso intercettare la richiesta, verificare se ho un'annotazione con il nome @. In tal caso, ottengo il valore e rimuovo l'intestazione dalla richiesta.
Questo metodo funziona bene anche se si vuole avere più di una "annotazione su misura":

@HEADERS({ 
    "@: NoAuth", 
    "@: LogResponseCode" 
}) 

Ecco come estrarre tutti questi "annotazioni personalizzate" e rimuoverli dalla richiesta:

new OkHttpClient.Builder().addNetworkInterceptor(new Interceptor() { 
    @Override 
    public okhttp3.Response intercept(Chain chain) throws IOException { 
     Request request = chain.request(); 

     List<String> customAnnotations = request.headers().values("@"); 

     // do something with the "custom annotations" 

     request = request.newBuilder().removeHeader("@").build(); 
     return chain.proceed(request); 
    } 
}); 
+0

Significa che stai usando il nuovo '' 'OkHttpClient''' per ogni richiesta? –

+1

@panduka No. Questo era solo per l'esempio, puoi avere un cliente. –

+0

Questa è la soluzione più pulita in realtà, grazie per la condivisione :) – MatPag

4

Forse è possibile farlo creando un diverso metodo di produzione di oggetti Retrofit come questo.

public class RestClient { 
    public static <S> S createService(Class<S> serviceClass) { 
     OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); 
     OkHttpClient client = httpClient.build(); 

     Retrofit retrofit = new Retrofit.Builder().baseUrl(APIConfig.BASE_URL) 
       .client(client) 
       .build(); 
     return retrofit.create(serviceClass); 
    } 

    public static <S> S createServiceWithAuth(Class<S> serviceClass) { 
     Interceptor interceptor = new Interceptor() { 
      @Override 
      public Response intercept(Chain chain) throws IOException { 
       final Request request = chain.request().newBuilder() 
         .addHeader("CUSTOM_HEADER_NAME_1", "CUSTOM_HEADER_VALUE_1") 
         .addHeader("CUSTOM_HEADER_NAME_2", "CUSTOM_HEADER_VALUE_2") 
         .build(); 

       return chain.proceed(request); 
      } 
     }; 
     OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); 
     httpClient.addInterceptor(interceptor); 
     OkHttpClient client = httpClient.build(); 

     Retrofit retrofit = new Retrofit.Builder().baseUrl(APIConfig.BASE_URL) 
       .client(client) 
       .build(); 
     return retrofit.create(serviceClass); 
    } 
} 

se si desidera chiamare API senza intestazione autenticazione, si può semplicemente chiamare il metodo CreateService:

YourApi api = RestClient.createService(YourApi.class); 

E utilizzare il metodo createServiceWithAuth se si desidera chiamare API con l'autenticazione:

YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class); 
+0

Grazie, è una buona soluzione, ma mi richiede di raggruppare gli endpoint in classi diverse in base al fatto che le richieste debbano essere autenticate o meno, e non è molto conveniente. –

Problemi correlati