Параметризованные @Headers для модернизации Android

85

Я использую OAuth, и мне нужно помещать токен OAuth в свой заголовок каждый раз, когда я делаю запрос. Я вижу @Headerаннотацию, но есть ли способ сделать ее параметризованной, чтобы я мог перейти во время выполнения?

Вот концепция

@Header({Authorization:'OAuth {var}', api_version={var} })

Можете ли вы передать их во время выполнения?

@GET("/users")
void getUsers(
    @Header("Authorization") String auth, 
    @Header("X-Api-Version") String version, 
    Callback<User> callback
)
jpotts18
источник
Вы когда-нибудь понимали это? Мне также нужно передать токен в заголовок
theSociableme
Я также ищу решение этой проблемы, из документации это похоже на аннотацию @Headers () в методе, добавляющую поля в заголовок одно за другим, но поддерживает только литералы. И @Header ("параметр") Аннотация строкового параметра String заменяет заголовок предоставленным значением.
nana
2
То же самое здесь, не мог понять, как обрабатывать сеансы при использовании модернизации.
Пт
Нам не нужно было сдавать все элементы, дооснащение все обрабатывает. Пожалуйста, проверьте ссылку на мой ответ в StackOverflow.
Субин Бабу

Ответы:

98

Помимо использования параметра @Header, я бы предпочел использовать RequestInterceptor для обновления всего вашего запроса без изменения интерфейса. Используя что-то вроде:

RestAdapter.Builder builder = new RestAdapter.Builder()
    .setRequestInterceptor(new RequestInterceptor() {
        @Override
        public void intercept(RequestFacade request) {
            request.addHeader("Accept", "application/json;versions=1");
            if (isUserLoggedIn()) {
                request.addHeader("Authorization", getToken());
            }                    
        }
    });

p / s: Если вы используете Retrofit2, вы должны использовать InterceptorвместоRequestInterceptor

Поскольку RequestInterceptorбольше не доступен в Retrofit 2.0

Феликс
источник
3
Это не связано напрямую, но если вам нужно получить значения из объекта запроса, чтобы сгенерировать заголовок авторизации, вам нужно будет расширить ApacheClient и выполнить дубликат объекта запроса (List <Header> headers = ... ; Запрос requestNew = новый запрос (request.getMethod (), request.getUrl (), headers, request.getBody ()); request = requestNew).
1
Это трюк, который портит закодированный, лучше используйте ответ
@nana
1
RestAdapterзависит от Retrofit1, в Retrofit2 так и есть Retrofit. Я собираюсь использовать Retrofit2, поэтому не будет проблем, если использовать, RequestInterceptorкак указано выше?
Huy Tower
55

Да, вы можете передать их во время выполнения. Собственно говоря, почти так же, как вы это напечатали. Это будет в вашем классе интерфейса API с именем, скажем, SecretApiInterface.java.

public interface SecretApiInterface {

    @GET("/secret_things")
    SecretThing.List getSecretThings(@Header("Authorization") String token)

}

Затем вы передаете параметры этому интерфейсу из своего запроса, что-то вроде этих строк: (этот файл будет, например, SecretThingRequest.java )

public class SecretThingRequest extends RetrofitSpiceRequest<SecretThing.List, SecretApiInteface>{

    private String token;

    public SecretThingRequest(String token) {
        super(SecretThing.List.class, SecretApiInterface.class);
        this.token = token;
    }

    @Override
    public SecretThing.List loadDataFromNetwork() {
        SecretApiInterface service = getService();
        return service.getSecretThings(Somehow.Magically.getToken());
    }
}

Где Somehow.Magically.getToken()находится вызов метода, который возвращает токен, решать вам, где и как его определить.

Конечно, вы можете иметь более одной @Header("Blah") String blahаннотации в реализации интерфейса, как и в вашем случае!

Меня это тоже сбивает с толку, в документации четко сказано, что он заменяет заголовок, но НЕТ !
Фактически добавляется как с @Headers("hardcoded_string_of_liited_use")аннотацией

Надеюсь это поможет ;)

нана
источник
1
В документации я обнаружил, что он не заменяет существующий заголовок: «Обратите внимание, что заголовки не перезаписывают друг друга». Проверьте square.github.io/retrofit и "Header Manipulation"
Amio.io
37

Принятый ответ относится к более старой версии Retrofit. Для будущих зрителей способ сделать это с помощью Retrofit2.0 - использовать собственный клиент OkHttp:

OkHttpClient httpClient = new OkHttpClient.Builder()
  .addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
      Builder ongoing = chain.request().newBuilder();
      ongoing.addHeader("Accept", "application/json;versions=1");
      if (isUserLoggedIn()) {
        ongoing.addHeader("Authorization", getToken());
      }
      return chain.proceed(ongoing.build());
    }
  })
  .build();

Retrofit retrofit = new Retrofit.Builder()
  // ... extra config
  .client(httpClient)
  .build();

Надеюсь, это кому-то поможет. :)

паблиско
источник
5
При обычном использовании с dagger2 retrofit2 будет одноэлементным, поэтому httpclient не будет создаваться каждый раз. в этом случае isUserLoggedIn () не имеет смысла, я прав? Единственное решение, которое я вижу в настоящее время, - это принудительная повторная инициализация retrofit2 при изменении статуса входа пользователя, чтобы соответствующий заголовок добавлялся или удалялся из запроса ... или есть какое-то очевидное решение, которое я не вижу в настоящее время? Спасибо.
bajicdusko
2
@bajicdusko, это моя та же загадка. Вы нашли решение? Это кажется таким расточительным и странным, что предыдущая версия была эффективнее.
deed02392
@ deed02392 Вы можете установить композицию, Interceptorна которую вы можете установить или сбросить перехватчик на более позднем этапе. Однако я бы сказал, что модернизация как синглтон может быть признаком ранней оптимизации. Нет накладных расходов на создание нового экземпляра модернизации: github.com/square/retrofit/blob/master/retrofit/src/main/java/…
pablisco
Я особо не задумывался об этом. У меня есть класс ApiFactory, который также инициализируется dagger2, и он отвечает за инициализацию модернизации. Я представил один общедоступный метод в ApiFactory, который при необходимости вызывает повторную инициализацию модифицированного экземпляра, так что это довольно просто. Я мог бы сделать это неправильно, но он выполнил свою работу, и я использую его только для заголовка авторизации, поэтому он используется при входе или выходе пользователя. Другой вариант - использовать аннотацию @Header внутри определения конечной точки, что для меня было неприемлемо. Я должен установить его на каждой конечной точке, что непрактично.
bajicdusko
@pablisco А, насколько я понимаю, вы не можете добавлять или удалять Interceptors после создания экземпляра Retrofit2.
deed02392
7

Модернизация 2.3.0

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    okHttpClientBuilder
            .addInterceptor(new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Request.Builder newRequest = request.newBuilder().header("Authorization", accessToken);
                    return chain.proceed(newRequest.build());
                }
            });

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(GithubService.BASE_URL)
            .client(okHttpClientBuilder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

Я использую это для подключения к GitHub.

Скоро Сантос
источник