Flutter 网络请求之dio

4,481 阅读2分钟

pubspec.yaml导包

dependencies:
  flutter:
    sdk: flutter
  dio: ^3.0.9 #请求库
  dio_cookie_manager: ^1.0.0  #请求cookie
  path_provider: ^1.6.11	#文件存储

请求目录

定义接口地址

class Api{
  static const String BASE_URL = 'http://xxxxxx';
  static const String LOGIN = BASE_URL + 'login/auth';
  static const String BULLETINS = BASE_URL + 'bulletins';
}

初始化dio,设置相关参数,token问题类外部顶层方法中设置,会丢失。在InterceptorsWrapper里面设置token,这里也可以处理token时效问题;例如请求服务器当前token是否有效,重新创建dio去请求,类外部的dio对象要lock。例如有个场景就是token过期,跳转到登录页面。

var dio = new Dio();

Map<String, dynamic> headers = {
  'content-type': 'application/json',
  'accept-language': 'zh-cn',
};

initOption() {
  dio.options
    ..baseUrl = Api.BASE_URL
    ..connectTimeout = 300000
    ..headers = headers;
}

class DioNet {
  // GET
  static Future get(String url, [Map<String, dynamic> params]) async {
    Response response;
    BotToast.showLoading();
    print('GetMothod--->$url\nparams--->$params');
    Directory documentsDir = await getApplicationDocumentsDirectory();
    String documentsPath = documentsDir.path;
    var dir = new Directory("$documentsPath/cookies");
    await dir.create();
    dio.interceptors
      ..add(CookieManager(PersistCookieJar(dir: dir.path)))
      //拦截设置token,在外部设置获取不到
      ..add((InterceptorsWrapper(
        onRequest: (Options options) async {
          var token = await SpUtil().getValue(SharePrefConstant.TOKEN);
          options.headers.addAll({'SESSION': token ?? ''});
          return options;
        },
      )));
    try {
      if (params != null) {
        response = await dio.get(url, queryParameters: params);
      } else {
        response = await dio.get(url);
      }
      BotToast.closeAllLoading();
      return response.data;
    } on DioError catch (err) {
      BotToast.closeAllLoading();
      //处理Error
      HttpError.dioError(err);
      return err;
    }
  }

  //POST
  static Future post(String url, Map<String, dynamic> params) async {
    BotToast.showLoading();
    print('PostMethod--->$url\nparams--->$params');
    Directory documentsDir = await getApplicationDocumentsDirectory();
    String documentsPath = documentsDir.path;
    var dir = new Directory("$documentsPath/cookies");
    await dir.create();
    dio.interceptors
      ..add(CookieManager(PersistCookieJar(dir: dir.path)))
      //拦截设置token,在外部设置获取不到
      ..add((InterceptorsWrapper(
        onRequest: (Options options) async {
          var token = await SpUtil().getValue(SharePrefConstant.TOKEN);
          options.headers.addAll({'SESSION': token ?? ''});
          return options;
        },
      )));
    try {
      var response = await dio.post(url, data: params);
      BotToast.closeAllLoading();
      return response.data;
    } on DioError catch (err) {
      BotToast.closeAllLoading();
      //处理Error
      HttpError.dioError(err);
      return err;
    }
  }
}

调用接口,可以根据服务器返回的状态码做对应的逻辑处理;

class RequestUtil {
  //登录
  static Future doLogin(Map<String, dynamic> params) async {
    var response = await DioNet.post(Api.LOGIN, params);
    // LoginInfo loginInfo = LoginInfo.fromJson(response);
    return getRespone(response);
  }

  //滚动消息
  static Future doBulletins(Map<String, dynamic> params) async {
    var response = await DioNet.get(Api.BULLETINS, params);
    return getRespone(response);
  }

//统一处理接口返回的结果
  static getRespone(response) {
    print("response--->$response");
    try {
      if (isSuccess(response)) {
        return response; //return调用接口的then才能获取到数据
      } else {
        switch (response['result']) {
          case '20011':
            //跳转指定页面并关闭当前所有页面
            Routers.finishAllToLoginPage();
            BotToast.showText(text: '9999');
            break;
          default:
            //处理Failure
            BotToast.showText(text: response['message']);
            break;
        }
      }
    } on StackTrace catch (e) {
      print("e----->$e");
    }
  }

  static bool isSuccess(dynamic result) {
    return (result != null && result['result'] == 'success') ? true : false;
  }
}

dio请求返回的错误码提示等处理

class HttpError {

  ///未知错误
  static const String UNKNOWN = "UNKNOWN";

  ///解析错误
  static const String PARSE_ERROR = "PARSE_ERROR";

  ///网络错误
  static const String NETWORK_ERROR = "NETWORK_ERROR";

  ///协议错误
  static const String HTTP_ERROR = "HTTP_ERROR";

  ///证书错误
  static const String SSL_ERROR = "SSL_ERROR";

  ///连接超时
  static const String CONNECT_TIMEOUT = "CONNECT_TIMEOUT";

  ///响应超时
  static const String RECEIVE_TIMEOUT = "RECEIVE_TIMEOUT";

  ///发送超时
  static const String SEND_TIMEOUT = "SEND_TIMEOUT";

  ///网络请求取消
  static const String CANCEL = "CANCEL";

  String code;

  String message;

  HttpError(this.code, this.message);

  HttpError.dioError(DioError error) {
    message = error.message;
    switch (error.type) {
      case DioErrorType.CONNECT_TIMEOUT:
        code = CONNECT_TIMEOUT;
        message = "网络连接超时,请检查网络设置";
        break;
      case DioErrorType.RECEIVE_TIMEOUT:
        code = RECEIVE_TIMEOUT;
        message = "服务器异常,请稍后重试!";
        break;
      case DioErrorType.SEND_TIMEOUT:
        code = SEND_TIMEOUT;
        message = "网络连接超时,请检查网络设置";
        break;
      case DioErrorType.RESPONSE:
        code = HTTP_ERROR;
        message = "服务器异常,请稍后重试!";
        break;
      case DioErrorType.CANCEL:
        code = CANCEL;
        message = "请求已被取消,请重新请求";
        break;
      case DioErrorType.DEFAULT:
        code = UNKNOWN;
        message = "网络异常,请稍后重试!";
        break;
    }
    BotToast.showText(text: message);
  }

  @override
  String toString() {
    return 'HttpError{code: $code, message: $message}';
  }
}

调用

RequestUtil.doLogin(params).then((result) {
                    if (RequestUtil.isSuccess(result)) {
                    //todo somethings
                    }