Retrofit Android parametrizado @Headers


85

Estou usando o OAuth e preciso colocar o token OAuth no meu cabeçalho sempre que faço uma solicitação. Eu vejo a @Headeranotação, mas existe uma maneira de parametrizá-la para que eu possa passar em tempo de execução?

Aqui está o conceito

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

Você pode passá-los em Runtime?

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

Você já descobriu isso? Preciso passar um token no cabeçalho também
theSociableme

Também estou procurando uma solução para isso, a partir da documentação, parece que a anotação @Headers () no método adiciona campos ao cabeçalho um por um, mas só oferece suporte a literais. E @Header ("parâmetro") A anotação de parâmetro de string de string substitui o cabeçalho pelo valor fornecido.
nana

2
Mesmo aqui, não consegui descobrir como lidar com as sessões ao usar retrofit.
FRR de

Não precisávamos passar todos os itens, retrofit-se para lidar com todos. Verifique meu link de resposta no StackOverflow.
Subin Babu

Respostas:


98

Além de usar o parâmetro @Header, prefiro usar RequestInterceptor para atualizar todas as suas solicitações sem alterar sua interface. Usando algo como:

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 você estiver usando Retrofit2, você deve usar em Interceptorvez deRequestInterceptor

Uma vez que RequestInterceptornão está mais disponível no Retrofit 2.0


3
Isso não está diretamente relacionado, mas se você precisar obter valores do objeto de solicitação para gerar seu cabeçalho de autorização, será necessário estender o ApacheClient e duplicar o objeto de solicitação (List <Header> headers = ... ; Request requestNew = new Request (request.getMethod (), request.getUrl (), headers, request.getBody ()); request = requestNew).

1
Esse é um truque que bagunça um código, é melhor usar a resposta de
@nana

1
RestAdapterdepende do Retrofit1, no Retrofit2 é Retrofit. Vou usar o Retrofit2, então não há problemas se usar o RequestInterceptorcódigo acima?
Torre Huy

55

Sim, você pode passá-los em tempo de execução. Na verdade, exatamente como você digitou. Isso estaria em sua classe de interface da API, chamada, digamos, SecretApiInterface.java

public interface SecretApiInterface {

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

}

Em seguida, você passa os parâmetros de sua solicitação para essa interface, algo nesse sentido: (este arquivo seria, por exemplo, 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());
    }
}

Onde Somehow.Magically.getToken()está uma chamada de método que retorna um token, cabe a você onde e como defini-lo.

É claro que você pode ter mais de uma @Header("Blah") String blahanotação na implementação da interface, como no seu caso!

Também achei confuso, a documentação diz claramente que substitui o cabeçalho, mas NÃO !
Na verdade, é adicionado como uma @Headers("hardcoded_string_of_liited_use")anotação

Espero que isto ajude ;)


1
Descobri nos documentos que ele não substitui um cabeçalho existente: "Observe que os cabeçalhos não se sobrescrevem." Verifique square.github.io/retrofit e "Manipulação de cabeçalho"
Amio.io

37

A resposta aceita é para uma versão mais antiga do Retrofit. Para futuros espectadores, a maneira de fazer isso com Retrofit2.0 é usando um cliente OkHttp personalizado:

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

Espero que ajude alguém. :)


5
Em uso comum com dagger2, retrofit2 será singleton, portanto, httpclient não será criado todas as vezes. nesse caso, isUserLoggedIn () não faz sentido, estou certo? A única solução que vejo atualmente é forçar a reinicialização do retrofit2 quando o status de login do usuário for alterado, para que o cabeçalho apropriado seja adicionado ou removido da solicitação ... ou há alguma solução óbvia que não consigo ver atualmente? Obrigado.
bajicdusko

2
@bajicdusko, este é exatamente o meu mesmo problema. Você encontrou uma solução? Parece muito desperdício e estranho que a versão anterior fosse mais eficiente.
deed02392 de

@ deed02392 Você pode definir um composto Interceptorpara o qual pode definir ou redefinir o interceptor em um estágio posterior. No entanto, eu diria que ter o retrofit como um singleton pode ser um sinal de otimização inicial. Não há sobrecarga na criação de uma nova instância de retrofit: github.com/square/retrofit/blob/master/retrofit/src/main/java/…
pablisco

Eu realmente não pensei sobre isso profundamente. Eu tenho alguma classe ApiFactory que também é inicializada com dagger2 e é responsável pela inicialização do retrofit. Eu expus um método público em ApiFactory que força a reinicialização da instância de retrofit quando necessário, então é bastante simples. Posso estar fazendo errado, mas funcionou, e estou usando-o apenas para o cabeçalho de autorização, então é usado quando o usuário faz o login ou logout. Outra opção é usar a anotação @Header dentro da definição do endpoint, o que não era aceitável para mim. Devo defini-lo em cada ponto de extremidade, o que não é prático.
bajicdusko

@pablisco Ah, pelo meu conhecimento, você não poderia adicionar ou remover Interceptors depois de criar uma instância Retrofit2.
deed02392 de

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

Estou usando isso para me conectar ao GitHub.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.