flutter封装的dio网络请求

实际项目里用到的对dio的封装,稳定运行很久 满足你的各种需求
用法如下,可以支持toast以及网络请求中是否有loading框
最后面有demo地址,你可以直接拿来用

  Map map = await JDApiRequest.post('xxxx')
          .params({'action': 'register', 'mobile': ''1234324341})
          .needToastError(true)
          .needLoading(true)
          .request;


enum JDRequestMethod {
  get,
  post, // 默认 json 格式
  postWithFormData,
}

typedef OnRequestStart = Function(String requestTag);
typedef OnRequestSuccess = Function(String requestTag);
typedef OnRequestFail = Function(
    String requestTag, Object exception, StackTrace stackTrace);

class JDRequestModel {
  final String code;
  final String msg;
  final dynamic data;

  JDRequestModel(this.code, this.msg, this.data);
}

@immutable
class JDRequestListener {
  /// 请求开始
  final OnRequestStart? onRequestStart;

  /// 请求成功
  final OnRequestSuccess? onRequestSuccess;

  /// 请求失败
  final OnRequestFail? onRequestFail;

  /// [toString]输出的文字
  final String? toStringText;

  const JDRequestListener(
      {this.onRequestStart,
      this.onRequestSuccess,
      this.onRequestFail,
      this.toStringText});

  @override
  String toString() => toStringText ?? super.toString();
}

typedef HookApiServiceHeaders = Map<String, dynamic> Function();

class JDApiRequest {
  static HookApiServiceHeaders? httpHeaders;
  static late String apiServiceUrl;

  final String _url;
  final JDRequestMethod _method;
  Map<String, dynamic>? requestParams;

  // 超时时间
  int? timeOutSecond;

  bool _needToast = false;
  bool _needFilterRequest = true;
  String? _requestTag;
  CancelToken? _cancelToken;
  JDRequestListener? _loadListener;

  /// 请求工具
  JDApiRequest._(this._method, this._url);

  /// GET 请求
  factory JDApiRequest.get(String url) =>
      JDApiRequest._(JDRequestMethod.get, url);

  /// POST 请求
  factory JDApiRequest.post(String url) =>
      JDApiRequest._(JDRequestMethod.post, url);

  /// POST 请求 通过 表单
  factory JDApiRequest.postFormData(String url) =>
      JDApiRequest._(JDRequestMethod.postWithFormData, url);

  /// 携带的参数
  JDApiRequest params(Map<String, dynamic>? params) {
    try {
      requestParams = params;
    } catch (error) {
      TDToast.showDebug('赋值失败($_url),请检查log');
    }
    return this;
  }

  /// 指定的请求[tag] 等于空串时不输出请求日志
  /// 需配合[needFilterRequest]一起使用
  JDApiRequest tag(String tag) {
    _requestTag = tag;
    return this;
  }

  JDApiRequest setTimeOut(int time) {
    timeOutSecond = time;
    return this;
  }

  /// 请求失败时是否通过toast提示
  JDApiRequest needToastError(bool need) {
    _needToast = need;
    return this;
  }

  /// 需要过滤请求,默认true,设置为false不参与过滤
  JDApiRequest needFilterRequest(bool need) {
    _needFilterRequest = need;
    return this;
  }

  /// 设置加载回调
  JDApiRequest loadListener(JDRequestListener? loadListener) {
    _loadListener = loadListener;
    return this;
  }

  JDApiRequest needLoading(bool needLoading) {
    if (needLoading) {
      _loadListener = JDApiRequest.defaultListener;
    }
    return this;
  }

  /// 设置取消的token
  JDApiRequest cancelToken(CancelToken? cancelToken) {
    _cancelToken = cancelToken;
    return this;
  }

  /// 发起请求
  Future<Map<String, dynamic>> get request => _startRequest(this);

  /// 生成请求Tag
  static String _generateRequestTag(JDApiRequest request) {
    final requestTime = DateTime.now().millisecondsSinceEpoch / 1000;
    if (request._method == JDRequestMethod.get) {
      return json.encode({
        'method': 'GET',
        'url': request._url,
        'params': request.requestParams,
        'requestTime': requestTime
      });
    } else if (request._method == JDRequestMethod.post) {
      return json.encode({
        'method': 'POST',
        'url': request._url,
        'params': request.requestParams,
        'requestTime': requestTime
      });
    } else if (request._method == JDRequestMethod.postWithFormData) {
      return json.encode({
        'method': 'POST_WITH_FORM_DATA',
        'url': request._url,
        'params': null,
        'requestTime': requestTime
      });
    }
    return request.hashCode.toString();
  }

  /// 生成请求路径
  static String _generateRequestUrl(JDApiRequest request) {
    if (request._url.startsWith(r'http://') == true ||
        request._url.startsWith(r'https://') == true) {
      return request._url;
    } else {
      return "${JDApiRequest.apiServiceUrl}${request._url}";
    }
  }

  /// 生成请求配置
  static Future<Map<String, dynamic>> _generateRequestParams(
      JDApiRequest request) async {
    Map<String, dynamic> baseMap =
        AppInitConfig.instance.requestParam.getRequestMap();
    // tdLog('请求参数11  $baseMap');
    baseMap.addAll(request.requestParams ?? {});
    // tdLog('请求参数22  $baseMap');
    return baseMap;
  }

