2016-06-10 36 views
11

我改造2(2.0.2當前)客戶需要定製標題添加到請求。改造2 - 在API級將報頭的優雅的方式

我使用的是Interceptor這些頭文件添加到所有請求:

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

一些標題我總是要添加,但一些頭我只需要根據特定端點的需求增加,對例如用戶是否需要被認證。

我想使用的註釋有控制,在API級別的能力,例如,像這樣:

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

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

發送請求時,以register沒有必要添加的認證令牌,但缺少@NO_AUTH註釋的請求將具有令牌標頭。

根據我的理解,Retrofit 2不支持自定義註釋,雖然我發現此替代方法適用於Custom Annotations with Retrofit 2,但它似乎有點太多。

我想避免需要通過每個請求這些標題,如:

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

就覺得這是多餘的我每次調用該方法,而不是在攔截器做的時間來做到這一點(因爲我可以靜態訪問標題值)。
我不知怎麼竟需要在我Interceptor.intercept實施知道這個具體的要求是否應該有一個具體的物管。

任何想法如何使這項工作?
我喜歡一個通用的解決方案,而不是僅僅爲驗證令牌的情況下,但具體的解決方案是值得歡迎的爲好。 感謝

回答

22

我想出了一個非常簡單而優雅的(在我看來)解決我的問題,並可能對其他方案。

我用Headers註釋通過我的自定義註釋,並自OkHttp要求他們遵循Name: Value格式,我決定,我的格式爲:@: ANNOTATION_NAME

所以基本上:

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

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

然後,我可以攔截請求,檢查自己是否有名稱@註解。如果是這樣,我得到值並從請求中刪除標題。
這種運作良好,即使你想有一個以上的「自定義註解」:

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

以下是如何提取所有的這些「定製註釋」,並從請求刪除:

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

這是否意味着您對每個請求都使用新的'''OkHttpClient'''? –

+1

@panduka不,這只是爲了舉例,你可以有一個客戶。 –

+0

這實際上是最乾淨的解決方案,謝謝分享:) – MatPag

4

也許你可以通過創建類似這樣的不同改造對象的工廠方法做到這一點。

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

如果你想調用沒有頭身份驗證API,你可以調用的CreateService方法:

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

如果你要調用的API與身份驗證使用createServiceWithAuth方法:

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

謝謝,這是一個很好的解決方案,但它需要我根據請求是否需要進行身份驗證將不同類中的端點組合在一起,這不是很方便。 –