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

Posted

技术标签:

【中文标题】如何使用拦截器在 Retrofit 2.0 中添加标头?【英文标题】:How to use interceptor to add Headers in Retrofit 2.0? 【发布时间】:2016-01-02 23:35:17 【问题描述】:

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

我想知道如何使用interceptor 在我们的android 应用程序中通过Retrofits 2.0 添加自定义标题。有很多 tutorials 关于在 Retrofit 1.X 中使用 interceptor 添加标头,但由于 API 在最新版本中发生了很大变化,我不确定如何在新版本中调整这些方法。此外,Retrofit 尚未更新其新文档。

例如,在下面的代码中,我应该如何实现Interceptor 类来添加额外的标题?此外,undocumented Chain object 到底是什么?什么时候会调用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();

【问题讨论】:

确保您的 BASE_API_URL 以 / 结尾,并且您的 API 网址不 (stuff/post/whatever) 【参考方案1】:

看看这个。

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;
    

科特林

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()?我们可以捕捉到重定向的中间响应,还是只得到最终响应? 每个请求都会调用这个,如果我没记错的话,那是因为你把它添加为拦截器,而不是网络拦截器。我认为您只能在此处获得最终响应,但可能有一个配置允许将重定向视为我不知道的重定向(也有一个用于 http URL 连接。) 只要参考这个链接:github.com/square/okhttp/wiki/Interceptors,得到我需要的信息:)谢谢~ 仅供参考,您需要使用构建器而不是 client.interceptors()。这看起来像new OkHttpClient.Builder().addInterceptor(<Your Interceptor>).build()【参考方案2】:

accepted answer 的另一种选择

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() 会保留原来的标头吗? 是的。除非调用 removeAll(String name),否则不会从请求中删除任何标头。 @VenomVendor 请帮我解决类似的问题***.com/questions/45078720/… 谢谢 这不会继续创建新对象吗? @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);
        

【讨论】:

【参考方案4】:

您可以使用带有类似这样的内置方法的拦截器来标头

   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 可能是参数列表中的final Context context @TheRealChx101 只是想指出我们不应该在这里有context,因为这是业务逻辑。 @rupinderjeet 如果需要,您可以在此处获取应用程序上下文 (context.applicationContext)。 @MohitAtray 是的,也许。这些天来,我养成了将所有网络逻辑完全分离在不同模块中的习惯。此网络模块独立于 android 依赖项。这就是为什么我对直接在这里使用context 持怀疑态度。

以上是关于如何使用拦截器在 Retrofit 2.0 中添加标头?的主要内容,如果未能解决你的问题,请参考以下文章

使用OkHttp拦截器和Retrofit进行缓存

Retrofit2.0 添加log拦截公共参数

Retrofit 2.0如何获得反序列化错误response.body

Retrofit 2.0如何获得反序列化错误response.body

Retrofit全攻略——进阶篇

Retrofit2 Authorization - 访问令牌的全局拦截器