Flutter入門進(jìn)階之旅(十七)Flutter dio網(wǎng)絡(luò)請求

前言

前面關(guān)于Flutter的講解部分我把關(guān)于flutter的基礎(chǔ)入門部分帶著大家梳理了一遍,那從本篇博客開始,我們開始進(jìn)入新的領(lǐng)域,也算是給進(jìn)階篇開個頭,今天我們來一塊學(xué)習(xí)一下Flutter中的網(wǎng)絡(luò)請求庫--->Dio,關(guān)于Flutter原生帶的Http使用起來不論在功能上還是擴(kuò)展上都不是那么的強(qiáng)大,鑒于此筆者在這里推薦大家在項(xiàng)目中使用Dio封裝網(wǎng)絡(luò)請求庫。關(guān)于Http的使用讀者可自行查閱資料學(xué)習(xí)。

課程目標(biāo)

  • 使用Dio完成最簡單的GET、POST請求
  • 基于Dio封裝網(wǎng)絡(luò)請求庫,并使用自己封裝的網(wǎng)絡(luò)請求工具類完成GET、POST請求
  • 了解InterceptorsWrapper攔截器
  • 利用攔截器給網(wǎng)絡(luò)請求添加統(tǒng)一參數(shù)(如,token,userId等)
  • 統(tǒng)一處理響應(yīng)返回?cái)?shù)據(jù)(做json轉(zhuǎn)實(shí)體或者格式化操作)
  • 操作請求統(tǒng)一攔截
1.使用Dio完成簡單的GET、POST請求

1.1使用dio get請求一條json數(shù)據(jù)

  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請求操作起來比較簡單,我就不單獨(dú)附效果圖跟講解說明了,相信讀者從開始看系列博客到現(xiàn)在,讀懂上面的代碼已經(jīng)不在話下了,我把上面的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網(wǎng)絡(luò)請求"),
      ),
      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("數(shù)據(jù)加載中...")
                  : Text(
                      resultJson,
                      style: TextStyle(fontSize: 16),
                    ),
            ),
          ))
        ],
      ),
    );
  }
}
2.封裝自己的Dio網(wǎng)絡(luò)請求庫

在項(xiàng)目開發(fā)過程中隨著項(xiàng)目越寫越大,代碼量也會成倍的增加,隔離業(yè)務(wù)抽取共性代碼的思想自然而然的出現(xiàn)在一個嚴(yán)于律己的開發(fā)者腦子里,特別是像網(wǎng)絡(luò)請求這種操作,好的封裝不僅會減少冗余代碼更能讓代碼層級變得清晰可讀,下面筆者就帶著自己的理解對Dio做一個簡單封裝,筆者自認(rèn)為才疏學(xué)淺,代碼中如有寫的不好的地方還請各位不吝賜教。

一般我們在處理工具類時都會用到單例的思想,做為一個項(xiàng)目全局的網(wǎng)絡(luò)請求工具類,我們同樣也把DioUtils封裝成單例模式

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

對于請求參數(shù)的初始化跟一些網(wǎng)絡(luò)配置,Dio為開發(fā)者提供了BaseOptions、Options、RequestOptions可選Options配置,三者的優(yōu)先級關(guān)系依次遞增

試想這樣一個場景:一般我們在對網(wǎng)絡(luò)請求工具類設(shè)置參數(shù),理論上是全局不能修改的,如果我們的業(yè)務(wù)需求必須讓我們修改配置參數(shù),比如基于不同的接口服務(wù)需要設(shè)置不同請求中的header內(nèi)容,這個時候利用options的優(yōu)先級我們就可以很輕松的處理這個問題,稍后我會在代碼里具體講解

先來看下BaseOptions給我們提供的可供配置的參數(shù):

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,
})

上面構(gòu)造方法中的屬性讀者基本都能見名知意做到自解釋,我就不單獨(dú)解釋了,貼上一段我在代碼里的配置:

    //請求參數(shù)配置
   _baseOptions = new BaseOptions(
     baseUrl: BASE_URL,
     //請求服務(wù)地址
     connectTimeout: 5000,
     //響應(yīng)時間
     receiveTimeout: 5000,
     headers: {
       //需要配置請求的header可在此處配置
     },
     //請求的Content-Type,默認(rèn)值是[ContentType.json]. 也可以用ContentType.parse("application/x-www-form-urlencoded")
     contentType: ContentType.json,
     //表示期望以那種格式(方式)接受響應(yīng)數(shù)據(jù)。接受三種類型 `json`, `stream`, `plain`, `bytes`. 默認(rèn)值是 `json`,
     responseType: ResponseType.json,
   );
   

