美文网首页NettoolAndroid知识
OkHttp中Interceptor拦截器之公共参数请求封装

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

作者: 菜鸟_一枚 | 来源:发表于2017-02-25 18:30 被阅读1529次

    前言

    之前在面试的时候遇到这样的一个问题,那就是如果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

    相关文章

      网友评论

      • 爱吃土豆的土豆:你好 为啥if(requestBody instanceof FormBody)返回的是false呢
      • 85cdfbeedeff:楼主我这里有这样一个需求就是在用户登陆成功的时候 返回一个Token 然后 在接下来的所有请求中都要加入带有请求头Token的请求怎样实现楼主
        85cdfbeedeff:@若兰明月 谢谢楼主
        菜鸟_一枚:@婆娑大梦_44f7
        在拦截器中这样写
        Request.Builder builder = original.newBuilder();
        if (!TextUtils.isEmpty(Account.getToken()))
        //Account.getToken() 这个是获取的本地token
        builder.addHeader("token", Account.getToken());
        builder.addHeader("Content-Type", "application/json");
        85cdfbeedeff:我的扣扣号 852263890希望得到楼主的指点

      本文标题:OkHttp中Interceptor拦截器之公共参数请求封装

      本文链接:https://www.haomeiwen.com/subject/lbrnwttx.html