Flutter入门进阶之旅(十七)Flutter dio网络请求

前言

前面关于Flutter的讲解部分我把关于flutter的基础入门部分带着大家梳理了一遍,那从本篇博客开始,我们开始进入新的领域,也算是给进阶篇开个头,今天我们来一块学习一下Flutter中的网络请求库--->Dio,关于Flutter原生带的Http使用起来不论在功能上还是扩展上都不是那么的强大,鉴于此笔者在这里推荐大家在项目中使用Dio封装网络请求库。关于Http的使用读者可自行查阅资料学习。

课程目标

  • 使用Dio完成最简单的GET、POST请求
  • 基于Dio封装网络请求库,并使用自己封装的网络请求工具类完成GET、POST请求
  • 了解InterceptorsWrapper拦截器
  • 利用拦截器给网络请求添加统一参数(如,token,userId等)
  • 统一处理响应返回数据(做json转实体或者格式化操作)
  • 操作请求统一拦截
1.使用Dio完成简单的GET、POST请求

1.1使用dio get请求一条json数据

  getRequest() async {
    Response response = await Dio()
        .get('https://www.wanandroid.com/banner/json');
    this.setState(() {
      result= response.toString();
    });
  }

1.2 利用dio post请求注册一个新用户

postRequest() async {
    var path = "https://www.wanandroid.com/user/register";
    var params = {
      "username": "aa112233",
      "password": "123456",
      "repassword": "123456"
    };
    Response response =
        await Dio().post(path, queryParameters: params);
    this.setState(() {
      result= response.toString();
    });
  }

由于简单的GET、跟POST请求操作起来比较简单,我就不单独附效果图跟讲解说明了,相信读者从开始看系列博客到现在,读懂上面的代码已经不在话下了,我把上面的get、跟post请求放到一张效果图上代码也贴到一块供大家读阅,另外感谢玩安卓提供的开放API作为本篇博客的请求测试用例。

效果图

在这里插入图片描述

代码

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

class NetWorkPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PageState();
}

class PageState extends State<NetWorkPage> {
  var resultJson = "";

  @override
  void initState() {
    super.initState();
  }

  getRequest() async {
    Response response = await Dio()
        .get('https://www.wanandroid.com/banner/json');
    this.setState(() {
      resultJson = response.toString();
    });
  }

  postRequest() async {
    var path = "https://www.wanandroid.com/user/register";
    var params = {
      "username": "aa112233",
      "password": "123456",
      "repassword": "123456"
    };
    Response response =
        await Dio().post(path, queryParameters: params);
    this.setState(() {
      resultJson = response.toString();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dio网络请求"),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          MaterialButton(
              color: Colors.pinkAccent,
              child: Text("GET 请求"),
              onPressed: () {
                getRequest();
              }),
          MaterialButton(
              color: Colors.blueAccent,
              child: Text("POST 请求"),
              onPressed: () {
                postRequest();
              }),
          Expanded(
              child: Padding(
            padding: EdgeInsets.all(20),
            child: Center(
              child: resultJson.length <= 0
                  ? Text("数据加载中...")
                  : Text(
                      resultJson,
                      style: TextStyle(fontSize: 16),
                    ),
            ),
          ))
        ],
      ),
    );
  }
}
2.封装自己的Dio网络请求库

在项目开发过程中随着项目越写越大,代码量也会成倍的增加,隔离业务抽取共性代码的思想自然而然的出现在一个严于律己的开发者脑子里,特别是像网络请求这种操作,好的封装不仅会减少冗余代码更能让代码层级变得清晰可读,下面笔者就带着自己的理解对Dio做一个简单封装,笔者自认为才疏学浅,代码中如有写的不好的地方还请各位不吝赐教。

一般我们在处理工具类时都会用到单例的思想,做为一个项目全局的网络请求工具类,我们同样也把DioUtils封装成单例模式

  static DioUtils getInstance() {
    if (_instance == null) {
      _instance = new DioUtils();
    }
    return _instance;
  }

对于请求参数的初始化跟一些网络配置,Dio为开发者提供了BaseOptions、Options、RequestOptions可选Options配置,三者的优先级关系依次递增

