项目使用Flutter来做,官方已经已经有Dio提供了支持。比如下面这段代码,在理想情况下,拿到response就可以直接解析json数据了。但一般而言,服务器返回的数据都不会这么简单,所以,我们需要做的就是二次封装Dio。
Response response = await dio.post(url, data: params);
有关Dio的科普,传送门.
单个数据结构如下:
{
"result": {
"apkName": "string",
"appInfo": "string",
"appNam": "string",
"appUrl": "string",
},
"resultInfo": {
"resultCode": "string",
"resultMsg": "string"
}
}
数据结构列表如下:
{
"result": {
"count": 0,
"data": [
{
"apkName": "string",
"appInfo": "string",
"appNam": "string",
"appUrl": "string",
}
],
"pagenum": 0,
"pagesize": 0
},
"resultInfo": {
"resultCode": "string",
"resultMsg": "string"
}
}
如果要直接解析这些数据显然是不行的。考虑统一封装,直接给出代码如下:
class ResultInfo {
String resultCode;
String resultMsg;
}
class RequestResult {
ResultInfo resultInfo;
dynamic result;
}
将Response的成员变量data申明为ResquestResult,通过post请求得到的数据应该都是RequestResult类型的了。flutter同时提供了jsonserializable
插件来为类进行json序列化和反序列化,大大提高了效率。使用jsonserializable
来生成相关代码:
@JsonSerializable()
class ResultInfo {
String resultCode;
String resultMsg;
ResultInfo({this.resultCode, this.resultMsg});
factory ResultInfo.fromJson(Map<String, dynamic> json) => _$ResultInfoFromJson(json);
Map<String, dynamic> toJson() => _$ResultInfoToJson(this);
@override
String toString() {
return toJson().toString();
}
}
@JsonSerializable()
class RequestResult {
ResultInfo resultInfo;
dynamic result;
RequestResult({this.resultInfo, this.result});
factory RequestResult.fromJson(Map<String, dynamic> json) => _$RequestResultFromJson(json);
Map<String, dynamic> toJson() => _$RequestResultToJson(this);
@override
String toString() {
return toJson().toString();
}
}
将有关网络请求的操作封装到NetworkManager
里,设置全局单例,方便调用。
NetworkManager._internal(String baseUrl) {
_dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: 5000,
receiveTimeout: 3000,
));
//请求结果需进行json反序列化
(_dio.transformer as DefaultTransformer).jsonDecodeCallback = _parseResponse;
//添加Interceptor,打印日志,方便调试
_dio.interceptors.add(NetworkLogInterceptor(requestBody: false,
responseHeader: false,
responseBody: true));
}
由于我们自己定义了数据结构,在得到请求结果的时候需要加上反序列化函数,否则抛出异常:
Future<RequestResult> _parseResponse(String jsonString) {
return compute(_parseResult, jsonString);
}
static RequestResult _parseResult(String jsonString) {
return RequestResult.fromJson(JsonCodec().decode(jsonString));
}
以post请求为例,给出代码
/**
** post方法
** onRequestSuccess:请求成功回调,回调给调用方
** onRequestFailure:请求失败回调,回调给调用方
** ParseResult:数据解析结果,回调给调用方
**/
typedef ParseResult = dynamic Function(Map<String, dynamic> json);
void post(String path, Function onRequestSuccess, Function onRequestFailure, {
data,
Map<String, dynamic> queryParameters,
ParseResult parseResult,
Options options,
CancelToken cancelToken,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress
}) async {
try {
Response<RequestResult> response = await _dio.post(path,
data: data,
options: _checkOptions(options),
queryParameters: queryParameters,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress);
if (response.data.resultInfo.resultCode == "200") {
if (onRequestSuccess != null) {
onRequestSuccess(parseResult != null && (response.data.result is Map<String, dynamic>)
? parseResult(response.data.result)
: response.data.result);
}
} else {
if (onRequestFailure != null) {
onRequestFailure(int.parse(response.data.resultInfo.resultCode),
response.data.resultInfo.resultMsg);
}
}
} on DioError catch(e) {
if (onRequestFailure != null) {
onRequestFailure(e.response != null ? e.response.statusCode : e.type.index,
"DioError [${e.type}]: " + (e.message ?? _dioErrorType[e.type]));
}
}
}
本段以视频列表为例,说明请求的具体过程。
{
"result": {
"count": 0,
"data": [
{
"iconUrl": "string",
"likeCount": 0,
"liked": false,
"orientation": 0,
"playUrl": "string",
"publishAvatar": "string",
"publishNick": "string",
"publishPhotosCount": 0,
"publishUid": 0,
"publishVideosCount": 0,
"published": false,
"reviewCount": 0,
"topicId": 0,
"topicName": "string",
"type": 0,
"vid": 0
}
],
"pagenum": 0,
"pagesize": 0
},
"resultInfo": {
"resultCode": "string",
"resultMsg": "string"
}
}
我们需先将返回的data定义为一个类,进行json序列化及反序列化。
发起请求时,需要先将请求的数据做一次json序列化,主要是请求数量、页码等服务器规定的参数。
@JsonSerializable()
class VideoListParam {
int topicId;
String udId;
@JsonKey(name: "pagenum")
int pageNum;
@JsonKey(name: "pagesize")
int pageSize;
VideoListParam({this.topicId, this.udId, this.pageNum, this.pageSize});
factory VideoListParam.fromJson(Map<String, dynamic> json) => _$VideoListParamFromJson(json);
Map<String, dynamic> toJson() => _$VideoListParamToJson(this);
}
data里面的item命名为VideoInfo
使用jsonserializable
生成相关代码:
@JsonSerializable()
class VideoInfo {
@JsonKey(defaultValue: 0)
int format;
String description;
@JsonKey(defaultValue: false, nullable: true)
bool followed;
@JsonKey(defaultValue: 0, nullable: true)
int likeCount;
@JsonKey(defaultValue: false, nullable: true)
bool liked;
String playUrl;
String publishAvatar;
String publishNick;
@JsonKey(defaultValue: 0, nullable: true)
int publishUid;
@JsonKey(defaultValue: 0, nullable: true)
int publishVideosCount;
@JsonKey(defaultValue: 0, nullable: true)
int publishPhotosCount;
@JsonKey(defaultValue: 0, nullable: true)
int reviewCount;
@JsonKey(defaultValue: 0, nullable: true)
int topicId;
String topicName;
@JsonKey(defaultValue: 0, nullable: true)
int vid;
@JsonKey(defaultValue: 0, nullable: true)
int orientation;
@JsonKey(defaultValue: true, nullable: true)
bool published;
VideoInfo({ this.format, this.description, this.followed, this.likeCount, this.liked, this.playUrl, this.publishAvatar, this.publishNick, this.publishUid, this.publishVideosCount, this.publishPhotosCount, this.reviewCount, this.topicId, this.topicName, this.vid, this.orientation, this.published});
factory VideoInfo.fromJson(Map<String, dynamic> json) => _$VideoInfoFromJson(json);
Map<String, dynamic> toJson() => _$VideoInfoToJson(this);
}
@JsonSerializable()
class VideoListResult {
int count;
@JsonKey(name: "pagenum")
int pageNum;
@JsonKey(name: "pagesize")
int pageSize;
List<VideoInfo> data;
VideoListResult({this.count, this.pageNum, this.pageSize});
factory VideoListResult.fromJson(Map<String, dynamic> json) => _$VideoListResultFromJson(json);
Map<String, dynamic> toJson() => _$VideoListResultToJson(this);
}
/**
**onSuccess:请求成功回调
**onFailure:请求失败回调
**/
typedef RequestSuccess = void Function(dynamic result);
typedef RequestFailure = void Function(int code, String desc);
void loadFromServer({RequestSuccess onSuccess, RequestFailure onFailure}) {
int pageNum = 1;
RequestSuccess requestSuccess = (dynamic result) {
VideoListResult videoListResult = result as VideoListResult;
if (!mounted) {
return;
}
setState(() {
……
});
};
RequestFailure requestFailure = (int code, String desc) {
setState(() {
……
});
Log.d("result", "$code, $desc");
};
//请求参数
VideoListParam videoListParam =
VideoListParam(pageNum: pageNum, pageSize: pageSize);
Request.getVideoList(videoListParam, requestSuccess, requestFailure);
}
class Request {
static void getVideoList(VideoListParam param, RequestSuccess onRequestSuccess, RequestFailure onRequestFailure) async {
NetworkManager().post(getVideoListUrl(),
onRequestSuccess,
onRequestFailure,
queryParameters: param.toJson(),
parseResult: (Map<String, dynamic> json) => VideoListResult.fromJson(json));
}
}
Flutter下的网络请求总体来说,还是很好用的。各种回调也不像java那样使用了接口,而是直接用typedef
关键字来达到了接口相同的目的。希望本文对你有所帮助。
相关参考:https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51
相关参考:https://www.jianshu.com/p/cb72e2f5df1c
相关参考:https://www.jianshu.com/p/1352351c7d08