Flutter Dio包网络请求抓包解决方案

GNU精神
• 阅读 8997

在Flutter中进行网络请求时,我们可以使用的库有3个,即Http请求库、HttpClient请求库和Dio请求库(详细介绍请参考:Flutter开发之Http网络请求),使用得最多的就是Dio请求库。因为相比Http请求库和HttpClient请求库,Dio库不仅支持常见的网络请求,还支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等操作。

不过,默认情况下,Dio进行网络请求时是不支持抓包的,所以如果要进行抓包,就需要对Dio进行请求封装,并编写代理代码。下面是代理的几种写法:

方法一

我们可以直接在Dio里面设置ip以及端口,通过硬编码的方式进行代理,代码如下:

(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        //这一段是解决安卓https抓包的问题
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return Platform.isAndroid;
        };
        client.findProxy = (uri) {
          return "PROXY 代理ip:代理port";
        };
      };

不过,这种硬编码方式,写得太死,不够灵活,每次更改代理都需要打包。

方法二

直接在原生插件获取手代理ip和代理端口,不过Android比较难,下面是iOS的实现。

 //自动获取手机代理
  NSDictionary *proxySettings = (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
           NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:call.arguments]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
           NSString *hostName = proxySettings[@"HTTPSProxy"];
           NSString *portName = [NSString stringWithFormat:@"%@",proxySettings[@"HTTPPort"]];
           long HTTPEnable = [proxySettings[@"HTTPEnable"] longValue];
           if (HTTPEnable==0) {
               hostName = @"";
           }

方法三

除了上面的硬编码方式外,我们还可以采用scheme协议的方式传入代理ip和代理端口。此方法的步骤如下:
1,注册自己的URL Scheme,例如:scheme://
2,定义参数规则,例如:scheme://tiaoshi?host=10.0.206.163
3,引入flutter插件:uni_links: ^0.2.0
4,flutter监听解析参数,并在dio里面设置代理
5,使用[草料]https://cli.im生成一个二维码:内容:scheme://tiaoshi?host=10.0.206.163
6,使用原生相机扫码进入app就可以抓包

下面是涉及的代码,Flutter代码如下:

Future<Null> initUniLinks() async {
   
    // 监听插件scheme数据
      getLinksStream().listen((String link) {
        link =  Uri.decodeComponent(link);
        if(link.contains("scheme://")){
          String type = getTypeStr(link);
          String param = link.replaceAll("scheme://$type?", "");
          Map dict = getUrlParams(param);
          if(type=="tiaoshi"){//设置抓包代理
            String host = dict["host"];
            String port = dict["port"];
            //这里是网络请求封装
            Net.setHttpProxy(host,port==null?"8888":port);
           }
        }
      // Parse the link and warn the user, if it is not correct
    }, onError: (err) {
      // Handle exception by warning the user their action did not succeed
    });
  }

//获取scheme 要处理的业务类型
  String getTypeStr(String link){
    List params = link.split("?");
    String typeStr = params[0];
    typeStr =  typeStr.replaceAll("scheme://", "");
    return typeStr;
  }

//url参数转map
  Map getUrlParams(String paramStr) {
    Map map = Map();
    List params = paramStr.split("&");
    for(int i=0;i<params.length;i++){
      String str = params[i];
      List arr = str.split("=");
      map[arr[0]]= arr[1];
    }
    return map;
  }

代理层代码:

static void setHttpProxy(String host,String port) {
    Application.httpProxy = host+':'+port;
    _initDio();
  }

static Future<void> _initDio() async {
    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
    if (Platform.isAndroid) {
      _androidInfo = await deviceInfo.androidInfo;
    } else if (Platform.isIOS) {
      _iosInfo = await deviceInfo.iosInfo;
    }
    
    _dio = Dio(BaseOptions(
      contentType: 'application/json',
      baseUrl: Config.BASE_URL,
    ));
    _dio.options.receiveTimeout = 5000;
    _dio.options.connectTimeout = 10000;

    if (Application.httpProxy.length != 0) {
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        //这一段是解决安卓https抓包的问题
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return Platform.isAndroid;
        };
       //这是抓包代理
        client.findProxy = (uri) {
          return "PROXY ${Application.httpProxy}";
        };
      };
    }
    _dio.interceptors.addAll([
      InterceptorsWrapper(
        onRequest: (Options options) {
          options.headers['DeviceName'] = 'xxxx';
          return options;
        },
        onResponse: (Response res) {
          try {

          ...

            return res;
          } catch (e) {
            return res;
          }
        },
        onError: (DioError e) {
          print(e);
         }
              break;
            default:
          }
          return e;
        },
      ),
    ]);
  }

static Future<ResponseModel> get(
    String path, {
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    void Function(int, int) onReceiveProgress,
  }) async {
    if (_dio == null) {
      await _initDio();
    }

    final res = await _dio.get<ResponseModel>(
      path,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onReceiveProgress: onReceiveProgress,
    );

    return res.data;
  }

  static Future<ResponseModel> post(
    String path, {
    dynamic data,
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    void Function(int, int) onSendProgress,
    void Function(int, int) onReceiveProgress,
  }) async {
    if (_dio == null) {
      await _initDio();
    }
    final res = await _dio.post<ResponseModel>(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );

    return res.data;
  }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Flutter 异步编程指南
在App开发中,经常会遇到处理异步任务的场景,如网络请求、读写文件等。Android、iOS使用的是多线程,而在Flutter中为单线程事件循环,如下图所示
希望的天 希望的天
4年前
Retrofit封装Okhttp逻辑原理
总结自retrofit封装了Okhttp本身并不能进行网络请求。只能在Android使用的网络请求框架。1.png2.pngrequest:统一完成(post/get/...)回调陷阱:完成上一步网络请求才能进行下一步网络请求。3.pngRetrofit简化了网络请求。优化了网络请求的使用。4.png5.png7.pngbuild设计模式:参数》5个;
Stella981 Stella981
3年前
Flutter 网络请求库http
http集成http库https://pub.dartlang.org/packages/http添加依赖dependencies:http:^0.12.0安装flutterpackagesget导入import'package:http/http.d
Easter79 Easter79
3年前
SpringMvc接受特殊符号参数被转义
WEB开发时,在前端通过get/post方法传递参数的时候 如果实参附带特殊符号,后端接收到的值中特殊符号就会被转义例如该请求: http://localhost:10001/demo/index.do?name张三(1)注:中文()不会出现此种情况后台就收到的实际name值为:  张三&40;1&41;&40;其实为h
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Flutter dio伪造请求头获取数据
在很多时候,后端为了安全都会有一些请求头的限制,只有请求头对了,才能正确返回数据。这虽然限制了一些人恶意请求数据,但是对于我们聪明的程序员来说,就是形同虚设。下面就以极客时间为例,讲一下通过伪造请求头,来获取极客时间首页主要数据。(不保证接口和安全措施一直可用哦)查看极客时间的数据端口如果你是一个前端,这套流程可能已经烂熟于心,先找出
liam liam
1年前
全面解读 Axios 的 GET 请求:最佳实践
在进行网络请求时,是一个非常常用的请求库。本文将介绍如何使用axios发起GET请求,并详细列出传参的几种写法。同时会提供一个实践案例,其中包含基本路由与请求处理的过程,并确保在IDE编辑器中可以顺利运行。什么是axios的GET请求?在开始之前,让我们简