Retrofit 2 - 在 api 级别添加 headers 的优雅方式
Retrofit 2 - Elegant way of adding headers in the api level
我的 Retrofit 2(2.0.2
当前)客户端需要向请求添加自定义 header。
我正在使用 Interceptor
将这些 header 添加到所有请求中:
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();
有些header我一直想添加,但有些header我只需要根据特定端点的要求添加,例如用户是否需要进行身份验证.
我希望能够在 api 级别控制它,例如使用注释,例如:
public interface MyApi {
@NO_AUTH
@POST("register")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
向 register
发送请求时无需添加身份验证令牌,但缺少 @NO_AUTH
注释的请求将具有令牌 header.
据我了解,Retrofit 2 不支持自定义注释,虽然我找到了 Custom Annotations with Retrofit 2 的解决方法,但似乎有点太多了。
我想避免在每个请求中传递这些 header,例如:
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);
}
每次我调用该方法而不是在拦截器中执行它时感觉是多余的(因为我可以静态访问 header 值)。
我只是需要在我的 Interceptor.intercept
实现中知道这个特定请求是否应该有一个特定的 header(s).
知道如何让这项工作成功吗?
我更喜欢通用解决方案,而不仅仅是针对身份验证令牌的情况,但也欢迎使用特定的解决方案。
谢谢
也许您可以像这样创建不同的 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);
}
}
如果你想在没有header auth的情况下调用api,你可以只调用createService方法:
YourApi api = RestClient.createService(YourApi.class);
如果您想调用 api 进行身份验证,请使用 createServiceWithAuth 方法:
YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class);
我想出了一个非常简单而优雅(在我看来)的解决方案来解决我的问题,并且可能适用于其他情况。
我使用 Headers
注释来传递我的自定义注释,由于 OkHttp 要求它们遵循 Name: Value
格式,我决定我的格式为:@: ANNOTATION_NAME
.
所以基本上:
public interface MyApi {
@POST("register")
@HEADERS("@: NoAuth")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
然后我可以拦截请求,检查我是否有名称为@
的注解。如果是这样,我获取该值并从请求中删除 header。
即使你想拥有多个 "custom annotation":
@HEADERS({
"@: NoAuth",
"@: LogResponseCode"
})
以下是提取所有这些 "custom annotations" 并将它们从请求中删除的方法:
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);
}
});
您也可以像 this article
那样做
我添加了一个小的额外扩展来缩短所有内容
fun Request.isAnnotationPresent(annotation: Class<out Annotation>) =
tag(Invocation::class.java)?.method()?.isAnnotationPresent(annotation) ?: false
private class CustomInterceptor(private val account: Account): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {chain.request().tag(Invocation::class.java)?.method()
if(chain.request().isAnnotationPresent(CustomAnnotation)) {
// Do your stuff
}
return chain.proceed(chain.request())
}
}
我的 Retrofit 2(2.0.2
当前)客户端需要向请求添加自定义 header。
我正在使用 Interceptor
将这些 header 添加到所有请求中:
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();
有些header我一直想添加,但有些header我只需要根据特定端点的要求添加,例如用户是否需要进行身份验证.
我希望能够在 api 级别控制它,例如使用注释,例如:
public interface MyApi {
@NO_AUTH
@POST("register")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
向 register
发送请求时无需添加身份验证令牌,但缺少 @NO_AUTH
注释的请求将具有令牌 header.
据我了解,Retrofit 2 不支持自定义注释,虽然我找到了 Custom Annotations with Retrofit 2 的解决方法,但似乎有点太多了。
我想避免在每个请求中传递这些 header,例如:
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);
}
每次我调用该方法而不是在拦截器中执行它时感觉是多余的(因为我可以静态访问 header 值)。
我只是需要在我的 Interceptor.intercept
实现中知道这个特定请求是否应该有一个特定的 header(s).
知道如何让这项工作成功吗?
我更喜欢通用解决方案,而不仅仅是针对身份验证令牌的情况,但也欢迎使用特定的解决方案。
谢谢
也许您可以像这样创建不同的 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);
}
}
如果你想在没有header auth的情况下调用api,你可以只调用createService方法:
YourApi api = RestClient.createService(YourApi.class);
如果您想调用 api 进行身份验证,请使用 createServiceWithAuth 方法:
YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class);
我想出了一个非常简单而优雅(在我看来)的解决方案来解决我的问题,并且可能适用于其他情况。
我使用 Headers
注释来传递我的自定义注释,由于 OkHttp 要求它们遵循 Name: Value
格式,我决定我的格式为:@: ANNOTATION_NAME
.
所以基本上:
public interface MyApi {
@POST("register")
@HEADERS("@: NoAuth")
Call<RegisterResponse> register(@Body RegisterRequest data);
@GET("user/{userId}")
Call<GetUserResponse> getUser(@Path("userId") String userId);
}
然后我可以拦截请求,检查我是否有名称为@
的注解。如果是这样,我获取该值并从请求中删除 header。
即使你想拥有多个 "custom annotation":
@HEADERS({
"@: NoAuth",
"@: LogResponseCode"
})
以下是提取所有这些 "custom annotations" 并将它们从请求中删除的方法:
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);
}
});
您也可以像 this article
那样做我添加了一个小的额外扩展来缩短所有内容
fun Request.isAnnotationPresent(annotation: Class<out Annotation>) =
tag(Invocation::class.java)?.method()?.isAnnotationPresent(annotation) ?: false
private class CustomInterceptor(private val account: Account): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {chain.request().tag(Invocation::class.java)?.method()
if(chain.request().isAnnotationPresent(CustomAnnotation)) {
// Do your stuff
}
return chain.proceed(chain.request())
}
}