Android okHttp拦截请求头和响应头

先给大家推荐一个开源项目:点这里

这是一款基于网易云音乐UI,使用Gank.Io及豆瓣api开发的符合Google Material Design的Android开源项目。项目采取的是MVVM-DataBinding架构开发,现主要包括:干货区、电影区和书籍区三个子模块。

我也是看了这个开源项目决定新项目框架使用MVVM DataBinding 和Retrofit的。

还是感谢这些无私奉献的人们。

关于Retrofit和binding的知识也出现非常多的资料了,我就不献丑了。

下面说下我遇到的问题。



新项目的网络请求,小白我用了仰慕已久的Retrofit。

项目中使用的jar是上面开源框架中使用的:

compile 'com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0'

前几天需要在网络请求的头中塞入登录成功时返回的码,用来后台验证身份。验证不通过会返回401.

这个需求并不陌生,以前项目中几乎每个都会有。

于是我去查了一下,大概思路明确了,在初始化okhttpclient builder对象时,和restAdapter对象的时候插入请求拦截器。


注:下面贴出的一种方式是我最开始尝试的方式,可以实现,但是麻烦,所以大家可以作为思路的参考,在后面贴出后来改进之后的简便方式。

下面是在OkhttpClient.Builder 对象初始化时插入拦截器,是在网络请求返回response时拦截头部取出cookies并自行保存。

okBuilder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {

                Response originalResponse = chain.proceed(chain.request());

                    if (!originalResponse.headers("Set-Cookie").isEmpty()) {
                        HashSet<String> cookies = new HashSet<String>();

                        for (String header : originalResponse.headers("Set-Cookie")) {
                            cookies.add(header);
                            Log.v("OkHttp", "save Header: " + header);
                        }

                        // 保存上面的cookies
                    }

                return originalResponse;
            }
        });
在下面就是拦截请求request,自行将上面保存的cookies放入request的header中。这步操作我放在了RestAdapter.Builder对象初始化中(其实也可以放入上面的位置,在add一个interceptor)。

builder.setRequestInterceptor(new RequestInterceptor() {
            @Override
            public void intercept(RequestFacade request) {
                        HashSet<String> preferences = // 取出你保存的cookies
                        for (String cookie : preferences) {
                            request.addHeader("Cookie", cookie);
                            Log.v("OkHttp", "Adding Header: " + cookie);
                        }
            }
        });

好了运行项目,接口返回200,开心的抽了根烟冷静冷静。抽完回来再次运行   401了!!!又接着尝试,有时候401,有时候200.

检查了N遍,登录成功返回的cookies和请求时塞入head的cookies。没毛病啊。

这个问题从早上九点坐着对着电脑到晚上九点。提出了无数猜想。

直接说最后一个猜想:上面代码中保存的cookies是用HashSet。我猜是因为它无序导致的。同事们都不信是这个问题。但是我没招了还是试试吧。

于是我将HashSet换成了ArrayList。

成了。真的是这个问题。(感觉这辈子不想再用HashSet了)


上面就是我在项目中,找到的解决办法,但是总感觉怪怪的,后来我又查资料进行尝试,找到了比较方便的方式。

注:下面的两个类是用kotlin的语言编写的,(与java兼容,不知道的同学该学一学了)。

下面先贴出,拦截请求头的cookies,直接存到缓存中:

package com.reliable.mihophysical.api

import android.content.Context
import android.text.TextUtils

import java.io.IOException
import java.util.HashSet

import okhttp3.Interceptor
import okhttp3.Response

/**
 * 自定义请求拦截器
 * Created by ge
 */

class SaveCookiesInterceptor(private val mContext: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)
        //set-cookie可能为多个
        if (!response.headers("set-cookie").isEmpty()) {
            val cookies = response.headers("set-cookie")
            val cookie = encodeCookie(cookies)
            saveCookie(request.url().toString(), request.url().host(), cookie)
        }

        return response
    }

    //整合cookie为唯一字符串
    private fun encodeCookie(cookies: List<String>): String {
        val sb = StringBuilder()
        val set = HashSet<String>()
        for (cookie in cookies) {
            val arr = cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            for (s in arr) {
                if (set.contains(s)) continue
                set.add(s)

            }
        }

        val ite = set.iterator()
        while (ite.hasNext()) {
            val cookie = ite.next()
            sb.append(cookie).append(";")
        }

        val last = sb.lastIndexOf(";")
        if (sb.length - 1 == last) {
            sb.deleteCharAt(last)
        }

        return sb.toString()
    }

    //保存cookie到本地,这里我们分别为该url和host设置相同的cookie,其中host可选
    //这样能使得该cookie的应用范围更广
    private fun saveCookie(url: String, domain: String, cookies: String) {
        val sp = mContext.getSharedPreferences(COOKIE_PREF, Context.MODE_PRIVATE)
        val editor = sp.edit()

        if (TextUtils.isEmpty(url)) {
            throw NullPointerException("url is null.")
        } else {
            editor.putString(url, cookies)
        }

        if (!TextUtils.isEmpty(domain)) {
            editor.putString(domain, cookies)
        }

        editor.apply()

    }

    companion object {

        private val COOKIE_PREF = "cookies_prefs"
    }
}

然后是添加请求头的cookies的类:

package com.reliable.mihophysical.api

import android.content.Context
import android.text.TextUtils

import java.io.IOException

import okhttp3.Interceptor
import okhttp3.Response

/**
 * 自定义请求拦截器
 * Created by ge 
 */

class AddCookiesInterceptor(private val mContext: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val builder = request.newBuilder()
        val cookie = getCookie(request.url().toString(), request.url().host())
        if (!TextUtils.isEmpty(cookie)) {
            builder.addHeader("Cookie", cookie!!)
        }

        return chain.proceed(builder.build())
    }

    private fun getCookie(url: String, domain: String): String? {
        val sp = mContext.getSharedPreferences(COOKIE_PREF, Context.MODE_PRIVATE)
        if (!TextUtils.isEmpty(url) && sp.contains(url) && !TextUtils.isEmpty(sp.getString(url, ""))) {
            return sp.getString(url, "")
        }
        if (!TextUtils.isEmpty(domain) && sp.contains(domain) && !TextUtils.isEmpty(sp.getString(domain, ""))) {
            return sp.getString(domain, "")
        }

        return null
    }

    companion object {

        private val COOKIE_PREF = "cookies_prefs"
    }
}

最后同样在构建okhttp的builder时候:

// 自定义请求拦截
        okBuilder.addInterceptor(AddCookiesInterceptor(context!!))
        okBuilder.addInterceptor(SaveCookiesInterceptor(context!!))

这样就不用像最开始提到的方式一样,去手动找cookies存起来,在取出来手动添加了。



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值