Retrofit中如何动态设置headers(Android)

How to dynamically set headers in Retrofit (Android)

我正在使用 API,它使用的授权方案需要设置特殊的 "X-Authorization" header 来验证请求。例如,此 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

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

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


1) 使用 OkHttp 2.2+ 使用 Interceptor

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

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

Header 参数可以为 null,这将从请求中省略它们。传递列表或数组将导致每个 non-null 项的 header。

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


编辑: 此选项不应被视为 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);

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

在参数中传递 header 会有所帮助。查看以下代码;

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

动态 Header 改造 2

在 Retrofit 2 中添加 Dynamic Header 太费劲了。

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

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

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

在 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。你可以用任何你需要传递的东西替换,或者你也可以删除它。

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

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

当这个答案的最后一部分 对我不起作用(项目进行到一半),我改进了它:-

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' 被用作对其值的引用,在全局术语中(public 静态)。