1. 程式人生 > >dio+json_serializable從網路請求到資料解析

dio+json_serializable從網路請求到資料解析

前言

網路請求到資料解析是一個app必不可少的流程之一,在flutter官網中目前主要是介紹 自帶的Http請求+Json解析 但是也推薦了更好的網路請求到組合的方式 dio 和 json_serializable,本篇文章主要介紹這兩個方式的使用,原始碼在結尾

本文同步更新於我的gitpage xsfelvis.github.io/2018/12/08/…

dio

簡介

package地址 pub.flutter-io.cn/packages/di…, 新增依賴

dependencies:    dio: ^1.0.9

支援了支援Restful API、FormData、攔截器、請求取消、Cookie管理、檔案上傳/下載、超時等

Performing a GET request: 簡單的使用如下

Response response;
response=await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
// Optionally the request above could also be done as
response=await dio.get("/test",data:{"id":12,"name":"wendu"})
print(response.data.toString());
複製程式碼

Performing a POST request:

response=await dio.post("/test",data:{"id":12,"name":"wendu"})
複製程式碼

Performing multiple concurrent requests:

response= await Future.wait([dio.post("/info"),dio.get("/token")]);
複製程式碼

Downloading a file:

response=await dio.download("https://www.google.cn/","./xx.html"
) 複製程式碼

Sending FormData:

FormData formData = new FormData.from({
   "name": "wendux",
   "age": 25,
});
response = await dio.post("/info", data: formData)
複製程式碼

Uploading multiple files to server by FormData:

FormData formData = new FormData.from({
   "name": "wendux",
   "age": 25,
   "file1": new UploadFileInfo(new File("./upload.txt"), "upload1.txt"),
   // upload with bytes (List<int>) 
   "file2": new UploadFileInfo.fromBytes(utf8.encode("hello world"),"word.txt"),
   // Pass multiple files within an Array 
   "files": [
      new UploadFileInfo(new File("./example/upload.txt"), "upload.txt"),
      new UploadFileInfo(new File("./example/upload.txt"), "upload.txt")
    ]
});
response = await dio.post("/info", data: formData)
複製程式碼

更多可以參考作者的說明文章以及 example

並且由於flutter原生的http庫不支援charles抓包,這個庫可以使用設定代理來達到抓包的目的,ip自己替換

dio.onHttpClientCreate = (HttpClient client) {
  client.findProxy = (uri) {
    //proxy all request to localhost:8888
    return "PROXY yourIP:yourPort";
  };
};
複製程式碼

這樣就可抓包了。

網路請求封裝使用

簡單封裝使我們更加容易的使用,核心程式碼如下

void _request(String url, Function callBack,
      {String method,
      Map<String, String> params,
      Function errorCallBack}) async {
//    dio.onHttpClientCreate = (HttpClient client) {
//      client.findProxy = (uri) {
//        //proxy all request to localhost:8888
//        return "PROXY 172.23.235.153:8888";
//      };
//    };
    String errorMsg = "";
    int statusCode;
    try {
      Response response;
      if (method == GET) {
        if (params != null && params.isNotEmpty) {
          StringBuffer sb = new StringBuffer("?");
          params.forEach((key, value) {
            sb.write("$key" + "=" + "$value" + "&");
          });
          String paramStr = sb.toString();
          paramStr = paramStr.substring(0, paramStr.length - 1);
          url += paramStr;
        }
        response = await dio.get(url);
      } else if (method == POST) {
        if (params != null && params.isNotEmpty) {
          response = await dio.post(url, data: params);
        } else {
          response = await dio.post(url);
        }
      }

      statusCode = response.statusCode;
      if (statusCode < 0) {
        errorMsg = "網路請求錯誤,狀態碼:" + statusCode.toString();
        _handError(errorCallBack, errorMsg);
        return;
      }
      if (callBack != null) {
        String res2Json  = json.encode(response.data);
        Map<String,dynamic> map=json.decode(res2Json);
        callBack(map["data"]);
      }
    } catch (exception) {
      _handError(errorCallBack, exception.toString());
    }
  }
複製程式碼

詳細的可以參考原始碼github.com/xsfelvis/le…

使用如下

HttpCore.instance.get(Api.HOME_BANNER, (data) {
  List<BannerData> banners = getBannersList(data);
  setState(() {
    slideData = banners;
  });
}, errorCallBack: (errorMsg) {
  print("error:" + errorMsg);
  return null;
});
複製程式碼

