OkHttp中Interceptor拦截器之公共参数请求封装

前言

之前在面试的时候遇到这样的一个问题,那就是如果app中所有的请求都要加入一些参数(例如 版本号、手机号、登录用户名、token等。。。)那么怎么做才能实现,而不至于在每次请求的时候都去进行添加这些请求头。其实这个问题,刚开始我是拒绝的(之前没有遇到过这样的需求)。当时只是想着可以使用okhttp体用的拦截器Interceptor来进行实现,但是具体的实现还是来到了今天。

Interceptor说明

在okhttp-wiki里面专门有一篇介绍Interceptor的(点击我跳转到Interceptor链接)里面有这样的一句话

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
拦截器是一种强大的机制,可以监视、重写和重试调用

Interceptor几大作用
  • Application Interceptors 应用拦截器
  • Network Interceptors 网络拦截器
  • Choosing between application and network interceptors 在应用和网络拦截器之间做选择
  • Rewriting Requests 重写请求
  • Rewriting Responses 重写响应

具体的作用简介这里不再赘述,这里推荐一篇翻译的文章(Okhttp-wiki 之 Interceptors 拦截器

进入主题

先来看下我们的需求

需求约束

  • 1、API Base Url : http://111.111.111.11:8082/xxxxx/xx(类似)

  • 2、客户端请求API的数据格式为json形式:

    {
        "publicParams":{},
        "key1":value,
        "key2":value
    }
    

    其中publicParams为公共参数,每个API接口必须传递

  • 3、公共参数(每次请求都需要携带)

字段 类型 说明
imei string 移动设备身份(非必须)
model 手机型号、设备名称
la string 系统语言
resolution string 分辨率(格式:1920*1080)
densityScaleFactor string 密度比例
sdk int SDK版本号
os string 系统源代码控制值

解决方法

我们就是利用Interceptor拦截器,对每次的网络请求进行拦截,然后拿到请求url,并对url进行改造来加入我们需要的公共参数,进行和请求参数的拼接,然后构造request,通过chain.proceed(request)进行改造。

第一步

  • 构建一个CommonParamsInterceptor.class类继承自Interceptor
  • 复写intercept(Chain chain)方法,我们需要用到chain.request()来获取到request对象
  • request里面的方法

这里我们只需要用到url()方法(主要获取到api地址和请求参数,并进行改造),用到method()方法来判断get请求和post请求。
这个时候我们看下代码怎么进行编写吧。

 @Override
    public Response intercept(Chain chain) throws IOException {

        //获取到request
        Request request = chain.request();

        //获取到方法
        String method = request.method();

        //公共参数hasmap

        try {

            //添加公共参数
            HashMap<String, Object> commomParamsMap = new HashMap<>();

            commomParamsMap.put(Constants.IMEI, DeviceUtils.getIMEI(mContext));
            commomParamsMap.put(Constants.MODEL, DeviceUtils.getModel());
            commomParamsMap.put(Constants.LANGUAGE, DeviceUtils.getLanguage());
            commomParamsMap.put(Constants.os, DeviceUtils.getBuildVersionIncremental());
            commomParamsMap.put(Constants.RESOLUTION, DensityUtil.getScreenW(mContext) + "*" + DensityUtil.getScreenH(mContext));
            commomParamsMap.put(Constants.SDK, DeviceUtils.getBuildVersionSDK() + "");
            commomParamsMap.put(Constants.DENSITY_SCALE_FACTOR, mContext.getResources().getDisplayMetrics().density + "");


            //get请求的封装
            if (method.equals("GET")) {
                //获取到请求地址api
                HttpUrl httpUrlurl = request.url();
                
                HashMap<String, Object> rootMap = new HashMap<>();
                //通过请求地址(最初始的请求地址)获取到参数列表
                Set<String> parameterNames = httpUrlurl.queryParameterNames();
                for (String key : parameterNames) {  //循环参数列表
                    if (Constants.PARM.equals(key)) {  //判断是否有匹配的字段  这个类似于  /xxx/xxx?p={}  匹配这个p
                        String oldParamsJson = httpUrlurl.queryParameter(Constants.PARM);
                        if (oldParamsJson != null) {  //因为有的是没有这个p={"page":0}  而是直接/xxx/index的
                            HashMap<String, Object> p = mGson.fromJson(oldParamsJson, HashMap.class);  //原始参数
                            if (p != null) {
                                for (Map.Entry<String, Object> entry : p.entrySet()) {
                                    rootMap.put(entry.getKey(), entry.getValue());
                                }
                            }
                        }
                    } else {
                        rootMap.put(key, httpUrlurl.queryParameter(key));
                    }
                }

                //String oldParamJson = httpUrlurl.queryParameter(Constants.PARM);

//            if (oldParamJson != null) {
//
//            }

                //把原来请求的和公共的参数进行组装
                rootMap.put("publicParams", commomParamsMap);  //重新组装

                String newJsonParams = mGson.toJson(rootMap);  //装换成json字符串

                String url = httpUrlurl.toString();
                int index = url.indexOf("?");
                if (index > 0) {
                    url = url.substring(0, index);
                }
                url = url + "?" + Constants.PARM + "=" + newJsonParams;  //拼接新的url

                request = request.newBuilder().url(url).build();  //重新构建请求


                //post请求的封装
            } else if (method.equals("POST")) {

//           FormBody.Builder builder = new FormBody.Builder();
//            builder.addEncoded("phone","phone");

                RequestBody requestBody = request.body();
                HashMap<String, Object> rootMap = new HashMap<>();
                if (requestBody instanceof FormBody) {
                    for (int i = 0; i < ((FormBody) requestBody).size(); i++) {
                        rootMap.put(((FormBody) requestBody).encodedName(i), ((FormBody) requestBody).encodedValue(i));
                    }
                } else {
                    //buffer流
                    Buffer buffer = new Buffer();
                    requestBody.writeTo(buffer);
                    String oldParamsJson = buffer.readUtf8();
                    rootMap = mGson.fromJson(oldParamsJson, HashMap.class);  //原始参数
                    rootMap.put("publicParams", commomParamsMap);  //重新组装
                    String newJsonParams = mGson.toJson(rootMap);  //装换成json字符串

                    request = request.newBuilder().post(RequestBody.create(JSON, newJsonParams)).build();
                }


            }
        } catch (JsonSyntaxException e) {
            e.printStackTrace();
        }
        //最后通过chain.proceed(request)进行返回
        return chain.proceed(request);
    }

第二步

当我们把自定义的Interceptor构建完成之后,我们需要在Okhttp中进行使用。

 return new OkHttpClient.Builder()
                //HeadInterceptor 实现了Intercepter  用来网Request  Header添加一些相关数据  如APP版本 token信息
//                .addInterceptor(new HttpLoggingInterceptor())
                //添加自定义的拦截器,完成公共参数的封装
                .addInterceptor(new CommonParamsInterceptor(gson,application))
                .connectTimeout(10, TimeUnit.SECONDS)//链接超时
                .readTimeout(10, TimeUnit.SECONDS)//设置读取超时
                .build();

这个时候就可以了,我们来看下拦截之前和加入拦截器之后的请求url,看看是否起到了作用。
这个时候我们debug一下看下就明白了。
我们可以看到下图中上面的参数是只有一个,但是第二行就已经改变了,添加过了我们需要的公共参数,并且还有我们请求的参数。符合我们的要求。


参考文章

Okhttp-wiki 之 Interceptors 拦截器
Okhttp-wiki之Interceptors

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容