概要:
dio已经更新到4.0版本,相对已经很稳定了,不过为了通用性,如果直接应用在项目中,难免比较离散。一般项目开发都是多人共同协作,所以统一的规范,简洁的调用方式,稳定的性能是必不可少的,所以才有二次封装,方便实际项目中使用。
首先预览下文件结构:
一、整体设计描述
- Api 对应项目中业务开发时候的调用接口
- Code定义了常见请求状态,例如网络错误、超时、成功等
- DataHelper定义了数据加密工具代码
- DioLogInterceptor页面Loading拦截器,用于网络数据返回加工预处理
- HttpManager核心工具类,提供get和post请求入口
- Loading网络请求等待页面处理
- ResponseInterceptors拦截器,用于网络数据返回加工预处理
- ResultData网络返回数据二次封装
- UrlPath对业务URL统一管理
二、下面对Api、HttpManager、ResponseInterceptors、ResultData详解介绍
Api:业务开发时候,首先使用Api来获取网络数据
Future<Autogenerated> getData() async {
ResultData resultData = await Api.request({});
final autogenerated = resultData.data;
final datas = autogenerated["data"]["datas"];
print("-----------------------");
print(datas);
print("-----------------------");
for(var sub in datas) {
listData.add(Datas.fromJson(sub));
}
state.listData = listData;
update();
return autogenerated;
}
/// Api实现
class Api {
///示例请求
static request(Map<String, dynamic> param) {
return HttpManager.getInstance().get(UrlPath.testPath, params: param);
}
static requestOther(Map<String, dynamic> param) {
return HttpManager.getInstance(baseUrl: UrlPath.otherUrl)
.post(UrlPath.testPath, params: param);
}
}
HttpManager:Api调用的接口,封装在HttpManager中
///通用的GET请求
get(api, {params, withLoading = true}) async {
if (withLoading) {
Loading.show();
}
Response response;
params["platform"] = "android";
params["system"] = "1.0.0";
params["channel"] = "App";
params["time"] = new DateTime.now().millisecondsSinceEpoch.toString();
params["sign"] = DataHelper.encryptParams(params);
final Options options = new Options(method: "get", headers: httpHeaders);
try {
response = await _dio.get(api, queryParameters: params, options: options);
if (withLoading) {
Loading.dismiss();
}
} on DioError catch (e) {
if (withLoading) {
Loading.dismiss();
}
return resultError(e);
}
if (response.data is DioError) {
return resultError(response.data['code']);
}
return response.data;
}
其中涉及到Headers和拦截器设置,要根据业务后端而定
///通用全局单例,第一次使用时初始化
HttpManager._internal({String baseUrl}) {
if (null == _dio) {
_dio = new Dio(new BaseOptions(
baseUrl: UrlPath.baseUrl,
connectTimeout: CONNECT_TIMEOUT,
receiveTimeout: RECEIVE_TIMEOUT,
headers: httpHeaders,
validateStatus: (status) {
// 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格)
return true;
},
));
_dio.interceptors.add(new DioLogInterceptor());
_dio.interceptors.add(new ResponseInterceptors());
}
}
/// 自定义Header
Map<String, dynamic> httpHeaders = {
'Accept': 'application/json,*/*',
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS, HEAD",
};
///日志拦截器
class DioLogInterceptor extends Interceptor {
///请求前
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
String requestStr = "\n==================== REQUEST ====================\n"
"- URL:\n${options.baseUrl + options.path}\n"
"- METHOD: ${options.method}\n";
requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";
final data = options.data;
if (data != null) {
if (data is Map)
requestStr += "- BODY:\n${data.mapToStructureString()}\n";
else if (data is FormData) {
final formDataMap = Map()
..addEntries(data.fields)
..addEntries(data.files);
requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";
} else
requestStr += "- BODY:\n${data.toString()}\n";
}
// print(requestStr);
handler.next(options);
}
/// 拦截器 数据初步处理
class ResponseInterceptors extends InterceptorsWrapper {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
RequestOptions option = response.requestOptions;
// print("---------response.data----------");
// print(response.data);
// print("---------response.data end----------");
try {
if (option.contentType != null && option.contentType.contains("text")) {
response.data = ResultData(response.data, true, 200);
handler.next(response);
}
///一般只需要处理200的情况,300、400、500保留错误信息,外层为http协议定义的响应码
if (response.statusCode == 200 || response.statusCode == 201) {
///内层需要根据公司实际返回结构解析,一般会有code,data,msg字段
int code = response.data["errorCode"];
if (code == 0) {
response.data = ResultData(response.data, true, 200,
headers: response.headers);
handler.next(response);
// print("----------response.data----------");
// print(response.data);
// print("----------response.data end----------");
return;
} else {
response.data = ResultData(response.data, false, 200,
headers: response.headers);
handler.next(response);
return;
}
}
} catch (e) {
print("ResponseError====" + e.toString() + "****" + option.path);
response.data = ResultData(response.data, false, response.statusCode,
headers: response.headers);
handler.next(response);
return;
}
response.data = ResultData(response.data, false, response.statusCode, headers: response.headers);
handler.next(response);
}
...
ResultData:拦截器中对返回数据进行了二次封装,具体实现要根据各自业务需要而定
class ResultData {
var data;
bool isSuccess;
int code;
var headers;
ResultData(this.data, this.isSuccess, this.code, {this.headers});
}
这样,一个网络请求封装基本就搞定了,要根据业务需要,灵活运用。
项目地址:https://github.com/lizhongfu/Flutter_Network_Dio