Параметризованные @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
)

Вы когда-нибудь понимали это? Мне также нужно передать токен в заголовок
— 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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.