2016-06-10 13 views
11

Mein Retrofit 2 (2.0.2 derzeit) Client muss benutzerdefinierte Header zu Anforderungen hinzufügen.Retrofit 2 - Elegante Möglichkeit zum Hinzufügen von Kopfzeilen in der API-Ebene

Ich verwende eine Interceptor diese Header auf alle Anfragen hinzuzufügen:

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

Einige Header Ich will immer hinzufügen, aber einige Header ich nur basierend auf den Anforderungen dieses spezifischen Endpunkt hinzufügen müssen, für Beispiel, ob der Benutzer authentifiziert werden muss oder nicht.

Ich möchte in der Lage sein, dass zum Beispiel auf API-Ebene zu steuern, eine Anmerkung mit, so etwas wie:

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

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

Wenn eine Anfrage an register dort sendet keine Notwendigkeit, die Authentifizierungs-Token hinzufügen , aber Anfragen, denen die @NO_AUTH Annotation fehlt, haben den Token-Header.

Von dem, was ich verstehe Retrofit 2 unterstützt benutzerdefinierte Anmerkungen nicht, und während ich diese Problemumgehung für Custom Annotations with Retrofit 2 fand, scheint es ein bisschen zu viel.

Ich möchte die Notwendigkeit zu vermeiden, diese Header pro Anforderung übergeben, wie:

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

Es fühlt sich einfach überflüssig, es zu tun, jedesmal wenn ich die Methode aufrufen, anstatt sie in die Abfangjäger tun (da Ich habe Zugriff auf die Header-Werte statisch).
Ich muss nur irgendwie in meiner Interceptor.intercept Implementierung wissen, ob diese spezifische Anfrage einen bestimmten Header haben sollte oder nicht.

Irgendeine Idee, wie ich das schaffen kann?
Ich bevorzuge eine generische Lösung und nicht nur für den Auth-Token-Fall, aber eine spezifische Lösung ist auch willkommen. Danke

Antwort

22

Ich habe eine sehr einfache und elegante (meiner Meinung nach) Lösung für mein Problem und wahrscheinlich für andere Szenarien gefunden.

Ich verwende die Headers Annotation meine benutzerdefinierten Anmerkungen zu übergeben, und da OkHttp verlangt, dass sie das Name: Value Format folgen, entschied ich mich, dass mein Format sein wird: @: ANNOTATION_NAME.

Also im Grunde:

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

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

Dann kann ich die Anforderung abfangen, ob ich @ eine Anmerkung mit Namen haben. Wenn ja, bekomme ich den Wert und entferne die Kopfzeile von der Anfrage.
Das funktioniert gut, auch wenn Sie mehr als eine „benutzerdefinierte Anmerkung“ haben will:

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

Hier ist, wie alle dieser „individuellen Anmerkungen“ zu extrahieren und entfernen sie aus der Anfrage:

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

Bedeutet das, dass Sie für jede Anfrage einen neuen '' 'OKHttpClient''' verwenden? –

+1

@panduka Nein. Das war nur für das Beispiel, Sie können einen Client haben. –

+0

Dies ist die sauberste Lösung tatsächlich, danke für die Weitergabe :) – MatPag

4

Vielleicht können Sie das tun, indem Sie verschiedene Retrofit Objekt Factory-Methode wie folgt erstellen.

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

wenn Sie api ohne Header Auth anrufen möchten, können Sie einfach Create Methode aufrufen:

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

Und verwenden createServiceWithAuth Methode, wenn Sie api mit Authentifizierung anrufen möchten:

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

Danke, das ist eine gute Lösung, aber ich muss die Endpunkte in verschiedenen Klassen gruppieren, je nachdem, ob die Anfragen authentifiziert werden müssen oder nicht, und das ist nicht sehr praktisch. –