Sparametryzowana modernizacja Androida @Headers

85

Używam OAuth i za każdym razem, gdy wysyłam żądanie, muszę umieszczać token OAuth w nagłówku. Widzę @Headeradnotację, ale czy istnieje sposób na sparametryzowanie jej, aby można było ją przekazać w czasie wykonywania?

Oto koncepcja

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

Czy możesz przekazać je w Runtime?

@GET("/users")
void getUsers(
    @Header("Authorization") String auth, 
    @Header("X-Api-Version") String version, 
    Callback<User> callback
)
jpotts18
źródło
Czy kiedykolwiek to rozgryzłeś? Muszę również przekazać token w nagłówku
theSociableme
Szukam też rozwiązania tego problemu, z dokumentacji to brzmi jak adnotacja @Headers () o metodzie dodaje pola do nagłówka jeden po drugim, ale obsługuje tylko literały. Natomiast @Header ("parameter") Ciąg znaków w opisie parametru zastępuje nagłówek podaną wartością.
nana
2
To samo tutaj, nie mogłem dowiedzieć się, jak obsługiwać sesje podczas korzystania z modernizacji.
FRR
Nie musieliśmy przekazywać wszystkich pozycji, sama modernizacja obsługuje wszystkie. Sprawdź mój link do odpowiedzi w StackOverflow.
Subin Babu

Odpowiedzi:

98

Oprócz używania parametru @Header, wolałbym raczej używać RequestInterceptor do aktualizowania wszystkich żądań bez zmiany interfejsu. Używając czegoś takiego:

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: Jeśli używasz Retrofit2, powinieneś użyć InterceptorzamiastRequestInterceptor

Ponieważ RequestInterceptornie jest już dostępny w Retrofit 2.0

Felix
źródło
3
Nie jest to bezpośrednio związane, ale jeśli okaże się, że musisz pobrać wartości z obiektu żądania w celu wygenerowania nagłówka Authorization, musisz rozszerzyć ApacheClient i wykonać zduplikowanie obiektu Request (List <Header> headers = ... ; Żądanie requestNew = nowe żądanie (request.getMethod (), request.getUrl (), headers, request.getBody ()); request = requestNew).
1
To sztuczka, która psuje zakodowaną, lepiej wykorzystaj odpowiedź
@nana
1
RestAdapterzależy od Retrofit1, w Retrofit2 jest Retrofit. Zamierzam użyć Retrofit2, więc nie ma problemów, jeśli użyję RequestInterceptorjak powyżej kodu?
Huy Tower
55

Tak, możesz je przekazać w czasie wykonywania. Prawdę mówiąc, prawie dokładnie tak, jak to wpisałeś. Byłoby to w twojej klasie interfejsu API, o nazwie powiedz SecretApiInterface.java

public interface SecretApiInterface {

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

}

Następnie przekazujesz parametry do tego interfejsu ze swojego żądania, coś w tym stylu: (ten plik będzie na przykład 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());
    }
}

Gdzie Somehow.Magically.getToken()jest wywołanie metody, która zwraca token, od Ciebie zależy, gdzie i jak ją zdefiniujesz.

Oczywiście możesz mieć więcej niż jedną @Header("Blah") String blahadnotację w implementacji interfejsu, tak jak w twoim przypadku!

Wydało mi się to również zagmatwane, dokumentacja wyraźnie mówi, że zastępuje nagłówek, ale NIE !
W rzeczywistości jest dodawany jak w przypadku @Headers("hardcoded_string_of_liited_use")adnotacji

Mam nadzieję że to pomoże ;)

nana
źródło
1
W dokumentach znalazłem, że nie zastępuje istniejącego nagłówka: „Pamiętaj, że nagłówki nie zastępują się nawzajem”. Sprawdź square.github.io/retrofit i „Manipulację
nagłówkiem
37

Zaakceptowana odpowiedź dotyczy starszej wersji Retrofit. Dla przyszłych widzów sposobem na to w Retrofitwersji 2.0 jest użycie niestandardowego klienta 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();

Mam nadzieję, że to komuś pomoże. :)

pablisco
źródło
5
W typowym użyciu z dagger2, retrofit2 będzie singleton, dlatego httpclient nie zostanie utworzony za każdym razem. w takim przypadku isUserLoggedIn () nie ma sensu, mam rację? Jedynym rozwiązaniem, które obecnie widzę, jest wymuszenie ponownej inicjalizacji retrofit2, gdy status logowania użytkownika zostanie zmieniony, tak aby odpowiedni nagłówek został dodany lub usunięty z żądania ... lub jest jakieś oczywiste rozwiązanie, którego obecnie nie mogę zobaczyć? Dzięki.
bajicdusko
2
@bajicdusko, to jest dokładnie to samo. Znalazłeś rozwiązanie? Wydaje się to tak marnotrawne i dziwne, że poprzednia wersja była bardziej wydajna.
deed02392
@ deed02392 Możesz ustawić kompozyt, Interceptordo którego możesz ustawić lub zresetować przechwytywacz na późniejszym etapie. Jednak argumentowałbym, że modernizacja jako singleton może być oznaką wczesnej optymalizacji. Nie ma narzutów na tworzenie nowej instancji modernizacji: github.com/square/retrofit/blob/master/retrofit/src/main/java/ ...
pablisco
Nie myślałem o tym głęboko. Mam klasę ApiFactory, która również jest inicjalizowana przez dagger2 i jest odpowiedzialna za inicjalizację retrofitu. W ApiFactory ujawniłem jedną publiczną metodę, która w razie potrzeby wymusza ponowną inicjalizację instancji modernizacji, więc jest to dość proste. Mogłem zrobić to źle, ale wykonałem swoje zadanie i używam go tylko do nagłówka Authorization, więc jest używany podczas logowania lub wylogowywania użytkownika. Inną opcją jest użycie adnotacji @Header wewnątrz definicji punktu końcowego, co było dla mnie nie do przyjęcia. Powinienem ustawić to na każdym punkcie końcowym, co nie jest praktyczne.
bajicdusko
@pablisco Ah, jak rozumiem, nie można było dodawać ani usuwać plików Interceptorpo utworzeniu instancji Retrofit2.
deed02392
7

Retrofit 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();

Używam tego do łączenia się z GitHubem.

Wkrótce Santos
źródło