美文网首页
Flutter Dio 网络请求

Flutter Dio 网络请求

作者: 番茄tomato | 来源:发表于2020-05-12 17:03 被阅读0次

    本来一直看书上,但是这部分讲的比较模糊不怎么好理解,所以就准备自己整理一下关于Dio网络请求的知识点。
    Dio Github地址:https://github.com/flutterchina/dio
    Github上列出了大多数使用场景,先好好看好好学。
    有人搬运并翻译的官方文档:
    https://blog.csdn.net/mqdxiaoxiao/article/details/102859897

    dio: ^3.0.9
    

    一 Json数据实体类

    类比于Android原生,我们的网络请求 如果是服务器返回JSON数据首先要有一个实体类来保存数据,Android Studio上有JAVA和Kotlin的根据Json数据生成实体类插件,当然也有Dart的生成实体类插件:
    FlutterJsonBeanFactory直接搜(直接搜“ FlutterJson”就可以)
    生成实体类:
    https://blog.csdn.net/yuzhiqiang_1993/article/details/88533166

    FlutterJsonBeanFactory

    二 Dio请求的基本使用

    2.1 get 请求百度首页“http://www.baidu.com”,获取其内容:

    这就是最简单的一个Dio使用例子

      Future<String> getBaiduContent() async {
        try {
          Response response = await Dio().get("http://www.baidu.com");
          print(response);
          return response.toString();
        } catch (e) {
          print(e);
        }
      }
    

    可以运行看一下:


    百度首页

    2.2 get 有Json数据的请求返回

    这里我采用的是极速数据的一个免费开放api:笑话接口
    get/post均可
    https://api.jisuapi.com/xiaohua/text?pagenum=1&pagesize=1&sort=addtime&appkey=******
    它返回的Json数据内容如下:

    {
        "status": 0,
        "msg": "ok",
        "result": {
            "total": 79630,
            "pagenum": 1,
            "pagesize": 1,
            "list": [
                {
                    "content": "王自健在节目中调侃,对于老婆打自己这件事没有任何不满,没有任何抱怨。 这反映了一个问题,在中国: 老婆就是用来爱的, 老公就是用来打的。 中国妇女的地位真的提高了,可喜可贺!",
                    "addtime": "2020-03-28 03:20:02",
                    "url": "http://m.kaixinhui.com/detail-128412.html"
                }
            ]
        }
    }
    

    根据这个数据使用FlutterJsonBeanFactory 生成数据实体类:


    实体类

    创建接口请求方法:

      Future<void> getJiSuJoke() async {
      Dio dio = Dio();
        try {
          Response response = await Dio()
            .get("https://api.jisuapi.com/xiaohua/text", queryParameters: {
            "pagenum": 1,
            "pagesize": 1,
            "sort": "rand",
            "appkey": "你的APPKEY"
          });
          print(response.data.toString());
        } catch (e) {
          print(e);
        }
      }
    

    调用一下这个方法就可以看到请求结果了。
    这里我们可以看到使用了queryParameters属性,类似于Retrofit中的@Query,将get方法“?”后边的值以map的形式传入,这样的好处是可以动态修改请求参数,灵活的修改请求方法传入的参数针对不同情况的接口调用.稍微修改一下之前的方法:

      Future<void> getJiSuJoke(int pagesize) async {
      Dio dio = Dio();
        int pagenum=1;
        Map<String,dynamic> mData = {
          "pagenum": pagenum,
          "pagesize": pagesize,
          "sort": "rand",
          "appkey": "35dc30ebaa5940ce"
        };
        try {
          Response response = await Dio()
              .get("https://api.jisuapi.com/xiaohua/text", queryParameters: mData);
          print(response.data.toString());
        } catch (e) {
          print(e);
        }
      }
    

    到这里其实我们还没有用到Json数据转化的实体类,请看:

      JisuJokeEntity jokeEntity;
      Future<void> getJiSuJoke(int pagesize) async {
        Dio dio = Dio(); //创建dio对象
        int pagenum = 1; //设置请求参数
        Map<String, dynamic> mData = {
          "pagenum": pagenum,
          "pagesize": pagesize,
          "sort": "rand",
          "appkey": "你的APPKEY"
        };
        try {
          //开始请求
          Response response = await dio
              .get("https://api.jisuapi.com/xiaohua/text",
              queryParameters: mData);
          //请求体结果response,将数据转化为实体类
          jokeEntity =
              JisuJokeEntity().fromJson(json.decode(response.data.toString()));
          print(response);
          print(jokeEntity.result.xList[0].content);
        } catch (e) {
          print(e);
        }
      }
    

    想要在日志里看到请求过程,只需要添加打印日志拦截即可:

       Dio dio = Dio(); //创建dio对象
       //添加请求拦截  LogInterceptor内 想看什么将什么传入ture
       dio.interceptors.add(LogInterceptor(responseBody: true,requestBody: true)); //开启请求日志
    

    post的用法和post传输数据官网上写的很清楚,后续补充

    二 Dio的单例模式和封装

    建议在项目中使用Dio单例,这样便可对同一个dio实例发起的所有请求进行一些统一的配置,比如设置公共header、请求基地址、超时时间等;

    之前我们在每个请求方法中都新建了一个Dio对象,这样其实是不推荐的,因为我们需要在整个项目中统一配置添加header,或者配置BaseUrl这些,所以推荐在一个项目中只使用一个Dio对象,方便统一管理。既然是这样,这就要求我们对Dio进行封装。
    这是我在网上找到了一个封装类,自己稍微修改了一下下,内置了get/post/downloadFile三个方法,待完善

    import 'package:dio/dio.dart';
    import 'api.dart';
    
    
    class HttpUtil {
      static HttpUtil instance;
      Dio dio;
      BaseOptions options;
    
      CancelToken cancelToken = CancelToken();
    
      static HttpUtil getInstance() {
        if (null == instance) instance = HttpUtil();
        return instance;
      }
    
      /*
       * config it and create
       */
      HttpUtil() {
        //BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
        options = BaseOptions(
          //请求基地址,可以包含子路径
          baseUrl: Api.BASE_URL,
          //连接服务器超时时间,单位是毫秒.
          connectTimeout: 10000,
          //响应流上前后两次接受到数据的间隔,单位为毫秒。
          receiveTimeout: 5000,
          //Http请求头.
          headers: {
            //do something
            "version": "1.0.0"
          },
          //请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体.
          contentType: Headers.formUrlEncodedContentType,
          //表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
          responseType: ResponseType.plain,
        );
    
        dio = Dio(options);
    
        //添加日志请求拦截 显示日志
    
        dio.interceptors.add(LogInterceptor(responseBody: true,requestBody: true)); //开启请求日志
    
    
        //Cookie管理 这个暂时不清楚
        //dio.interceptors.add(CookieManager(CookieJar()));
    
        //添加拦截器
        dio.interceptors
            .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
          //print("请求之前");
          // Do something before request is sent
          return options; //continue
        }, onResponse: (Response response) {
         // print("响应之前");
          // Do something with response data
          return response; // continue
        }, onError: (DioError e) {
         // print("错误之前");
          // Do something with response error
          return e; //continue
        }));
      }
    
      /*
       * get请求
       * options:单个请求自定义配置
       * data:query  ?后的数据
       *
       */
      get(url, {data, options, cancelToken}) async {
        Response response;
        try {
          response = await dio.get(url,
              queryParameters: data, options: options, cancelToken: cancelToken);
         // print('get success---------${response.statusCode}');
         // print('get success---------${response.data}');
    
    //      response.data; 响应体
    //      response.headers; 响应头
    //      response.request; 请求体
    //      response.statusCode; 状态码
    
        } on DioError catch (e) {
          print('get error---------$e');
          formatError(e);
        }
        return response;
      }
    
      /*
       * post请求
       *
       * formData:POST传递form表单
       */
      post(url, {queryData,formData, options, cancelToken}) async {
        Response response;
        try {
          response = await dio.post(url,data: formData,
              queryParameters: queryData, options: options, cancelToken: cancelToken);
          print('post success---------${response.data}');
        } on DioError catch (e) {
          print('post error---------$e');
          formatError(e);
        }
        return response;
      }
    
      /*
       * 下载文件
       */
      downloadFile(urlPath, savePath) async {
        Response response;
        try {
          response = await dio.download(urlPath, savePath,
              onReceiveProgress: (int count, int total) {
            //进度
            print("$count $total");
          });
          print('downloadFile success---------${response.data}');
        } on DioError catch (e) {
          print('downloadFile error---------$e');
          formatError(e);
        }
        return response.data;
      }
    
      /*
       * error统一处理
       */
      void formatError(DioError e) {
        if (e.type == DioErrorType.CONNECT_TIMEOUT) {
          // It occurs when url is opened timeout.
          print("连接超时");
        } else if (e.type == DioErrorType.SEND_TIMEOUT) {
          // It occurs when url is sent timeout.
          print("请求超时");
        } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
          //It occurs when receiving timeout
          print("响应超时");
        } else if (e.type == DioErrorType.RESPONSE) {
          // When the server response, but with a incorrect status, such as 404, 503...
          print("出现异常");
        } else if (e.type == DioErrorType.CANCEL) {
          // When the request is cancelled, dio will throw a error with this type.
          print("请求取消");
        } else {
          //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
          print("未知错误");
        }
      }
    
      /*
       * 取消请求
       *
       * 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
       * 所以参数可选
       */
      void cancelRequests(CancelToken token) {
        token.cancel("cancelled");
      }
    }
    
    

    然后我们需要一个统一管理URL的API

    class Api {
      //应该根据当前的编译环境确定BASE_URL 
      static const String BASE_URL = "https://api.jisuapi.com/";
      static const String JISUJOKE = "xiaohua/text";
    }
    

    然后就可以用了,还是刚刚的笑话接口:

      Future<void> getJiSuJoke2() async {
        Map<String, dynamic> mData = {
          "pagenum": 1,
          "pagesize": 1,
          "sort": "rand",
          "appkey": APPKEY
        };
        try {
          //开始请求
          var response =await HttpUtil().post(Api.JISUJOKE,
              queryData: mData);
          //请求体结果response,将数据转化为实体类
          jokeEntity =
              JisuJokeEntity().fromJson(json.decode(response.data.toString()));
          //print(response);
          print(jokeEntity.result.xList[0].content);
        } catch (e) {
          print(e);
        }
      }
    

    这里有点Android MVP的那种感觉了 不过目前只是demo项目 没有完全分开,之后尝试自己搭建一套Flutter的MVP或者MVVM架构。

    在封装的HttpUtil中,预留了许多空,比如请求拦截的操作呀,请求成功之后服务器的返回码判断呀,header的添加呀,各个项目都有不同所以暂时先预留空位。

    HttpUtil中的请求方法中都保留了一个参数options可以为每个请求单独配置请求参数

    相关文章

      网友评论

          本文标题:Flutter Dio 网络请求

          本文链接:https://www.haomeiwen.com/subject/hzmbnhtx.html