在封裝網路庫的的時候發現 底層 response.data['data']時候當是一個jsonarray時候竟然無法直接取出'data',"Stirng not subType of Index "錯誤,原因是 此時拿到的不是一個json資料,沒有雙引號的假json資料,但是在jsonobject卻可以,解決如下

if (callBack != null) {
  String res2Json  = json.encode(response.data);
  Map<String,dynamic> map=json.decode(res2Json);
  callBack(map["data"]);
}
複製程式碼

json_serializable

簡介

package地址 pub.flutter-io.cn/packages/js… 新增依賴

dependencies:    json_serializable: ^2.0.0 dev_dependencies    build_runner: ^1.1.2    json_serializable: ^2.0.0

這個是個好東西,之前的相當於json資料一個個通過key解出來(關於自帶的json解析可以參考 juejin.im/post/5b5d78…),不僅耗時而且容易出錯,json_serializable 讓我們直接反序列化成物件直接使用類似於Gson,而且還提供了一個工具來自動幫助我們生成程式碼,簡單快捷而且不容易出錯

使用

以下面json為例

{
    "data":{
        "curPage":2,
        "datas":[
            {
                "apkLink":"",
                "author":"鴻洋",
                "chapterId":410,
                "chapterName":"玉剛說",
                "collect":false,
                "courseId":13,
                "desc":"",
                "envelopePic":"",
                "fresh":false,
                "id":7604,
                "link":"https://mp.weixin.qq.com/s/cCZKmqKrdCn63eWTbOuANw",
                "niceDate":"2018-12-03",
                "origin":"",
                "projectLink":"",
                "publishTime":1543830090000,
                "superChapterId":408,
                "superChapterName":"公眾號",
                "tags":[
                    {
                        "name":"公眾號",
                        "url":"/wxarticle/list/410/1"
                    }
                ],
                "title":"在 Retrofit 和 OkHttp 中使用網路快取,提高訪問效率",
                "type":0,
                "userId":-1,
                "visible":1,
                "zan":0
            },
            {
                "apkLink":"",
                "author":"鴻洋",
                "chapterId":408,
                "chapterName":"鴻洋",
                "collect":false,
                "courseId":13,
                "desc":"",
                "envelopePic":"",
                "fresh":false,
                "id":7605,
                "link":"https://mp.weixin.qq.com/s/r3AWeYafyMEc1-g8BWEHBg",
                "niceDate":"2018-12-03",
                "origin":"",
                "projectLink":"",
                "publishTime":1543766400000,
                "superChapterId":408,
                "superChapterName":"公眾號",
                "tags":[
                    {
                        "name":"公眾號",
                        "url":"/wxarticle/list/408/1"
                    }
                ],
                "title":"非 UI 執行緒能呼叫 View.invalidate()?",
                "type":0,
                "userId":-1,
                "visible":1,
                "zan":0
            }
        ],
        "offset":20,
        "over":false,
        "pageCount":289,
        "size":20,
        "total":5779
    },
    "errorCode":0,
    "errorMsg":""
}
複製程式碼

為例,我們首先找到實際資料資訊,因為異errorcode 之類的處理已在上面的封裝網路請求中處理了,這裡需要關注 實際內容傳遞者 “data”裡面的資料,將上面這個data裡面的jsonObject,從data 這個key後面包括大括號一起復制到上面的程式碼生成工具網頁裡面,截圖如下

image.png | left | 747x341

然後當前專案的目錄下執行 flutter packages pub run build_runner build

然就可以得到news.g.dart檔案

然後在使用中通過獲取物件屬性的方式就可以直接拿到我們關注的欄位了

//  獲取News資料
void _getNewsList(int curpage) {
  var url = Api.HOME_ARTICLE + curpage.toString() + "/json";
  HttpCore.instance.get(url, (data) {
    News news = News.fromJson(data);
    List<Datas> newsDatas = news.datas;
    setState(() {
      listData = newsDatas;
    });
  });
}
複製程式碼

其他

自定義欄位的處理

我們可以通過JsonKey自定義引數進行註釋並自定義引數來自定義各個欄位。例如:是否允許欄位為空等。注意,這裡不加任何JsonKey預設允許空json欄位。 比如解析欄位有個橫線,而dart中只允許字母數字下劃線作為變數名。所以我們必須對它進行特殊處理。@JsonKey(name="off-set")來解析map,通常自動生成程式碼的工具已經可以幫助我們解決了

@JsonKey(name: 'off-set')
int offset;
複製程式碼

實踐

效果如下圖

image.png | left | 250x515.9574468085107

介面來自 玩Android的開放介面

程式碼在 learnflutter 的demo3