Как динамически устанавливать заголовки в Retrofit (Android)
Я использую API, который использует схему авторизации, для которой требуется установить специальный заголовок "X-Authorization" для проверки подлинности запроса. Например, эта настройка 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
Любые идеи о том, как я мог обойти это?
Ответы
Ответ 1
Начиная с Retrofit 2.0 у вас есть два варианта
1) Используя OkHttp 2. 2+ используйте Interceptor
На уровне Http у вас есть больший контроль над запросом, поэтому вы можете делать такие вещи, как применение заголовков только к конкретному запросу, сделанному к определенной конечной точке, и так далее.
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 в параметр метода и передайте его как значение при вызове.
Из документов:
// Replaces the header with the the value of its target.
@GET("/")
void foo(@Header("Accept-Language") String lang, Callback<Response> cb);
Параметры заголовка могут быть нулевыми, что исключит их из запроса. Передача списка или массива приведет к появлению заголовка для каждого ненулевого элемента.
Примечание. Заголовки не перезаписывают друг друга. Все заголовки с одинаковым именем будут включены в запрос.
РЕДАКТИРОВАТЬ: эта опция не должна рассматриваться как 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);
"Проблема" в этом подходе заключается в том, что перехватчик будет выполняться на всех конечных точках, как это установлено на уровне RestAdapter, а не для каждой конечной точки. Кроме того, RequestFacade
не предоставляет много информации о запросе, поэтому нет шансов добавить много логики вокруг него.
Ответ 2
Передача заголовка в параметре будет полезной. Посмотрите на следующий код;
@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());
Ответ 3
Динамический заголовок в модификации 2 https://medium.com/@manoj.cs.iu/dynamic-header-in-retrofit-2-7b1324a9339f
Ответ 4
Динамический заголовок в модификации 2
Я слишком много пытался добавить динамический заголовок в Retrofit 2.
Я прошел через очень много блогов и StackOver потока. Каждый показал пример с Interceptor.
И это не мудро, просто для одного вызова API нам нужно проделать такую большую работу.
Вам просто нужно добавить @HeaderMap в качестве аргумента веселья. Я сделал очень простым способом:
В котлине
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. Вы можете заменить все, что вам нужно передать, или вы можете удалить это тоже.
На яве
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. Вы можете заменить все, что вам нужно передать, или вы можете удалить это тоже.