Java Retrofit 2 - 在 api 级别添加标题的优雅方式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37757520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Retrofit 2 - Elegant way of adding headers in the api level
提问by Nitzan Tomer
My Retrofit 2 (2.0.2
currently) client needs to add custom headers to requests.
我的 Retrofit 2(2.0.2
当前)客户端需要向请求添加自定义标头。
I'm using an Interceptor
to add these headers to all requests:
我正在使用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();
Some headers I always want to add, but some headers I only need to add based on requirements of that specific endpoint, for example whether the user needs to be authenticated or not.
我总是想添加一些标头,但我只需要根据该特定端点的要求添加一些标头,例如用户是否需要进行身份验证。
I'd like to have the ability to control that at the api level, for example using an annotation, something like:
我希望能够在 api 级别控制它,例如使用注释,例如:
public interface MyApi {
@NO_AUTH
@POST("register")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
When sending a request to register
there's no need to add the authentication token, but requests who lack the @NO_AUTH
annotation will have the token header.
向 发送请求时register
无需添加身份验证令牌,但缺少@NO_AUTH
注释的请求将具有令牌标头。
From what I understand Retrofit 2 doesn't support custom annotations, and while I found this workaround for Custom Annotations with Retrofit 2, it's seems a bit too much.
据我了解,Retrofit 2 不支持自定义注释,虽然我发现了使用 Retrofit 2 的自定义注释的解决方法,但似乎有点太多了。
I'd like to avoid the need to pass these headers per request, like:
我想避免需要为每个请求传递这些标头,例如:
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);
}
It just feels redundant to do it every time I call the method instead of doing it in the interceptor (since I have access to the header values statically).
I just somehow need to know in my Interceptor.intercept
implementation whether or not this specific request should have a specific header(s).
每次我调用方法而不是在拦截器中执行它时都感觉是多余的(因为我可以静态访问标头值)。
我只是需要在我的Interceptor.intercept
实现中以某种方式知道这个特定的请求是否应该有一个特定的标头。
Any idea how I can make this work?
I prefer a generic solution and not just for the auth token case, but a specific solution is welcome as well.
Thanks
知道我怎样才能使这项工作?
我更喜欢通用解决方案,而不仅仅是针对身份验证令牌的情况,但也欢迎使用特定的解决方案。谢谢
采纳答案by Nitzan Tomer
I came up with a very simple and elegant (in my opinion) solution to my problem, and probably for other scenarios.
我想出了一个非常简单和优雅(在我看来)的解决方案来解决我的问题,并且可能适用于其他场景。
I use the Headers
annotation to pass my custom annotations, and since OkHttp requires that they follow the Name: Value
format, I decided that my format will be: @: ANNOTATION_NAME
.
我使用Headers
注释来传递我的自定义注释,并且由于 OkHttp 要求它们遵循Name: Value
格式,因此我决定我的格式为:@: ANNOTATION_NAME
.
So basically:
所以基本上:
public interface MyApi {
@POST("register")
@HEADERS("@: NoAuth")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
Then I can intercept the request, check whether I have an annotation with name @
. If so, I get the value and remove the header from the request.
This works well even if you want to have more than one "custom annotation":
然后我可以拦截请求,检查我是否有 name 的注释@
。如果是这样,我会获取该值并从请求中删除标头。
即使您想拥有多个“自定义注释”,这也很有效:
@HEADERS({
"@: NoAuth",
"@: LogResponseCode"
})
Here's how to extract all of these "custom annotations" and remove them from the request:
以下是提取所有这些“自定义注释”并将它们从请求中删除的方法:
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);
}
});
回答by ikhsanudinhakim
Maybe you can do that by creating different Retrofit object factory method like this.
也许你可以通过像这样创建不同的 Retrofit 对象工厂方法来做到这一点。
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);
}
}
if you want to call api without header auth, you can just call createService method:
如果你想在没有头认证的情况下调用 api,你可以调用 createService 方法:
YourApi api = RestClient.createService(YourApi.class);
And use createServiceWithAuth method if you want to call api with authentication:
如果要使用身份验证调用 api,请使用 createServiceWithAuth 方法:
YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class);