Flutter网络请求dio封装

概要:

dio已经更新到4.0版本,相对已经很稳定了,不过为了通用性,如果直接应用在项目中,难免比较离散。一般项目开发都是多人共同协作,所以统一的规范,简洁的调用方式,稳定的性能是必不可少的,所以才有二次封装,方便实际项目中使用。

首先预览下文件结构:

Flutter网络请求dio封装

一、整体设计描述

  • Api 对应项目中业务开发时候的调用接口
  • Code定义了常见请求状态,例如网络错误、超时、成功等
  • DataHelper定义了数据加密工具代码
  • DioLogInterceptor页面Loading拦截器,用于网络数据返回加工预处理
  • HttpManager核心工具类,提供get和post请求入口
  • Loading网络请求等待页面处理
  • ResponseInterceptors拦截器,用于网络数据返回加工预处理
  • ResultData网络返回数据二次封装
  • UrlPath对业务URL统一管理

二、下面对Api、HttpManager、ResponseInterceptors、ResultData详解介绍

Api:业务开发时候,首先使用Api来获取网络数据

Future<Autogenerated> getData() async {
    ResultData resultData = await Api.request({});

    final autogenerated = resultData.data;
    final datas = autogenerated["data"]["datas"];

    print("-----------------------");
    print(datas);
    print("-----------------------");

    for(var sub in datas) {
      listData.add(Datas.fromJson(sub));
    }

    state.listData = listData;

    update();

    return autogenerated;
  }
/// Api实现
class Api {
  ///示例请求
  static request(Map<String, dynamic> param) {
    return HttpManager.getInstance().get(UrlPath.testPath, params: param);
  }

  static requestOther(Map<String, dynamic> param) {
    return HttpManager.getInstance(baseUrl: UrlPath.otherUrl)
        .post(UrlPath.testPath, params: param);
  }
}

HttpManager:Api调用的接口,封装在HttpManager中

///通用的GET请求
  get(api, {params, withLoading = true}) async {
    if (withLoading) {
      Loading.show();
    }

    Response response;

    params["platform"] = "android";
    params["system"] = "1.0.0";
    params["channel"] = "App";
    params["time"] = new DateTime.now().millisecondsSinceEpoch.toString();
    params["sign"] = DataHelper.encryptParams(params);

    final Options options = new Options(method: "get", headers: httpHeaders);
    try {
      response = await _dio.get(api, queryParameters: params, options: options);
      if (withLoading) {
        Loading.dismiss();
      }
    } on DioError catch (e) {
      if (withLoading) {
        Loading.dismiss();
      }
      return resultError(e);
    }

    if (response.data is DioError) {
      return resultError(response.data['code']);
    }

    return response.data;
  }

其中涉及到Headers和拦截器设置,要根据业务后端而定

///通用全局单例,第一次使用时初始化
  HttpManager._internal({String baseUrl}) {
    if (null == _dio) {
      _dio = new Dio(new BaseOptions(
        baseUrl: UrlPath.baseUrl,
        connectTimeout: CONNECT_TIMEOUT,
        receiveTimeout: RECEIVE_TIMEOUT,
        headers: httpHeaders,
        validateStatus: (status) {
          // 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格)
          return true;
        },
      ));
      _dio.interceptors.add(new DioLogInterceptor());
      _dio.interceptors.add(new ResponseInterceptors());
    }
  }
/// 自定义Header
  Map<String, dynamic> httpHeaders = {
    'Accept': 'application/json,*/*',
    'Content-Type': 'application/json',
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Credentials": true,
    "Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale",
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS, HEAD",
  };
///日志拦截器
class DioLogInterceptor extends Interceptor {
  ///请求前
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    String requestStr = "\n==================== REQUEST ====================\n"
        "- URL:\n${options.baseUrl + options.path}\n"
        "- METHOD: ${options.method}\n";

    requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";

    final data = options.data;
    if (data != null) {
      if (data is Map)
        requestStr += "- BODY:\n${data.mapToStructureString()}\n";
      else if (data is FormData) {
        final formDataMap = Map()
          ..addEntries(data.fields)
          ..addEntries(data.files);
        requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";
      } else
        requestStr += "- BODY:\n${data.toString()}\n";
    }
    // print(requestStr);

    handler.next(options);
  }
/// 拦截器 数据初步处理
class ResponseInterceptors extends InterceptorsWrapper {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    RequestOptions option = response.requestOptions;
    // print("---------response.data----------");
    // print(response.data);
    // print("---------response.data end----------");
    try {
      if (option.contentType != null && option.contentType.contains("text")) {
        response.data = ResultData(response.data, true, 200);
        handler.next(response);
      }

      ///一般只需要处理200的情况,300、400、500保留错误信息,外层为http协议定义的响应码
      if (response.statusCode == 200 || response.statusCode == 201) {
        ///内层需要根据公司实际返回结构解析,一般会有code,data,msg字段

        int code = response.data["errorCode"];
        if (code == 0) {
          response.data = ResultData(response.data, true, 200,
              headers: response.headers);
          handler.next(response);

          // print("----------response.data----------");
          // print(response.data);
          // print("----------response.data end----------");

          return;
        } else {
          response.data = ResultData(response.data, false, 200,
              headers: response.headers);
          handler.next(response);
          return;
        }
      }
    } catch (e) {
      print("ResponseError====" + e.toString() + "****" + option.path);

      response.data = ResultData(response.data, false, response.statusCode,
          headers: response.headers);
      handler.next(response);
      return;
    }

    response.data = ResultData(response.data, false, response.statusCode, headers: response.headers);
    handler.next(response);
  }

...
ResultData:拦截器中对返回数据进行了二次封装,具体实现要根据各自业务需要而定
class ResultData {
  var data;
  bool isSuccess;
  int code;
  var headers;

  ResultData(this.data, this.isSuccess, this.code, {this.headers});
}

这样,一个网络请求封装基本就搞定了,要根据业务需要,灵活运用。

项目地址:https://github.com/lizhongfu/Flutter_Network_Dio

 

 

 

 

上一篇:APP测试常见功能测试点


下一篇:最大矩阵连乘次数