如何在Retrofit 2.0中使用拦截器添加头部?

121

我们团队决定采用Retrofit 2.0,我正在对其进行初步研究。我是这个库的新手。

我想知道如何使用interceptor通过Retrofits 2.0在我们的Android应用程序中添加自定义标头。有许多教程介绍如何在Retrofit 1.X中使用interceptor添加标头,但由于最新版本中的API已经发生了很大变化,我不确定如何将那些方法适应到新版本中。此外,Retrofit尚未更新其新文档。

例如,在以下代码中,我应该如何实现Interceptor类以添加额外的标头?此外,什么是未记录的Chain对象intercept()函数什么时候会被调用?

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

1
确保你的 BASE_API_URL 以 / 结尾,并且你的 API URL 不要包含 (stuff/post/whatever)。 - EpicPandaForce
4个回答

152

看这个。

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

: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()
        )        
    }
}

谢谢!所以,每次应用程序发送请求时都会触发这个intercept()函数?我们能捕获重定向的中间响应吗,还是只能获取最终响应? - hackjutsu
这是针对每个请求调用的,如果我没记错的话,这是因为你将其添加为拦截器,而不是网络拦截器。我认为你只能在这里获取最终响应,但可能有一些配置可以允许查看重定向作为重定向,这是我脑海中不知道的(http URL连接也有一个)。 - EpicPandaForce
1
请参考此链接:https://github.com/square/okhttp/wiki/Interceptors,获取我所需的信息:) 谢谢~ - hackjutsu
5
请注意,您需要使用建造者(builder)而不是 client.interceptors()。 可以这样写:new OkHttpClient.Builder().addInterceptor(<Your Interceptor>).build() - GLee

26

以下是来自被接受的答案的另一种选择:

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;
    }
}

不错!那么 request.newBuilder().headers(moreHeaders).build() 会保留原始的标头吗? - hackjutsu
1
是的。除非调用removeAll(String name),否则请求中不会删除头部信息。 - VenomVendor
@VenomVendor,请帮我解决一个类似的问题,链接在这里 https://stackoverflow.com/questions/45078720/dagger-generating-multiple-instances-of-retrofit-interceptor谢谢 - user606669
这样不会一直创建新对象吗? - TheRealChx101
@TheRealChx101请解释一下你的意思? - Christopher Smit

4
   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);
        }
}

3
你可以使用拦截器和其内置方法来设置请求头,代码如下:
   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);
        }
    });
}

我想知道你是如何在这个地方获取context的? - rupinderjeet
@rupinderjeet 可能在参数列表中加入一个 final Context context - TheRealChx101
@TheRealChx101 只是想指出我们不应该在这里使用 context,因为这是业务逻辑。 - rupinderjeet
@rupinderjeet 如果你想要的话,可以在这里获取应用程序上下文(context.applicationContext)。 - Mohit Atray
@MohitAtray 是的,也许吧。最近我有一个习惯,就是将所有网络逻辑完全分离到不同的模块中。这个网络模块独立于 Android 依赖项。这就是为什么我对直接在这里使用“context”持怀疑态度的原因。 - rupinderjeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接