Android Retrofit Parametrisiert @Headers

84

Ich verwende OAuth und muss das OAuth-Token jedes Mal in meinen Header einfügen, wenn ich eine Anfrage stelle. Ich sehe die @HeaderAnmerkung, aber gibt es eine Möglichkeit, sie zu parametrisieren, damit ich sie zur Laufzeit übergeben kann?

Hier ist das Konzept

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

Können Sie sie zur Laufzeit weitergeben?

@GET("/users")
void getUsers(
    @Header("Authorization") String auth, 
    @Header("X-Api-Version") String version, 
    Callback<User> callback
)
jpotts18
quelle
Hast du das jemals herausgefunden? Ich muss auch ein Token in der Kopfzeile übergeben
theSociableme
Ich suche auch nach einer Lösung für dieses Problem. Aus der Dokumentation geht hervor, dass die Annotation @Headers () der Methode Felder nacheinander zum Header hinzufügt , aber nur Literale unterstützt. Und @Header ("parameter") Annotation von String-String- Parametern ersetzt den Header durch den angegebenen Wert.
Nana
2
Das gleiche hier, konnte nicht herausfinden, wie Sitzungen mit Nachrüstung zu behandeln sind.
FR
Wir mussten nicht alle Artikel übergeben, sondern alle nachrüsten. Bitte überprüfen Sie meine Antwort Link in Stackoverflow.
Subin Babu

Antworten:

98

Neben der Verwendung des Parameters @Header würde ich lieber RequestInterceptor verwenden, um alle Ihre Anforderungen zu aktualisieren, ohne Ihre Benutzeroberfläche zu ändern. Verwenden Sie so etwas wie:

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: Wenn Sie Retrofit2 verwenden, sollten Sie Interceptoranstelle von verwendenRequestInterceptor

Da RequestInterceptorist in Retrofit 2.0 nicht mehr verfügbar

Felix
quelle
3
Dies hängt nicht direkt zusammen, aber wenn Sie Werte vom Anforderungsobjekt abrufen müssen, um Ihren Autorisierungsheader zu generieren, müssen Sie ApacheClient erweitern und das Anforderungsobjekt duplizieren (List <Header> headers = ... ; Request requestNew = new Request (request.getMethod (), request.getUrl (), Header, request.getBody ()); request = requestNew).
1
Das ist ein Trick, der eine verschlüsselte, besser @
nanas
1
RestAdapterhängt von Retrofit1 ab, in Retrofit2 ist es Retrofit. Ich werde Retrofit2 verwenden, also keine Probleme, wenn RequestInterceptorwie oben beschriebener Code verwendet wird?
Huy Tower
55

Ja, Sie können sie zur Laufzeit übergeben. In der Tat ziemlich genau so, wie Sie es abgetippt haben. Dies wäre in Ihrer API-Schnittstellenklasse mit dem Namen SecretApiInterface.java

public interface SecretApiInterface {

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

}

Dann übergeben Sie die Parameter aus Ihrer Anfrage an diese Schnittstelle, ungefähr so : (Diese Datei wäre zum Beispiel 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());
    }
}

Wo Somehow.Magically.getToken()ein Methodenaufruf ein Token zurückgibt, liegt es an Ihnen, wo und wie Sie es definieren.

Sie können natürlich mehr als eine @Header("Blah") String blahAnmerkung in der Schnittstellenimplementierung haben, wie in Ihrem Fall!

Ich fand es zu verwirrend, die Dokumentation deutlich sagt , es ersetzt die Header, aber es DOES NOT !
Es wird tatsächlich wie bei einer @Headers("hardcoded_string_of_liited_use")Anmerkung hinzugefügt

Hoffe das hilft ;)

Nana
quelle
1
Ich habe in den Dokumenten festgestellt, dass es keinen vorhandenen Header ersetzt: "Beachten Sie, dass sich Header nicht gegenseitig überschreiben." Überprüfen Sie square.github.io/retrofit und "Header Manipulation"
Amio.io
37

Die akzeptierte Antwort gilt für eine ältere Version von Retrofit. Für zukünftige Betrachter besteht die Möglichkeit, dies mit Retrofit2.0 zu tun, in der Verwendung eines benutzerdefinierten OkHttp-Clients:

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

Hoffe es hilft jemandem. :) :)

Pablisco
quelle
5
In der allgemeinen Verwendung von dagger2 ist retrofit2 singleton, daher wird httpclient nicht jedes Mal erstellt. in diesem Fall macht isUserLoggedIn () keinen Sinn, habe ich recht? Die einzige Lösung, die ich derzeit sehen kann, besteht darin, die Neuinitialisierung von retrofit2 zu erzwingen, wenn der Anmeldestatus des Benutzers geändert wird, sodass der entsprechende Header zur Anforderung hinzugefügt oder daraus entfernt wird. Oder gibt es eine offensichtliche Lösung, die ich derzeit nicht sehen kann? Vielen Dank.
Bajicdusko
2
@bajicdusko das ist genau das gleiche Rätsel. Haben Sie eine Lösung gefunden? Es scheint so verschwenderisch und seltsam, dass die vorherige Version effizienter war.
deed02392
@ deed02392 Sie können einen Verbund festlegen, Interceptorauf den Sie den Interceptor zu einem späteren Zeitpunkt einstellen oder zurücksetzen können. Ich würde jedoch argumentieren, dass eine Nachrüstung als Singleton ein Zeichen für eine frühzeitige Optimierung sein kann. Das Erstellen einer neuen Nachrüstinstanz ist kein Aufwand: github.com/square/retrofit/blob/master/retrofit/src/main/java/…
pablisco
Ich habe nicht wirklich tief darüber nachgedacht. Ich habe eine ApiFactory-Klasse, die ebenfalls mit dagger2 initialisiert wird und für die Initialisierung der Nachrüstung verantwortlich ist. Ich habe eine öffentliche Methode in ApiFactory verfügbar gemacht, die bei Bedarf eine Neuinitialisierung der Nachrüstinstanz erzwingt. Das ist also ganz einfach. Ich könnte es falsch machen, aber es hat den Job gemacht, und ich verwende es nur für den Autorisierungsheader, so dass es beim Anmelden oder Abmelden des Benutzers verwendet wird. Eine andere Option ist die Verwendung der @ Header-Annotation innerhalb der Endpunktdefinition, was für mich nicht akzeptabel war. Ich sollte es auf jeden Endpunkt setzen, was nicht praktikabel ist.
Bajicdusko
@pablisco Ah, nach meinem Verständnis konnten Sie Interceptors nicht hinzufügen oder entfernen, nachdem Sie eine Retrofit2-Instanz erstellt hatten.
deed02392
7

Nachrüstung 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();

Ich benutze dies, um eine Verbindung zu GitHub herzustellen.

Bald Santos
quelle