IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter dio 使用 注意事项 -> 正文阅读

[移动开发]Flutter dio 使用 注意事项

dio 配置抓包代理

需要通过以下代码才能设置代理。

  //是否开启抓包功能
  static const bool isProxyEnable = true;
  //设置代理服务器地址和端口
  static const String proxy = "192.168.7.134:8888";
  init(){
  ...
//配置可以通过Fiddler抓包
    if(isProxyEnable){
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return isProxyEnable &&DebugModelUtil.isDebugMode;
        };
        client.findProxy = (url) {
          return  'PROXY $proxy' ;
        };
      };
    }  
     }

以上方法使用了一个工具用来判断app在debug模式还是release模式下

import 'package:flutter/foundation.dart';

bool _debug = kDebugMode; //constant下的一个常量

bool _release = kReleaseMode; //constant下的一个常量

///用于判断是否在debug模式下
class DebugModelUtil {
  static bool get isDebugMode {
    return _debug;
  }
}

post请求 参数需要放在data中

错误情况
测试代码如下,如果放在queryParameters 中,将会拼接到url中。

 Map<String, dynamic> params = {};
                    params['name']='zhangshan&a b c';
                    params['age']=24;
                    params['language']='中文';

                     HttpUtil.dio.post(
                        'http://192.168.1.1',
                        queryParameters: params,);

fiddler抓包如下
在这里插入图片描述

正确情况

测试代码

					 Map<String, dynamic> params = {};
                    params['name'] = 'zhangshan&a b c';
                    params['age'] = 24;
                    params['language'] = '中文';
                    HttpUtil.dio.post(
                      'http://192.168.1.1',
                      data: params,
                    );

fiddler抓包结果
在这里插入图片描述

指定content-type

在默认情况下,dio的content-type使用的是 application/json; charset=utf-8 而要使用其他的content-type。需要如下使用,有两种方法

通过header指定

代码如下

 				 Map<String, dynamic> params = {};
                    params['name'] = 'zhangshan&a b c';
                    params['age'] = 24;
                    params['language'] = '中文';
                    HttpUtil.dio.post('http://192.168.1.1',
                        data: params,
                        options: Options(headers: {
                          Headers.contentTypeHeader:
                              Headers.formUrlEncodedContentType
                        }));

抓包结果如下:
在这里插入图片描述

通过content-type指定

代码如下

				 Map<String, dynamic> params = {};
                    params['name'] = 'zhangshan&a b c';
                    params['age'] = 24;
                    params['language'] = '中文';
                    HttpUtil.dio.post('http://192.168.1.1',
                        data: params,
                        options: Options(
                            contentType: 'application/x-www-form-urlencoded'));

抓包结果
在这里插入图片描述
以上两种方法需要注意的点如下

  • 内容不一样,header中使用的是一个Headers中的常量内容是application/x-www-form-urlencoded;charset=utf-8 而contentType中并没有charset这部分。

  • 如果指定了类型为application/x-www-form-urlencoded 则dio会对data中的数据自动编码。比如中文和空格。在抓包结果中可以看到。

  • multipart/form-data 不支持这种方式,下面会介绍。

使用 multipart/form-data (附带上传文件)

上面介绍的方法对multipart/form-data 这种类型不支持。需要单独编码。因为这种类型比较复杂,还可以上传文件。

测试代码

 					Map params = {};
                    params['name']='zhangshan&a b c';
                    params['age']=24;
                    params['language']='中文';
                    //上传文件
                    var mtp= MultipartFile.fromString('abcdef',filename: 'text.file') ;
                    //携带其他参数
                    var formData = FormData.fromMap({'file':mtp,...params});
                    HttpUtil.dio.post(
                        'http://newapp.jyb.cn/app_pub/',
                        data: formData,
                      );

抓包结果
在这里插入图片描述
MultipartFile类具有很多方便的静态方法,可以很轻松的获取一个文件。
在这里插入图片描述

指定返回类型

如果你请求的时候指定了返回类型,dio会自动帮你转化,比如你指定类型为一个json。则拿到的response.data 属性就会是一个Map。 这个主要是通过设置option的responseType来实现的。
代码如下

					 Map params = {};
                    params['name']='zhangshan&a b c';
                    params['age']=24;
                    params['language']='中文';

                    HttpUtil.dio.post(
                        'http://newapp.jyb.cn/app_pub/',
                        data: params,
                        options: Options(responseType: ResponseType.json)
                      );

在这里插入图片描述

附带一个网络访问工具的封装

import 'dart:io';

import 'package:connectivity/connectivity.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:dio_log/interceptor/dio_log_interceptor.dart';
import 'package:tibet_wxb/common/http/mock_data_interceptor.dart';
import 'package:tibet_wxb/common/util/dev_model_check.dart';
import 'package:tibet_wxb/common/util/web_util.dart';

typedef JsonParseFun<T> = T Function(Map<String, dynamic> json);
typedef StringParseFun<T> = T Function(String str);

enum DioMethod {
  get,
  post,
  put,
  delete,
  patch,
  head,
}

class HttpUtil {
  static late Dio _dio;
  static const int _connectTimeout = 30 * 1000; //15s
  static const int _receiveTimeout = 30 * 1000;
  static const int _sendTimeout = 30 * 1000;
  static DioCacheManager? _dioCacheManager;
  //是否开启抓包功能
  static const bool isProxyEnable = true;
  //设置代理服务器地址和端口
  static const String proxy = "192.168.7.134:8888";

