[TOC]
网络框架主要包含2个方面
- 网络访问
- 数据解析
1 网络访问
dio库
# 网络请求
dio: ^2.0.7
get用queryParameters参数,post用data参数。extra参数并没有什么实际的作用
这里技术胖的博客有点的问题, post是用的queryParameters的参数,这个参数其实应该是get使用的。post使用的应该是data参数。
-
在最后拼接URI的时候用到的只是queryParameters参数。DefaultHttpClientAdapter.fetch中最后openurl的时候用的就是 URI。只拼接了 queryParameters,其他的什么extra并没加进来
-
在head拼接的时候 dio._transformData实际上只拼接了data的相关数据,其他的数据是并没有放到head中的。
dio说明参数也是放在data中,上面的博客应该是有错误的。
1.1 添加拦截器
跟Android的相关框架相似的,这里可以对网络添加拦截器,对请求数据进行前,后的相关操作。
Dio getDio({String baseUrl}) {
if (baseUrl == null) {
baseUrl = KCWC;
}
dio = Dio(
BaseOptions(
baseUrl: baseUrl,
connectTimeout: 5000,
receiveTimeout: 5000,
contentType: ContentType.parse(
"application/x-www-form-urlencoded",
),
),
);
dio.interceptors.add(
new InterceptorsWrapper(onRequest: (options) {
///请求数据前对数据进行统一的处理
if (options.queryParameters != null) {
options.queryParameters
.addAll({"machine_type": Platform.operatingSystem});
}
if (options.data is Map<String, String>) {
(options.data as Map<String, String>)
.addAll({"machine_type": Platform.operatingSystem});
}
return options;
}, onResponse: (response) {
///这个地方用来打印返回结果监听
print(response);
return response;
}),
);
return dio;
}
1.2 对网络请求状态进行管理
封装网络状态的相关状态,能反映网络状态,以及相关的数据处理结果。
/// 获取网络数据接口 前几个参数跟[Dio.request]一致。
/// [T] 目标数据结构,基于项目是[BaseJson.data]中的数据结构。如果不关心返回数据,可以不用传入
/// [start] 开始请求
/// [end] 请求结束
/// [succeed] 接口返回成功后返回对应的[BaseJson.data]数据,如果有指定[T]需要与[builder]配合使用。
/// [failure] 数据错误的回调,配合[nullWord]来处理空数据的回调
/// [builder] 如果指定了泛型T 需要传入对应的[builder]用来解析JSON。其中builder的参数根据具体的JSON数据,可能是list,可能是map
/// [options] 为空默认用get,快捷方式[post],[delete]
/// [responseCallBack] 直接获取相应的Response,只要有就会返回。与[succeed]不冲突
/// [networkError] 相关的网络错误返回,详见:[NetWorkStatStatus]
/// [ignoreStatusCode] 是否忽略状态码。为0表示成功。默认不忽略。
request<T>(
url, {
data,
Map<String, dynamic> queryParameters,
CancelToken cancelToken,
Options options,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress,
String baseUrl = KCWC,
VoidCallback start,
VoidCallback end,
ValueChanged<Response> responseCallBack,
DataBeanBuilder<T> builder,
ValueChanged<T> succeed,
ValueChanged<String> failure,
String nullWord = "返回数据为空",
ValueChanged<BaseJson> networkError,
bool ignoreStatusCode = false,
}) async {
if (networkError == null) {
networkError = defaultCallBack;
}
if (start == null) {
start = () => defaultCallBack("start");
}
if (end == null) {
end = () => defaultCallBack("end");
}
if (responseCallBack == null) {
responseCallBack = (v) => defaultCallBack(v);
}
await start();
if (url == null) {
await end();
return;
}
// 判断网络
var connectivityResult = await (new Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
networkError(NetWorkStatStatus.NO_NET_WORK);
await end();
return;
}
Response response;
try {
response = await getDio(baseUrl: baseUrl).request(url,
data: data,
cancelToken: cancelToken,
queryParameters: queryParameters,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
options: options);
} on DioError catch (error) {
await end();
CommonUtils.log2(["请求异常", url, error.toString()]);
switch (error.type) {
case DioErrorType.CONNECT_TIMEOUT:
networkError(NetWorkStatStatus.CONNECT_TIMEOUT);
return;
case DioErrorType.SEND_TIMEOUT:
networkError(NetWorkStatStatus.SEND_TIMEOUT);
return;
case DioErrorType.RECEIVE_TIMEOUT:
networkError(NetWorkStatStatus.RECEIVE_TIMEOUT);
return;
case DioErrorType.RESPONSE:
networkError(NetWorkStatStatus.RESPONSE);
return;
case DioErrorType.CANCEL:
networkError(NetWorkStatStatus.CANCEL);
return;
default:
networkError(NetWorkStatStatus.DEFAULT);
return;
}
}
if (response == null) {
await end();
networkError(NetWorkStatStatus.NULL_BACK);
return;
}
responseCallBack(response);
await end();
BaseJson<T> baseJson;
try {
baseJson = BaseJson<T>.fromJsonString(response.toString(), builder);
} catch (e) {
CommonUtils.log2([e]);
if (failure != null) {
failure(NetworkStatusCode.DATA_ERROR);
}
return;
}
BaseJson.checkAns(baseJson,
succeed: succeed, failure: failure, nullWord: nullWord);
}
static Options post = Options(method: "POST");
static Options put = Options(method: "PUT");
static Options delete = Options(method: "DELETE");
2 对数据进行封装。
- json解析
- 数据模板
由于这里不支持反射,所以原本的那些框架都不能用,这个提供的方案其实都是本地自动生成相关的代码。
个人推荐使用网络模板生成,更简单,直观
2.1 json解析
2.1.1 方案1:使用官方的方式
这种方案显得很臃肿,引入一个个包不说还要命令生成。太麻烦。
git:283e705a6ce17da3f05c2ed58951501966b13fe6
2.1.1.1 加入相关的依赖 pubspec.yaml
dependencies:
# Your other regular dependencies here
json_annotation: ^2.0.0
dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.0.0
json_serializable: ^2.0.0
2.1.1.2 自己写相关的原始类,预留申明 参考
2.1.1.3. 运行代码生成程序 参考
flutter packages pub run build_runner build
2.1.2 方案2 直接生成模板
直接用工具生成需要的代码,不必转几次。这里唯一不友好的是,这样不能用@JsonKey这种关键字,来起别名,但是这样无伤大雅。要别名手动改就行。而且,实际运用中并没有多少次用到了别名。
2.1.2.1 由于无法泛型实例化,这里要手动解析下基类数据
只能将具体的数据放在data中,当string处理,然后进一步的解析json。参考
2.1.2.2 复杂的数据结构这个工具网站无法处理,也是需要自己去添加
2.2 数据模板
一般来讲,数据都会有一个统一的数据模板。其中 data中的数据才是具体内容,这里就需要一个通用模板
{
"code": 1,
"msg": "xxx",
"data": ""
}
由于flutter把dart的反射给屏蔽了,我们也没必要强行使用反射。这里我们传入一个构造方法来当作构造器DataBeanBuilder。
并通过这个构造器来构成相应的数据类型。
class BaseJson<T> {
int code;
String msg;
T data;
BaseJson(this.code, this.msg, this.data);
BaseJson.fromJson(Map<String, dynamic> json, DataBeanBuilder<T> builder) {
code = json['code'];
msg = json['msg'];
if (builder != null) {
data = builder(json['data']);
} else {
data = json['data'];
}
}
BaseJson.fromJsonString(String jsonString, DataBeanBuilder<T> builder) {
Map<String, dynamic> jsonMap = json.decode(jsonString);
try {
code = jsonMap['code'];
} catch (e) {
CommonUtils.log2([e]);
code = -1;
}
msg = jsonMap['msg'];
if (builder != null) {
data = builder(jsonMap['data']);
} else {
data = jsonMap['data'];
}
}
}
/// 通过第一步解析[BaseJson]得到的data的数据来再次解析成对应的数据,[value]为list或者map
typedef DataBeanBuilder<T> = T Function(dynamic value);
///当构建的DATA是list的时候用
List<T> build<T>(dynamic d, DataBeanBuilder<T> builder) {
if (d != null && d is List) {
return d.map((v) => builder(v)).toList();
}
return [];
}
范例
getAllSetList(
{ValueChanged<List<AllocationSetListItemBean>> succeed,
VoidCallback start,
VoidCallback end,
ValueChanged<String> failure,
ValueChanged<BaseJson> networkError,
String nullWord}) async {
String testUrl = "http://ya7/v1/cluesalesman?token=xxx";
///这里的builder中其实要得只是list的数据,所有,就直接在这里复写。
DataBeanBuilder<List<AllocationSetListItemBean>> builder = (data) {
if (data == null || data["list"] is! List) return [];
return (data["list"] as List).map((v) {
return new AllocationSetListItemBean.fromJson(v);
}).toList();
};
DioHelper.getInstance().request<List<AllocationSetListItemBean>>(
Test.testUrl ? testUrl : url,
queryParameters: Test.testUrl ? null : {"token": token},
start: start,
end: end,
succeed: succeed,
failure: failure,
nullWord: nullWord,
networkError: networkError,
builder: builder,
);
}
网友评论