试想这样一个场景:一般我们在对网络请求工具类设置参数,理论上是全局不能修改的,如果我们的业务需求必须让我们修改配置参数,比如基于不同的接口服务需要设置不同请求中的header内容,这个时候利用options的优先级我们就可以很轻松的处理这个问题,稍后我会在代码里具体讲解

先来看下BaseOptions给我们提供的可供配置的参数:

BaseOptions({
  String method,
  int connectTimeout,
  int receiveTimeout,
  Iterable<Cookie> cookies,
  this.baseUrl,
  this.queryParameters,
  Map<String, dynamic> extra,
  Map<String, dynamic> headers,
  ResponseType responseType = ResponseType.json,
  ContentType contentType,
  ValidateStatus validateStatus,
  bool receiveDataWhenStatusError = true,
  bool followRedirects = true,
  int maxRedirects = 5,
 RequestEncoder requestEncoder,
  ResponseDecoder responseDecoder,
})

上面构造方法中的属性读者基本都能见名知意做到自解释,我就不单独解释了,贴上一段我在代码里的配置:

    //请求参数配置
   _baseOptions = new BaseOptions(
     baseUrl: BASE_URL,
     //请求服务地址
     connectTimeout: 5000,
     //响应时间
     receiveTimeout: 5000,
     headers: {
       //需要配置请求的header可在此处配置
     },
     //请求的Content-Type,默认值是[ContentType.json]. 也可以用ContentType.parse("application/x-www-form-urlencoded")
     contentType: ContentType.json,
     //表示期望以那种格式(方式)接受响应数据。接受三种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
     responseType: ResponseType.json,
   );
   

然后把_baseOptions通过参数的形式传入Dio实例中完成配置的初始化

     //创建dio实例
    _dio = new Dio(_baseOptions);

接下来我把我们开篇用DIO做的简单GET、POST方法用我们新写的工具类封装完成后重新做请求,让我们来一起感受下封装带来的便利。

封装GET请求

  /**
   * get请求
   */

  get(url, {data, options, cancleToken}) async {
    print('get request path ------${url}-------请求参数${data}');
    Response response;
    try {
      response = await _dio.get(url,
          queryParameters: data, options: options, cancelToken: cancleToken);
      print('get success ---${response.data}');
    } on DioError catch (e) {
      print('请求失败---错误类型${e.type}');
    }

    return response.data;
  }

利用我们封装好的get请求方法,开篇的get请求只需改为:

  getRequest() async {
    String result= await DioUtils().get('/banner/json');
    this.setState(() {
      resultJson = result;
    });
  }

或者get的时候需要携带参数,例如如下这样一个请求

https://www.wanandroid.com/article/list/0/json?cid=60
方法:GET
参数:cid 分类的id,上述二级目录的id

对上面的url进行分析

baseUrlhttps://www.wanandroid.com 我们已经在工具类中初始化过了,所以不用设置
path: /article/list/0/json 我们在get请求中需要传入的url
data:cid=60 我们封装好的get请求中对应的data数据

上述请求为:

  getRequest() async {
    var data = {
      "cid": 60
    };
    String result = await DioUtils().get('/article/list/0/json', data: data);
    this.setState(() {
      resultJson = result;
    });
  }

在刚刚讲BaseOptions时,我们提到还有Options、RequestOptions可供配置,我们提到可以利用Options的优先级重新覆盖掉原先在工具类里设置好的网络配置,比如修改提前在header中设置好的请求内容。
还是上述代码,我先修改_baseOptions中的header的配置如下:

我们利用Options修改BaseUrl后的请求url

 _baseOptions = new BaseOptions(
     baseUrl: BASE_URL,
     connectTimeout: 5000,
     receiveTimeout: 5000,
     headers: {
       //预设好的header信息
       "testHeader":"bb"
     },
     contentType: ContentType.json,
     responseType: ResponseType.json,
   );

还是上述请求,现在我们重新走一遍上述的get请求,看下log控制台打印的header信息

在这里插入图片描述

