Android Retrofit parametrizzato @Headers


85

Sto utilizzando OAuth e devo inserire il token OAuth nella mia intestazione ogni volta che faccio una richiesta. Vedo l' @Headerannotazione, ma c'è un modo per renderla parametrizzata in modo da poterla passare in fase di esecuzione?

Ecco il concetto

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

Puoi passarli a Runtime?

@GET("/users")
void getUsers(
    @Header("Authorization") String auth, 
    @Header("X-Api-Version") String version, 
    Callback<User> callback
)

L'hai mai capito? Devo anche passare un gettone nell'intestazione
theSociableme

Sto anche cercando una soluzione a questo, dalla documentazione suona come l' annotazione @Headers () sul metodo aggiunge i campi all'intestazione uno per uno, ma supporta solo i letterali. E @Header ("parametro") L' annotazione del parametro stringa stringa sostituisce l'intestazione con il valore fornito.
nana

2
Lo stesso qui, non sono riuscito a scoprire come gestire le sessioni quando si utilizza il retrofit.
FRR

Non avevamo bisogno di passare tutti gli elementi, il retrofit stesso si occupava di tutto. Si prega di controllare il link della mia risposta in StackOverflow.
Subin Babu

Risposte:


98

Oltre a utilizzare il parametro @Header, preferirei usare RequestInterceptor per aggiornare tutte le tue richieste senza modificare la tua interfaccia. Usando qualcosa come:

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: se stai usando Retrofit2, dovresti usare al Interceptorposto diRequestInterceptor

Poiché RequestInterceptornon è più disponibile in Retrofit 2.0


3
Questo non è direttamente correlato, ma se ti ritrovi a dover ottenere valori dall'oggetto richiesta per generare l'intestazione di autorizzazione, dovrai estendere ApacheClient e in esecuzione duplicare l'oggetto Request (List <Header> headers = ... ; Request requestNew = new Request (request.getMethod (), request.getUrl (), headers, request.getBody ()); request = requestNew).

1
Questo è un trucco che rovina un codice, meglio usare la risposta di @ nana
Ivan Fazaniuk

1
RestAdapterdipende da Retrofit1, in Retrofit2 lo è Retrofit. Userò Retrofit2, quindi non ha problemi se si utilizza RequestInterceptorcome codice sopra?
Torre Huy

55

Sì, puoi passarli in runtime. È un dato di fatto, più o meno esattamente come l'hai digitato. Questo sarebbe nella tua classe di interfaccia API, chiamata ad esempio SecretApiInterface.java

public interface SecretApiInterface {

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

}

Quindi passi i parametri a questa interfaccia dalla tua richiesta, qualcosa del genere: (questo file sarebbe ad esempio 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());
    }
}

Dov'è Somehow.Magically.getToken()una chiamata al metodo che restituisce un token, dipende da te dove e come lo definisci.

Ovviamente puoi avere più di una @Header("Blah") String blahannotazione nell'implementazione dell'interfaccia, come nel tuo caso!

Anch'io l'ho trovato confuso, la documentazione dice chiaramente che sostituisce l'intestazione, ma NON lo fa !
In effetti è aggiunto come con@Headers("hardcoded_string_of_liited_use") annotazione

Spero che sia di aiuto ;)


1
Ho riscontrato nella documentazione che non sostituisce un'intestazione esistente: "Nota che le intestazioni non si sovrascrivono a vicenda". Controlla square.github.io/retrofit e "Header Manipulation"
Amio.io

37

La risposta accettata è per una versione precedente di Retrofit. Per i futuri spettatori il modo per farlo con Retrofit2.0 è usare un client OkHttp personalizzato:

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

Spero che aiuti qualcuno. :)


5
Nell'uso comune con dagger2, retrofit2 sarà singleton, quindi httpclient non verrà creato ogni volta. in tal caso isUserLoggedIn () non ha senso, giusto? L'unica soluzione che posso vedere attualmente è quella di forzare la reinizializzazione di retrofit2 quando lo stato di accesso dell'utente viene modificato, in modo che l'intestazione appropriata venga aggiunta o rimossa dalla richiesta .. o c'è qualche soluzione ovvia che non riesco a vedere attualmente? Grazie.
bajicdusko

2
@bajicdusko questo è il mio stesso identico connubio. Hai trovato una soluzione? Sembra così dispendioso e strano che la versione precedente fosse più efficiente.
deed02392

@ deed02392 Puoi impostare un composito Interceptorsu cui puoi impostare o reimpostare l'interceptor in una fase successiva. Tuttavia, direi che avere il retrofit come singolo può essere un segno di ottimizzazione iniziale. Non ci sono costi
aggiuntivi

Non ci ho davvero pensato a fondo. Ho una classe ApiFactory che è anche inizializzata con dagger2 ed è responsabile dell'inizializzazione del retrofit. Ho esposto un metodo pubblico in ApiFactory che forza la reinizializzazione dell'istanza di retrofit quando necessario, quindi è abbastanza semplice. Potrei sbagliarmi, ma ha funzionato e lo sto usando solo per l'intestazione di autorizzazione, quindi viene utilizzato quando l'utente effettua il login o il logout. Un'altra opzione è usare l'annotazione @Header all'interno della definizione dell'endpoint, che non era accettabile per me. Dovrei impostarlo su ogni endpoint che non è pratico.
bajicdusko

@pablisco Ah, da quanto ho capito, non è stato possibile aggiungere o rimuovere messaggi Interceptoruna volta creata un'istanza 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();

Lo sto usando per connettermi a GitHub.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.