如何在Retrofit(Android)中动态设置标头

joh*_*ser 54 android retrofit

我使用的API使用授权方案,该方案需要设置特殊的"X-Authorization"标头来验证请求.例如,此Retrofit设置适用于其身份验证令牌为的用户abc123:

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

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

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

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

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

Rob*_*ill 86

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


1)使用OkHttp 2.2+使用拦截器

在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);
Run Code Online (Sandbox Code Playgroud)

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

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

来自文档:

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

标头参数可以为null,这将从请求中省略它们.传递List或数组将导致每个非null项的标头.

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


编辑:此选项不应被视为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);
Run Code Online (Sandbox Code Playgroud)

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


use*_*335 14

Retrofit 2 中的动态标题

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

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

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

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

在科特林

    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>
Run Code Online (Sandbox Code Playgroud)

这里的第二个参数我有 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);
Run Code Online (Sandbox Code Playgroud)

这里的第二个参数我有 JsonObject。你可以用任何你需要传递的东西替换它,或者你也可以删除它。


Irf*_*Haq 13

在参数中传递标题会很有帮助.查看以下代码;

 @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());
Run Code Online (Sandbox Code Playgroud)


小智 7

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

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
Run Code Online (Sandbox Code Playgroud)