然后我修改原先的get请求,给options添加RequestOptions,修改里面的header值如代码所示:

  getRequest() async {
   var data = {"cid": 60};
   RequestOptions requestOptions = new RequestOptions(headers: {"testHeader":"aaaa"});
   String result = await DioUtils()
       .get('/article/list/0/json', data: data,options: requestOptions);
   this.setState(() {
     resultJson = result;
   });
 }

运行代码,再次执行get请求,控制台的header信息已经被我们修改过了:


在这里插入图片描述

笔者只不过是借用这个例子让读者了解怎么去修改工具类里配置好的参数,通过RequestOptions不仅仅是可以修改headerl,基本你能在工具类设置的东西都能做修改,感兴趣的读者可以自行阅读源码测试,限于篇幅问题我这里就不展开讲解了。

POST请求封装
POST请求封装跟GET类似,这里我就不过多分析了,我直接贴源码了读者可结合代码自行对比差异

  postRequest() async {
    var params = {
      "username": "aa112233",
      "password": "123456",
      "repassword": "123456"
    };
    String result = await DioUtils().post("/user/register",data: params);
    this.setState(() {
      resultJson = result;
    });
  }
3.利用拦截器给网络请求添加统一参数

在DIo中我们可以通过Interceptors为我们的网络请求添加拦截器

_dio.interceptors.add()

我们通过_dio.interceptors.add()方法可以根据不同业务为我们的工具类添加不同的拦截器,比如在网络请求开始之前,我们给每个请求都添加统一的token,或者userId,或者我们可以对请求返回的数据做统一json格式化处理,对错误响应统一处理,这些业务场景都可以通过interceptors来完成,比如下面我的配置:

    //可根据项目需要选择性的添加请求拦截器
    _dio.interceptors.add(
      InterceptorsWrapper(onRequest: (RequestOptions requestions) async {
        //此处可网络请求之前做相关配置,比如会所有请求添加token,或者userId
        requestions.queryParameters["token"] = "testtoken123443423";
        requestions.queryParameters["userId"] = "123456";
        return requestions;
      }, onResponse: (Response response) {
        //此处拦截工作在数据返回之后,可在此对dio请求的数据做二次封装或者转实体类等相关操作
        return response;
      }, onError: (DioError error) {
        //处理错误请求
        return error;
      }),
    );
  }

现在我们通过工具类进行的所有的网络请求的url后面都会被加入token=“testtoken123443423”&userId=123456这样两个参数,还是上面的GET请求,下面我们通过代码跟控制台的输出内容看下通过拦截器添加完通用参数的请求,log控制台打印的请求参数信息

  getRequest() async {
    var data = {"cid": 60};
    String result = await DioUtils()
        .get('/article/list/0/json', data: data);
    this.setState(() {
      resultJson = result;
    });
  }

在这里插入图片描述

上面的GET请求,我们在参数里只添加了cid = 60的参数,但是通过控制台我们已经清楚的看到token跟userId已经通过拦截器的被我们添加到queryParameters里面了。

限于篇幅问题,封装好的DioUtils我就不贴出来了,附上源码的github地址,供读者参考,最后感谢玩安卓开放API平台提供的测试API用于本篇博客中的测试用例。

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

推荐阅读更多精彩内容

  • AFHTTPRequestOperationManager 网络传输协议UDP、TCP、Http、Socket、X...
    Carden阅读 4,337评论 0 12
  • dio是Flutter中文网开源的一个强大的Dart Http请求库,支持Restful API、FormData...
    lazydu阅读 69,073评论 10 56
  • 一、Uri Uri 类 提供了 编码和解码 URI(URL) 字符的功能。 这些函数处理 URI 特殊的字符,例如...
    小哥_xiaoge阅读 4,198评论 1 0
  • 今天可乐生日,先生送了一个巨型的变形金刚给他做生日礼物。 昨晚派对结束回到家,我先一步到屋里,两父子从车场取东西。...
    Crystal婉韵阅读 285评论 1 0
  • 经历了风霜 你还是一样 以挺拔的姿势 站立在路旁 春天是新生的希望 你却为何把气息隐藏 是春风太柔弱 吹不开你的心...
    薄荷味的笑1216阅读 604评论 4 9