Retrofit 2 - 在api级别添加标题的优雅方式

时间:2016-06-10 21:39:36

标签: java header annotations retrofit retrofit2

我的Retrofit 2(当前2.0.2)客户端需要为请求添加自定义标头。

我使用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();

我总是想添加一些标头,但是我只需要根据特定端点的要求添加一些标头,例如用户是否需要进行身份验证。

我希望能够在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注释的请求将具有令牌标头。

根据我的理解,Retrofit 2不支持自定义注释,虽然我发现Custom Annotations with Retrofit 2的这种解决方法,但它看起来有点太多了。

我希望避免为每个请求传递这些标头,例如:

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

每次调用方法而不是在拦截器中执行此操作时都会感到多余(因为我可以静态访问标头值)。
我只是需要在我的Interceptor.intercept实现中知道这个特定请求是否应该有一个特定的标题。

知道如何才能做到这一点吗? 我更喜欢通用解决方案而不仅仅是auth令牌案例,但也欢迎特定的解决方案。 感谢

2 个答案:

答案 0 :(得分:27)

我提出了一个非常简单和优雅(在我看来)解决我的问题的方法,可能还有其他方案。

我使用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);
}

然后我可以拦截请求,检查我是否有名称为@的注释。如果是这样,我得到值并从请求中删除标题 即使您希望拥有多个自定义注释&#34;:

,这也可以正常工作
@HEADERS({
    "@: NoAuth",
    "@: LogResponseCode"
})

以下是如何提取所有这些&#34;自定义注释&#34;并从请求中删除它们:

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

答案 1 :(得分:4)

也许你可以通过创建不同的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);
    }
}

如果你想在没有头部认证的情况下调用api,你可以调用createService方法:

YourApi api = RestClient.createService(YourApi.class);

如果要使用身份验证调用api,请使用createServiceWithAuth方法:

YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class);