【问题标题】:How to dynamically set headers in Retrofit (Android)如何在 Retrofit (Android) 中动态设置标题
【发布时间】:2015-07-05 06:29:00
【问题描述】:

我正在使用一个使用授权方案的 API,该方案需要设置一个特殊的“X-Authorization”标头来对请求进行身份验证。例如,此 Retrofit 设置非常适合身份验证令牌为 abc123 的用户:

@Headers("X-Authorization: abc123")
@GET("/posts")
Observable<List<Post>> get_posts();

我缓存了用户的 X-Authorization 令牌,所以我可以访问它,但是,我不能将它放在 @Headers 声明中。

@Headers("X-Authorization: " + token)
@GET("/posts")
Observable<List<Post>> get_posts();

我在这里得到一个编译错误:Error:(41, 34) error: element value must be a constant expression

关于如何解决这个问题的任何想法?

【问题讨论】:

  • 是的,这部分 [(@Headers("X-Authorization: " + token)] 在编译时评估 IIRC。所以它不会动态设置令牌

标签: android retrofit


【解决方案1】:

从 Retrofit 2.0 开始,您有两个选择


1) 使用 OkHttp 2.2+ 使用Interceptor

在 Http 级别,您可以更好地控制请求,因此您可以执行一些操作,例如仅将标头应用于对特定端点发出的特定请求,等等。

public class MyOkHttpInterceptor implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
    Request originalRequest = chain.request();
    if (!"/posts".contains(originalRequest.url()) ) {
        return chain.proceed(originalRequest);
    }

    String token = // get token logic 

    Request newRequest = originalRequest.newBuilder()
        .header("X-Authorization", token)
        .build();

    return chain.proceed(newRequest);
}

[...]

OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.networkInterceptors().add(new MyOkHttpInterceptor());
OkClient okClient = new OkClient(okHttpClient);
YourApi api = new RestAdapter.Builder()
            .setEndpoint(url)
            .setClient(okClient)
            .build()
            .create(YourApi.class);

编辑: 添加@JakeWarthon 注释作为另一个选项也是有效的。

2) 将@Header 放在方法参数上,并在调用时将其作为值传递。

来自docs

// Replaces the header with the the value of its target.
@GET("/")
void foo(@Header("Accept-Language") String lang, Callback<Response> cb);

标头参数可能为空,这将在请求中忽略它们。传递列表或数组将为每个非空项生成一个标头。

注意:标题不会相互覆盖。所有具有相同名称的标头都将包含在请求中。


编辑:此选项不应被视为 Retrofit 2.* 放弃了对拦截器的支持。

3) 用户改造 RequestInterceptor

来自文档: 在执行之前拦截每个请求以添加额外数据。

你可以做类似的事情

public class MyRetrofitInterceptor implements RequestInterceptor {

@Override
public void intercept(RequestFacade req) {
    String token = // get token logic 
    if (token != null) {
        req.addHeader("X-Authorization", token);
    }
}

[...]

YourApi api = new RestAdapter.Builder()
            .setEndpoint(url)
            .setRequestInterceptor(new MyRetrofitInterceptor())
            .build()
            .create(YourApi.class);

这种方法的“问题”是拦截器将在所有端点上执行,因为它是在 RestAdapter 级别设置的,而不是每个端点。此外,RequestFacade 没有公开太多有关请求的信息,因此没有机会在其周围添加太多逻辑。

【讨论】:

  • 我不知道@Header 可以作为参数包含在内。我在拦截器上苦苦挣扎,因为我的 Apiclient 类是单例,添加拦截器需要我打破单例模式。很高兴我找到了这个答案。
【解决方案2】:

在参数中传递标头会很有帮助。看下面的代码;

 @GET("/posts")
Observable<JsonElement> getDataFromService(
        @HeaderMap Map<String, String> headers,
        @QueryMap HashMap<String, Object> queryParams
);

        hashMap1.put("Authorization", token);
    return ApiService.getAPI_test().getDataFromService(hashMap1, url, hashMap)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io());

更新:

更好的是

 @GET("/posts")
Observable<JsonElement> getDataFromService(
        @Header("Authorization") token: String = "Bearer " + PreferenceUtils.getToken(),
        @QueryMap HashMap<String, Object> queryParams
);

【讨论】:

    【解决方案3】:

    Retrofit 2 中的动态标题

    在 Retrofit 2 中添加动态标题我费了很大劲。

    我浏览了很多博客和 StackOver 流程​​。每个人都展示了 Interceptor 的示例。

    而且这不是一件明智的事情,仅仅为了一个 API 调用我们就需要做这么多的工作。

    您只需添加 @HeaderMap 作为有趣的参数。我以非常简单的方式完成了:-

    在 Kotlin 中

        val headers = HashMap<String, String>()
        headers["KEY_AUTHORIZATION"] = "paste AUTHORIZATION value here"
        headers["KEY_TOKEN"] = "paste TOKEN value here"
    
        val jsonObject= JsonObject()
    
    I am passing here header and other data also
    Calling of fun:-
    
    postEvent(headers,jsonObject)
    
    API Declaration 
    
        @POST("/v1/post_data")
        fun postEvent(@HeaderMap headers: Map<String, String>, @Body jsonObject: JsonObject): Call<JsonObject>
    
    API Declaration with RxAndroid
    
        @POST("/v1/post_data")
        fun postEvent(@HeaderMap headers: Map<String, String>, @Body jsonObject: JsonObject): Single<JsonObject>
    

    这里的第二个参数我有 JsonObject。您可以替换为任何您需要传递的内容,也可以将其删除。

    在 Java 中

     HashMap<String, String> headers = new HashMap<String, String>();
        headers.put("KEY_AUTHORIZATION","paste AUTHORIZATION value here");
        headers.put("KEY_TOKEN", "paste TOKEN value here");
    
        JsonObject jsonObject= new JsonObject();
    
    I am passing here header and other data also
    
    Calling of fun:-
    postEvent(headers,jsonObject);
    
        API Declaration 
        @POST("/v1/post_data")
        Call<JsonObject> postEvent(@HeaderMap Map<String, String> headers, @Body JsonObject jsonObject);
    
    API Declaration with RxAndroid
    
        @POST("/v1/post_data")
        Single<JsonObject> postEvent(@HeaderMap Map<String, String> headers, @Body JsonObject jsonObject);
    

    这里的第二个参数我有 JsonObject。您可以替换为任何您需要传递的内容,也可以将其删除。

    【讨论】:

      【解决方案4】:

      可以使用@Header 注解动态更新请求标头。必须为@Header 提供相应的参数。如果值为 null,则将省略标头。否则,将对值调用 toString,并使用结果。

      @GET("user")
      Call<User> getUser(@Header("Authorization") String authorization)
      

      【讨论】:

        【解决方案5】:

        当这个答案的最后一部分 How to dynamically set headers in Retrofit (Android) 对我不起作用(项目进行到一半),我对其进行了改进:-

        public class MyRetrofitInterceptor implements RequestInterceptor {
        // volatile variable
        public static String token = null; //change at start of app
        @Override
        public void intercept(RequestFacade req) {
            // change token from outside the class.
            if (token != null) {
                req.addHeader("X-Authorization", token);
            }
        }
        

        从服务器 API 的响应中更新令牌后,它就开始工作了。

        我认为它作为字符串变量'token'被用作对其值的引用,在全局术语中(是公共静态的)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-25
          • 2023-03-31
          • 2016-09-06
          • 1970-01-01
          • 2014-09-23
          相关资源
          最近更新 更多