  /// 生成请求配置
  static Options _generateRequestOption(JDApiRequest request) {
    JDRequestMethod method = request._method;
    String contentType;
    if (JDRequestMethod.post == method) {
      contentType = ContentType.json.toString();
    } else if (JDRequestMethod.postWithFormData == method) {
      contentType = ContentType('multipart', 'form-data').toString();
    } else {
      contentType = ContentType.binary.toString();
    }
    int timeOut = request.timeOutSecond ?? 7;
    Options options =
        Options(method: JDRequestMethod.get == method ? "GET" : "POST")
          ..headers = {
            ...?httpHeaders?.call(),
            HttpHeaders.acceptHeader: '*/*',
            HttpHeaders.contentTypeHeader: contentType,
          }
          ..sendTimeout = Duration(seconds: timeOut)
          ..receiveTimeout = Duration(seconds: timeOut);
    return options;
  }

  static get defaultListener {
    return JDRequestListener(
      onRequestStart: (String requestTag) {
        return JDLoadingTool.showLoading(tag: requestTag);
      },
      onRequestSuccess: (String requestTag) {
        return JDLoadingTool.dismissLoading(tag: requestTag);
      },
      onRequestFail:
          (String requestTag, Object exception, StackTrace stackTrace) {
        return JDLoadingTool.dismissLoading(tag: requestTag);
      },
      toStringText: 'defaultListener',
    );
  }
}

final Set<String?> _requestingList = {};

class TDDioInstance {
  Dio dio = Dio();
  factory TDDioInstance() => _instance;
  static final TDDioInstance _instance = TDDioInstance._internal();
  TDDioInstance._internal();
}

/// 开始请求
Future<Map<String, dynamic>> _startRequest(JDApiRequest request) async {
  final requestTag =
      request._requestTag ?? JDApiRequest._generateRequestTag(request);
  if (_requestingList.contains(requestTag) && request._needFilterRequest) {
    jdLog("重复请求中($request)..");
  }
  final startTime = DateTime.now();
  try {
    _requestingList.add(requestTag);
    _runZonedIfNotNull(request._loadListener?.onRequestStart?.call(requestTag));
    final resp = await _requestInner(request, requestTag);
    _runZonedIfNotNull(
        request._loadListener?.onRequestSuccess?.call(requestTag));
    jdLog("请求成功结束----->  ${request._url} $resp");
    if (!resp.responseSucceed()) {
      if (request._needToast) {
        TDToast.showToast(resp.getStringNotNull(TDResponseConstant.message));
      }
    }
    return Future.value(resp);
  } catch (exception, stackTrace) {
    String errorMsg = '';
    int errorCode = 0;
    if (exception is JDApiException) {
      errorMsg = exception.msg;
      errorCode = exception.code;
      jdLog(
          '请求异常1:error--->  ${exception.msg} requestTag--> $requestTag stackTrace---> ${stackTrace.toString()}');
      if (request._needToast) {
        TDToast.showToast(exception.msg);
      }
    } else {
      jdLog('请求异常2---> ${exception.toString()}');
      errorMsg = exception.toString();
      errorCode = -101;
      if (request._needToast) {
        TDToast.showToast(errorMsg);
      }
    }
    _runZonedIfNotNull(request._loadListener?.onRequestFail
        ?.call(requestTag, exception, stackTrace));
    return {
      'error': {'errorMsg': errorMsg, 'errorCode': errorCode}
    };
  } finally {
    _requestingList.remove(requestTag);
    final duration = DateTime.now().duration - startTime.duration;
    if (duration > const Duration(seconds: 3)) {
      // TDLoggerUtil.e('接口请求时间过长:[tag: $requestTag, duration: $duration]');
    }
  }
}

/// 解析响应
Future<Map<String, dynamic>> _requestInner(
    JDApiRequest request, String requestTagOnce) async {
  try {
    // String requestShortTag = shortHash(requestTagOnce);
    String requestFullUrl = JDApiRequest._generateRequestUrl(request);
    Map<String, dynamic> params =
        await JDApiRequest._generateRequestParams(request);
    Options option = JDApiRequest._generateRequestOption(request);

    jdLog('开始请求requestUrl----->  ${request._url} 参数-->  $params');

    Response response = await TDDioInstance().dio.request(
          requestFullUrl,
          queryParameters:
              request._method == JDRequestMethod.get ? params : null,
          data: request._method == JDRequestMethod.postWithFormData ||
                  request._method == JDRequestMethod.post
              ? FormData.fromMap(params)
              : params,
          options: option,
          cancelToken: request._cancelToken,
        );
    Map<String, dynamic> responseMap = {};
    dynamic responseData = response.data;
    if (responseData is String) {
      responseData = json.decode(responseData);
    }
    responseMap = responseData;

    return responseMap;
  } on JDApiException {
    rethrow;
  } on DioException catch (exception) {
    if (kDebugMode) {
      throw ApiNetworkException(exception.response?.statusCode ?? -1,
          '${exception.message}', request, exception);
    } else {
      throw ApiNetworkException(exception.response?.statusCode ?? -1,
          '连接服务器失败,请检查网络~', request, exception);
    }
  } catch (exception) {
    if (kDebugMode) {
      throw JDApiException(
          -101, '系统出现未知错误\n(${exception.toString()})', request, exception);
    } else {
      throw JDApiException(-101, '系统出现未知错误', request, exception);
    }
  }
}

_runZonedIfNotNull<R>(R Function()? body) {
  if (body != null) runZoned(body);
}


具体的封装实现在https://gitee.com/kuaipai/my_app.git

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

推荐阅读更多精彩内容