Retrofit 2 - Элегантный способ добавления заголовков на уровне api
My 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
, должен ли этот конкретный запрос иметь определенный заголовок.
Любая идея, как я могу сделать эту работу?
Я предпочитаю универсальное решение, а не только для токена аутентификации, но также приветствуется конкретное решение.
Благодаря
Ответы
Ответ 1
Я придумал очень простой и элегантный (на мой взгляд) решение моей проблемы и, возможно, для других сценариев.
Я использую аннотацию 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);
}
Затем я могу перехватить запрос, проверить, есть ли у меня аннотация с именем @
. Если это так, я получаю значение и удаляю заголовок из запроса.
Это хорошо работает, даже если вы хотите иметь более одной "пользовательской аннотации":
@HEADERS({
"@: NoAuth",
"@: LogResponseCode"
})
Здесь, как извлечь все эти "пользовательские аннотации" и удалить их из запроса:
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);
}
});
Ответ 2
Возможно, вы можете сделать это, создав другой способ → .
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 без заголовка auth, вы можете просто вызвать метод createService:
YourApi api = RestClient.createService(YourApi.class);
И используйте метод createServiceWithAuth, если вы хотите вызвать api с аутентификацией:
YourApiWithAuth api = RestClient.createServiceWithAuth(YourApiWithAuth.class);