  static init() {
    /// 全局属性:请求前缀、连接超时时间、响应超时时间
    var options = BaseOptions(
      /// 请求的Content-Type,默认值是"application/json; charset=utf-8".
      /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
      /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]就会自动编码请求体.
      responseType: ResponseType.json,
      validateStatus: (status) {
        // 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格)
        return true;
      },
      connectTimeout: _connectTimeout,
      receiveTimeout: _receiveTimeout,
      sendTimeout: _sendTimeout,
    );
    _dio = Dio(options);
    //添加网络日志监听
    _dio.interceptors.add(DioLogInterceptor());
    //添加网络缓存
    if (PlatformUtil.isInMobile) {
      _dioCacheManager = DioCacheManager(CacheConfig());
      _dio.interceptors.add(_dioCacheManager!.interceptor);
    }
    //添加对模拟数据的处理
    _dio.interceptors.add(MockDataInterceptor());

    //配置可以通过Fiddler抓包
    if(isProxyEnable){
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return isProxyEnable &&DebugModelUtil.isDebugMode;
        };
        client.findProxy = (url) {
          return  'PROXY $proxy' ;
        };
      };
    }
  }

  static void clearCache() {
    _dioCacheManager!.clearAll();
  }
  static Dio get dio{
    return _dio;
  }

//只获取string
  static Future<String> requestString<T>(
    String path, {
    DioMethod method = DioMethod.get,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headers,
    data,
    CancelToken? cancelToken,
    Options? options,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    options??=Options();
    var str = await request<String>(
      path,
      stringParseFun: (str)=>str,
      method: method,
      params: params,
      headers: headers,
      data: data,
      cancelToken: cancelToken,
      options: options.copyWith(
        responseType: ResponseType.plain
      ),
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );

    return str ?? "";
  }

  /// 对网络访问的统一封装
  /// 如果请求的数据是string的话,那么需要传入stringParseFun 方法
  /// 如果返回的数据格式是json的话,需要传入jsonParseFun 方法
  static Future<T?> request<T>(
    String path, {
    JsonParseFun<T>? jsonParseFun,
    StringParseFun<T>? stringParseFun,
    DioMethod method = DioMethod.get,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headers,
    data,
    CancelToken? cancelToken,
    Options? options,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    const _methodValues = {
      DioMethod.get: 'get',
      DioMethod.post: 'post',
      DioMethod.put: 'put',
      DioMethod.delete: 'delete',
      DioMethod.patch: 'patch',
      DioMethod.head: 'head'
    };

    options ??= Options();
    options=options.copyWith(method: _methodValues[method],headers: headers);
    print('options content type is ${options.contentType}');
    try {
      Response response;
      response = await _dio.request(path,
          data: data,
          queryParameters: params,
          cancelToken: cancelToken,
          options: options,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress);
      if (response.statusCode == 200) {
        if (stringParseFun != null) {
          return stringParseFun(response.data);
        }

        if (response.data is Map && jsonParseFun != null) {
          return jsonParseFun(response.data);
        }
      } else {
        throw DioError(
            requestOptions: response.requestOptions,
            type: DioErrorType.other,
            response: response,
            error: "服务器错误:状态码为" + response.statusCode.toString());
      }
    } on DioError catch (e) {
      onErrorInterceptor(e);
      rethrow;
    }
  }

  // 对错误添加更好的语义化处理

  static void onErrorInterceptor(DioError err) async {
    // 异常分类
    switch (err.type) {
      // 4xx 5xx response
      case DioErrorType.response:
        err.requestOptions.extra["errorMsg"] = err.response?.data ?? "连接异常";
        break;
      case DioErrorType.connectTimeout:
        err.requestOptions.extra["errorMsg"] = "连接超时";
        break;
      case DioErrorType.sendTimeout:
        err.requestOptions.extra["errorMsg"] = "发送超时";
        break;
      case DioErrorType.receiveTimeout:
        err.requestOptions.extra["errorMsg"] = "接收超时";
        break;
      case DioErrorType.cancel:
        err.requestOptions.extra["errorMsg"] =
            err.message.isNotEmpty ? err.message : "取消连接";
        break;
      case DioErrorType.other:
      default:
        var connectivityResult = await (Connectivity().checkConnectivity());
        //判断是否有网络
        if (connectivityResult == ConnectivityResult.none) {
          err.requestOptions.extra["errorMsg"] = "网络未连接";
          break;
        }
        err.requestOptions.extra["errorMsg"] = "连接异常";
        break;
    }
  }
}

对模拟数据的支持

使用拦截器实现

import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'dart:convert';

class MockDataInterceptor extends Interceptor{

  static const String local_path='http://127.0.0.1/';

  @override
  Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    var path = options.path;

    if(path.startsWith(local_path)){
      try{
        String relPath= "assets/mock_data/"+path.substring(local_path.length);
        String jsonStr=await rootBundle.loadString(relPath);
        Response response=Response(requestOptions: options);
        response.data=json.decode(jsonStr);
        response.statusCode=200;
        handler.resolve(response);
      }catch (e){
        DioError dioError=DioError(requestOptions: options,error: "解析模拟数据错误$e");
        handler.reject(dioError);
      }

    }else{
      handler.next(options);
    }
  }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 22:19:09  更:2022-03-17 22:19:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 18:50:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码