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 静态)。
我正在使用 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 静态)。