Retrofit Android paramétré @Headers


85

J'utilise OAuth et je dois mettre le jeton OAuth dans mon en-tête chaque fois que je fais une demande. Je vois l' @Headerannotation, mais y a-t-il un moyen de la paramétrer pour que je puisse passer au moment de l'exécution?

Voici le concept

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

Pouvez-vous les transmettre au Runtime?

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

Avez-vous déjà compris cela? Je dois également passer un jeton dans l'en-tête
theSociableme

Je recherche également une solution à cela, à partir de la documentation, cela ressemble à l' annotation @Headers () sur la méthode ajoute des champs à l'en-tête un par un, mais ne prend en charge que les littéraux. Et la chaîne Chaîne @Header ( « paramètre ») annotation de paramètre remplace l' en- tête avec la valeur fournie.
nana le

2
Idem ici, impossible de savoir comment gérer les sessions lors de l'utilisation de la modernisation.
FRR

Nous n'avons pas eu besoin de passer tous les éléments, la rénovation elle-même s'occupe de tout. Veuillez vérifier mon lien de réponse dans StackOverflow.
Subin Babu

Réponses:


98

En plus d'utiliser le paramètre @Header, je préfère utiliser RequestInterceptor pour mettre à jour toutes vos demandes sans changer votre interface. En utilisant quelque chose comme:

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: Si vous utilisez Retrofit2, vous devez utiliser à la Interceptorplace deRequestInterceptor

Depuis RequestInterceptorn'est plus disponible dans Retrofit 2.0


3
Ce n'est pas directement lié mais si vous avez besoin d'obtenir des valeurs de l'objet de requête afin de générer votre en-tête d'autorisation, vous devrez étendre ApacheClient et exécuter en double l'objet Request (List <Header> headers = ... ; Request requestNew = new Request (request.getMethod (), request.getUrl (), headers, request.getBody ()); request = requestNew).

1
C'est un truc qui gâche un codé, mieux vaut utiliser la réponse de @ nana
Ivan Fazaniuk

1
RestAdapterdépend de Retrofit1, c'est le cas dans Retrofit2 Retrofit. Je vais utiliser Retrofit2, donc pas de problèmes si vous utilisez RequestInterceptorle code ci-dessus?
Huy Tower

55

Oui, vous pouvez les transmettre lors de l'exécution. En fait, à peu près exactement comme vous l'avez tapé. Ce serait dans votre classe d'interface API, nommée par exemple SecretApiInterface.java

public interface SecretApiInterface {

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

}

Ensuite, vous passez les paramètres à cette interface à partir de votre requête, quelque chose du genre: (ce fichier serait par exemple 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());
    }
}

Où se Somehow.Magically.getToken()trouve un appel de méthode qui renvoie un jeton, c'est à vous de décider où et comment vous le définissez.

Vous pouvez bien sûr avoir plus d'une @Header("Blah") String blahannotations dans l'implémentation de l'interface, comme dans votre cas!

Je l'ai trouvé déroutant aussi, la documentation indique clairement qu'il remplace l'en-tête, mais ce n'est PAS le cas !
Il est en fait ajouté comme avec une @Headers("hardcoded_string_of_liited_use")annotation

J'espère que cela t'aides ;)


1
J'ai trouvé dans la documentation qu'il ne remplace pas un en-tête existant: "Notez que les en-têtes ne se remplacent pas." Vérifiez square.github.io/retrofit et "Header Manipulation"
Amio.io

37

La réponse acceptée concerne une ancienne version de Retrofit. Pour les futurs téléspectateurs, la façon de le faire avec Retrofit2.0 consiste à utiliser un client OkHttp personnalisé:

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

J'espère que ça aide quelqu'un. :)


5
Dans l'usage courant avec dagger2, retrofit2 sera singleton, donc httpclient ne sera pas créé à chaque fois. dans ce cas, isUserLoggedIn () n'a pas de sens, ai-je raison? La seule solution que je peux voir actuellement est de forcer la réinitialisation de retrofit2 lorsque l'état de connexion de l'utilisateur est modifié, de sorte que l'en-tête approprié soit ajouté ou supprimé de la demande .. ou il existe une solution évidente que je ne peux pas voir actuellement? Merci.
bajicdusko

2
@bajicdusko c'est exactement le même connundrum. Avez-vous trouvé une solution? Cela semble tellement inutile et étrange que la version précédente soit plus efficace.
deed02392

@ deed02392 Vous pouvez définir un composite Interceptorsur lequel vous pouvez définir ou réinitialiser l'intercepteur ultérieurement. Cependant, je dirais que la mise à niveau en tant que singleton peut être le signe d'une optimisation précoce. Il n'y a pas de frais généraux sur la création d'une nouvelle instance de mise à niveau: github.com/square/retrofit/blob/master/retrofit/src/main/java
...

Je n'y ai pas vraiment réfléchi profondément. J'ai une classe ApiFactory qui est également initialisée avec dagger2, et elle est responsable de l'initialisation de la mise à niveau. J'ai exposé une méthode publique dans ApiFactory qui force la réinitialisation de l'instance de retrofit en cas de besoin, donc c'est assez simple. Je peux le faire mal, mais cela a fait le travail, et je ne l'utilise que pour l'en-tête d'autorisation, donc il est utilisé lors de la connexion ou de la déconnexion de l'utilisateur. Une autre option consiste à utiliser l'annotation @Header dans la définition du point de terminaison, ce qui n'était pas acceptable pour moi. Je devrais le définir sur chaque point final, ce qui n'est pas pratique.
bajicdusko

@pablisco Ah d'après ce que j'ai compris, vous ne pouviez pas ajouter ou supprimer de Interceptors une fois que vous auriez créé une instance Retrofit2.
deed02392

7

Rénovation 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();

J'utilise ceci pour me connecter à GitHub.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.