然后把_baseOptions通過參數(shù)的形式傳入Dio實(shí)例中完成配置的初始化

     //創(chuàng)建dio實(shí)例
    _dio = new Dio(_baseOptions);

接下來我把我們開篇用DIO做的簡單GET、POST方法用我們新寫的工具類封裝完成后重新做請求,讓我們來一起感受下封裝帶來的便利。

封裝GET請求

  /**
   * get請求
   */

  get(url, {data, options, cancleToken}) async {
    print('get request path ------${url}-------請求參數(shù)${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的時候需要攜帶參數(shù),例如如下這樣一個請求

https://www.wanandroid.com/article/list/0/json?cid=60
方法:GET
參數(shù):cid 分類的id,上述二級目錄的id

對上面的url進(jìn)行分析

baseUrlhttps://www.wanandroid.com 我們已經(jīng)在工具類中初始化過了,所以不用設(shè)置
path: /article/list/0/json 我們在get請求中需要傳入的url
data:cid=60 我們封裝好的get請求中對應(yīng)的data數(shù)據(jù)

上述請求為:

  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的優(yōu)先級重新覆蓋掉原先在工具類里設(shè)置好的網(wǎng)絡(luò)配置,比如修改提前在header中設(shè)置好的請求內(nèi)容。
還是上述代碼,我先修改_baseOptions中的header的配置如下:

我們利用Options修改BaseUrl后的請求url

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

還是上述請求,現(xiàn)在我們重新走一遍上述的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;
   });
 }

運(yùn)行代碼,再次執(zhí)行g(shù)et請求,控制臺的header信息已經(jīng)被我們修改過了:


在這里插入圖片描述

筆者只不過是借用這個例子讓讀者了解怎么去修改工具類里配置好的參數(shù),通過RequestOptions不僅僅是可以修改headerl,基本你能在工具類設(shè)置的東西都能做修改,感興趣的讀者可以自行閱讀源碼測試,限于篇幅問題我這里就不展開講解了。

POST請求封裝
POST請求封裝跟GET類似,這里我就不過多分析了,我直接貼源碼了讀者可結(jié)合代碼自行對比差異

  postRequest() async {
    var params = {
      "username": "aa112233",
      "password": "123456",
      "repassword": "123456"
    };
    String result = await DioUtils().post("/user/register",data: params);
    this.setState(() {
      resultJson = result;
    });
  }
3.利用攔截器給網(wǎng)絡(luò)請求添加統(tǒng)一參數(shù)

在DIo中我們可以通過Interceptors為我們的網(wǎng)絡(luò)請求添加攔截器

_dio.interceptors.add()

我們通過_dio.interceptors.add()方法可以根據(jù)不同業(yè)務(wù)為我們的工具類添加不同的攔截器,比如在網(wǎng)絡(luò)請求開始之前,我們給每個請求都添加統(tǒng)一的token,或者userId,或者我們可以對請求返回的數(shù)據(jù)做統(tǒng)一json格式化處理,對錯誤響應(yīng)統(tǒng)一處理,這些業(yè)務(wù)場景都可以通過interceptors來完成,比如下面我的配置:

    //可根據(jù)項(xiàng)目需要選擇性的添加請求攔截器
    _dio.interceptors.add(
      InterceptorsWrapper(onRequest: (RequestOptions requestions) async {
        //此處可網(wǎng)絡(luò)請求之前做相關(guān)配置,比如會所有請求添加token,或者userId
        requestions.queryParameters["token"] = "testtoken123443423";
        requestions.queryParameters["userId"] = "123456";
        return requestions;
      }, onResponse: (Response response) {
        //此處攔截工作在數(shù)據(jù)返回之后,可在此對dio請求的數(shù)據(jù)做二次封裝或者轉(zhuǎn)實(shí)體類等相關(guān)操作
        return response;
      }, onError: (DioError error) {
        //處理錯誤請求
        return error;
      }),
    );
  }

現(xiàn)在我們通過工具類進(jìn)行的所有的網(wǎng)絡(luò)請求的url后面都會被加入token=“testtoken123443423”&userId=123456這樣兩個參數(shù),還是上面的GET請求,下面我們通過代碼跟控制臺的輸出內(nèi)容看下通過攔截器添加完通用參數(shù)的請求,log控制臺打印的請求參數(shù)信息

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

在這里插入圖片描述

上面的GET請求,我們在參數(shù)里只添加了cid = 60的參數(shù),但是通過控制臺我們已經(jīng)清楚的看到token跟userId已經(jīng)通過攔截器的被我們添加到queryParameters里面了。

限于篇幅問題,封裝好的DioUtils我就不貼出來了,附上源碼的github地址,供讀者參考,最后感謝玩安卓開放API平臺提供的測試API用于本篇博客中的測試用例。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容