dio+json_serializable从网络请求到数据解析

前言

网络请求到数据解析是一个app必不可少的流程之一,在flutter官网中目前主要是介绍 自带的Http请求+Json解析 但是也推荐了更好的网路请求到组合的方式 dio 和 json_serializable,本篇文章主要介绍这两个方式的使用,源码在结尾

dio

简介

package地址 https://pub.flutter-io.cn/packages/dio
添加依赖

dependencies:
dio: ^1.0.9
支持了支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等
Performing a GET request: 简单的使用如下

1
2
3
4
5
6
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:

1
response=await dio.post("/test",data:{"id":12,"name":"wendu"})

Performing multiple concurrent requests:

1
response= await Future.wait([dio.post("/info"),dio.get("/token")]);

Downloading a file:

1
response=await dio.download("https://www.google.cn/","./xx.html")

Sending FormData:

1
2
3
4
5
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData)

Uploading multiple files to server by FormData:

1
2
3
4
5
6
7
8
9
10
11
12
13
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自己替换

1
2
3
4
5
6
dio.onHttpClientCreate = (HttpClient client) {
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY yourIP:yourPort";
};
};

这样就可抓包了。

网络请求封装使用

简单封装使我们更加容易的使用,核心代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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());
}
}

详细的可以参考源码https://github.com/xsfelvis/learnflutter/blob/047fb2c9cc08334c1adcc7b7892ebfe28a2cf13f/lib/demo3/utils/net/HttpCore.dart

使用如下

1
2
3
4
5
6
7
8
9
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却可以,解决如下

1
2
3
4
5
if (callBack != null) {
String res2Json = json.encode(response.data);
Map<String,dynamic> map=json.decode(res2Json);
callBack(map["data"]);
}

json_serializable

简介

package地址 https://pub.flutter-io.cn/packages/json_serializable
添加依赖

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

这个是个好东西,之前的相当于json数据一个个通过key解出来(关于自带的json解析可以参考 https://juejin.im/post/5b5d782ae51d45191c7e7fb3#heading-7),不仅耗时而且容易出错,json_serializable 让我们直接反序列化成对象直接使用类似于Gson,而且还提供了一个工具来自动帮助我们生成代码,简单快捷而且不容易出错

使用

以json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
{
"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文件

然后在使用中通过获取对象属性的方式就可以直接拿到我们关注的字段了

1
2
3
4
5
6
7
8
9
10
11
//  获取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="Nicehash-CNHeavy")来解析map,通常自动生成代码的工具已经可以帮助我们解决了

1
2
@JsonKey(name: 'off-set')
int offset;

实践

效果如下图

image.png | left | 250x515.9574468085107

接口来自 玩Android的开放接口

代码在 learnflutter 的demo3

0%