Como usar o interceptor para adicionar cabeçalhos no Retrofit 2.0?

96

Nossa equipe decidiu adotar o Retrofit 2.0 e estou fazendo algumas pesquisas iniciais sobre ele. Eu sou um novato nesta biblioteca.

Estou me perguntando como usar interceptorpara adicionar cabeçalhos personalizados por meio de Retrofits 2.0 em nosso aplicativo Android. Existem muitos tutoriais sobre interceptorcomo adicionar cabeçalhos no Retrofit 1.X, mas, como as APIs mudaram muito na versão mais recente, não tenho certeza de como adaptar esses métodos na nova versão. Além disso, Retrofit ainda não atualizou sua nova documentação.

Por exemplo, nos códigos a seguir, como devo implementar a Interceptorclasse para adicionar cabeçalhos extras? Além disso, o que exatamente é o objeto não documentadoChain ? Quando será intercept()chamado?

    OkHttpClient client = new OkHttpClient();
    client.interceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());

            // How to add extra headers?

            return response;
        }
    });

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_API_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
hackjutsu
fonte
1
Certifique-se de que seu BASE_API_URL termina com /e que seus URLs de API não stuff/post/whatever
terminam

Respostas:

120

Veja isso.

public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request()
                .newBuilder()
                .addHeader("appid", "hello")
                .addHeader("deviceplatform", "android")
                .removeHeader("User-Agent")
                .addHeader("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0")
                .build();
        Response response = chain.proceed(request);
        return response;
    }
}

Kotlin

class HeaderInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response = chain.run {
        proceed(
            request()
                .newBuilder()
                .addHeader("appid", "hello")
                .addHeader("deviceplatform", "android")
                .removeHeader("User-Agent")
                .addHeader("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0")
                .build()
        )        
    }
}
EpicPandaForce
fonte
Obrigado!! Então, isso intercept()é acionado toda vez que uma solicitação é enviada do aplicativo? Podemos pegar a resposta intermediária para redirecionar ou apenas obter a resposta final?
hackjutsu
Isso é chamado para cada requisição, e se eu sei direito é porque você adiciona como interceptador, e não como interceptor de rede. Acho que você pode obter apenas a resposta final aqui, mas pode haver uma configuração para permitir ver redirecionamentos como redirecionamentos que não conheço de cara (há um para conexão de URL http também.)
EpicPandaForce
1
Basta acessar este link: github.com/square/okhttp/wiki/Interceptors e obter as informações de que preciso :) Obrigado ~
hackjutsu
5
Fyi, você precisa usar um construtor em vez de client.interceptors(). Isto parecenew OkHttpClient.Builder().addInterceptor(<Your Interceptor>).build()
GLee
22

Outra alternativa da resposta aceita

public class HeaderInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        request = request.newBuilder()
                .addHeader("headerKey0", "HeaderVal0")
                .addHeader("headerKey0", "HeaderVal0--NotReplaced/NorUpdated") //new header added
                .build();

        //alternative
        Headers moreHeaders = request.headers().newBuilder()
                .add("headerKey1", "HeaderVal1")
                .add("headerKey2", "HeaderVal2")
                .set("headerKey2", "HeaderVal2--UpdatedHere") // existing header UPDATED if available, else added.
                .add("headerKey3", "HeaderKey3")
                .add("headerLine4 : headerLine4Val") //line with `:`, spaces doesn't matter.
                .removeAll("headerKey3") //Oops, remove this.
                .build();

        request = request.newBuilder().headers(moreHeaders).build();

        /* ##### List of headers ##### */
        // headerKey0: HeaderVal0
        // headerKey0: HeaderVal0--NotReplaced/NorUpdated
        // headerKey1: HeaderVal1
        // headerKey2: HeaderVal2--UpdatedHere
        // headerLine4: headerLine4Val

        Response response = chain.proceed(request);
        return response;
    }
}
VenomVendor
fonte
Agradável! Então request.newBuilder().headers(moreHeaders).build(), manterá os cabeçalhos originais?
hackjutsu
1
Sim. Nenhum cabeçalho é removido da solicitação, a menos que removeAll (nome da string) seja chamado.
VenomVendor
@VenomVendor, por favor me ajude com uma pergunta semelhante aqui stackoverflow.com/questions/45078720/… obrigado
user606669
Isso não vai continuar criando novos objetos?
TheRealChx101
3
   public class ServiceFactory {  
    public static ApiClient createService(String authToken, String userName, String password) {
            OkHttpClient defaultHttpClient = new OkHttpClient.Builder()
                    .addInterceptor(
                            chain -> {
                                Request request = chain.request().newBuilder()
                                        .headers(getJsonHeader(authToken))
                                        .build();
                                return chain.proceed(request);
                            })
                    .authenticator(getBasicAuthenticator(userName, password))
                    .build();
            return getService(defaultHttpClient);
        }
        private static Headers getJsonHeader(String authToken) {
            Headers.Builder builder = new Headers.Builder();
            builder.add("Content-Type", "application/json");
            builder.add("Accept", "application/json");
            if (authToken != null && !authToken.isEmpty()) {
                builder.add("X-MY-Auth", authToken);
            }
            return builder.build();
        }
        private static Authenticator getBasicAuthenticator(final String userName, final String password) {
            return (route, response) -> {
                String credential = Credentials.basic(userName, password);
                return response.request().newBuilder().header("Authorization", credential).build();
            };
        }
          private static ApiClient getService(OkHttpClient defaultHttpClient) {
            return new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .client(defaultHttpClient)
                    .build()
                    .create(ApiClient.class);
        }
}
Fawad Badar
fonte
2

Você pode usar cabeçalhos usando Interceptors com seus métodos embutidos como este

   interceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();

            Request.Builder builder = original.newBuilder();

            builder.header("Authorization","Bearer "+ LeafPreference.getInstance(context).getString(LeafPreference.TOKEN));

            Request request = builder.method(original.method(), original.body())
                    .build();
            Log.e("request",request.urlString());
            Log.e("header",request.header("Authorization"));
            return chain.proceed(request);
        }
    });
}

fonte
Eu quero saber como você consegue contextualizar este lugar?
rupinderjeet
@rupinderjeet Provavelmente um final Context contextna lista de parâmetros.
TheRealChx101
@ TheRealChx101 Só queria salientar que não deveríamos ter contextaqui porque esta é a lógica de negócios.
rupinderjeet