乐趣区

关于flutter:Flutter-Dio包网络请求抓包解决方案

在 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;
  }
退出移动版