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.