在flutter中进行网络请求有多种方式,这里我使用的是dio。dio这个库被封装的很好,使用起来也很简单,但为了更方便我还是自己在做了简单的封装。整体代码如下
class HttpRequestManager{
factory HttpRequestManager() =>getInstance();
static HttpRequestManager get instance =>getInstance();
static HttpRequestManager _instance;
Dio dio;
static CancelToken cancelToken;
String baseUrl="http://cardapi.mimiplay.cn/cardtest-api/";
String baseTestUrl="http://cardapi.mimiplay.cn/cardtest-api/";
HttpRequestManager._init(){
dio=new Dio();
BaseOptions options=BaseOptions(
baseUrl:GlobalData.isProduct? baseUrl:baseTestUrl,
connectTimeout: 20000,
receiveTimeout: 20000,
headers: {
"content-type": "application/json;charset=UTF-8"
},
contentType: Headers.jsonContentType,//请求类型
responseType: ResponseType.json,//接收类型
);
dio.options=options;
cancelToken=new CancelToken();
dio.interceptors.add(InterceptorsWrapper(//拦截器
onRequest: (RequestOptions options){
LogOut.I("请求地址: ${options.uri}");
LogOut.I("请求参数: ${options.data}");
},
onResponse: (Response e){
// if (e.statusCode==200) {
// Map map=json.decode(e.data);
// BaseResult<T> data=BaseResult.fromJson(map);
// }
LogOut.I("服务器返回数据----- ${e.toString()}");
}
));
}
static HttpRequestManager getInstance(){
if (_instance==null) {
_instance=new HttpRequestManager._init();
}
return _instance;
}
}
代码也很简单,就是在单例类里对dio进行一些设置,比如请求超时,请求头,返回类型等。然后也可以为其设置拦截,拦截有三个方法分别是
onrequest:可以在此方法内设置请求前的操作,比如设置token等,我这里只是打印了请求地址和参数
onResponse:此处拦截工作在数据返回之后,可以在方法内对dio请求的数据做二次封装或者转实体类等相关操作
onError:处理错误
做完了对dio的基本封装,下面就看下怎么使用其做网络请求,并对数据进行网络解析。具体代码如下
typedef CallBackSuccess =Function(BaseResult result);//设置请求成功方法
typedef CallBackFail =Function(String message);//设置请求失败方法
class HttpRequest{
static void postRequest(String url,{Map<String, dynamic> data,CancelToken cancelToken,Options options,CallBackSuccess success,CallBackFail fail}) async{
try {
options=options??Options(method: "POST");//默认请求方法为post
Response response= await HttpRequestManager.instance.dio.request
(url,data:data,cancelToken:cancelToken,options: options);
if (response.statusCode==200) {
Map<String, dynamic> map=json.decode(response.toString());//请求成功将数据转化成map
LogOut.I("data类型 ${map["data"].runtimeType}");//得到map里data的数据类型,方便接下来根据数据类型做解析
BaseResult result;//数据基类
if (map["data"] == null) {//data为null
result=BaseResult.fromNoData(map);
}else if (map["data"] is Map) {//data为map类型
result=BaseResult.fromJson(map);
}else if(map["data"] is List){//data为list类型
LogOut.I("list类型");
result=BaseResult.fromJsonList(map);
}else if(map["data"] is String){//data为字符串
result=BaseResult.fromString(map);
}
if(result.code=="900002"){
//todo 登录失效处理
Fluttertoast.showToast(msg: result.message);
SPUtil util=await SPUtil.getInstances();
await util.setString("userId", "");
await util.setString("tokenId", "");
await util.setString("phone", "");
LogOut.I("登录失效");
fail(result.message);
MyRouters.navigatorKey.currentState.push(MaterialPageRoute(builder: (context){
return new LoginPage();//登陆失效返回登录页
},));
}else{
if (success!=null) {
success(result);
}
}
}else{
if (fail!=null) {
fail(response.statusMessage+"错误地址:$url");
}else{
Fluttertoast.showToast(msg: response.statusMessage);
}
}
} catch (e) {
print(e);
if (fail!=null) {
fail(e.toString()+"错误地址:$url");
}
// callBack.fail(e.toString()+"错误地址:$url");
}
}
}
在上面的代码中,网络请求方法默认为post,同时也做了数据解析。在flutter中数据解析不想Android那样方便,我这里是对其进行了分类型解析,这样不管后台返回的data为何种类型都有对应的解析方式。解析成功后就是看自己的需求做操作了,我在这里做了登录失效和请求成功的处理。成功时,将baseresult传入success方法里。至于baseresult是怎么分类型进行解析的,我这里借助了FlutterJsonBeanFactory这个插件实现的。下面看baseresult的具体代码
class BaseResult<T>{
String code;
String message;
List<dynamic> listResult;
Map<String, dynamic> result;
String data;
BaseResult({this.code, this.message, this.listResult,this.result,this.data});
factory BaseResult.fromJsonList(Map<String, dynamic> json) {//data为list
return BaseResult(
code:json['code'],
message: json['message'],
listResult: json['data'],
);
}
factory BaseResult.fromNoData(Map<String, dynamic> json) {//data为空
return BaseResult(
code:json['code'],
message: json['message'],
);
}
factory BaseResult.fromJson(Map<String, dynamic> json) {//data为map
return BaseResult(
code:json['code'],
message: json['message'],
result: json['data'],
);
}
factory BaseResult.fromString(Map<String, dynamic> json) {//data为lString
return BaseResult(
code:json['code'],
message: json['message'],
data: json['data'],
);
}
List<T> getListData<T>(){//data为list,得到data
List<T> list=new List<T>();
if (listResult!=null) {
listResult.forEach((value) {
list.add(JsonConvert.fromJsonAsT<T>(value));//插件方法
});
}
return list;
}
T getMapData<T>(){//data为map,得到data
LogOut.I("result $result");
return JsonConvert.fromJsonAsT<T>(result);//插件方法
}
String getData(){//data为string,得到data
return data;
}
@override
String toString() {
return 'BaseResult{code: $code, message: $message, listResult: $listResult, result: $result, data: $data}';
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['code'] = this.code;
data['message'] = this.message;
if (this.result != null) {
data['data'] = this.result;
}else if(this.listResult != null){
data['data'] = this.listResult;
}else if(this.data!=null){
data['data'] = this.data;
}
return data;
}
}
上面网络封装和数据解析都已经准备好了,接下来就是调用的问题了,我这里做了一个类专门放各个接口请求。具体代码如下
class RequestUtil{
factory RequestUtil() =>getInstances();
static RequestUtil get requestUtil =>getInstances();
RequestUtil._init();
static RequestUtil _requestUtil;
static RequestUtil getInstances(){
if (_requestUtil==null) {
_requestUtil=new RequestUtil._init();
}
return _requestUtil;
}
Future<Map<String, dynamic>> _setBaseMap() async{//固定参数
LoginManager loginManager=LoginManager.getInstances();
LoginInfo info=await loginManager.getLogin();
String userId=info.userId;
String tokenId=info.tokenId;
Map<String, dynamic> map={
"userId":userId,
"tokenId":tokenId,
"clientVersion":clientVersion,
"clientType":clientType
};
return map;
}
///消息公告
getMessage(Map<String, dynamic> data,{success(BaseResult result),fail(message)}) async{
String url="notice/find/message";
Map<String, dynamic> map=await _setBaseMap();
map.addAll(data);
HttpRequest.postRequest(url,data: map,success:success,fail: fail);
}
///得到用户信息
getUserInfo({success(BaseResult result),fail(message)}) async{
String url="account/userAccount/108/myAccountInfo";
Map<String, dynamic> map=await _setBaseMap();
HttpRequest.postRequest(url,data: map,success:success,fail: fail);
}
///登录
doLogin(Map<String, dynamic> data,{success(BaseResult result),fail(message)}) async{
String url="auth/login/normal";
Map<String, dynamic> map=await _setBaseMap();
map.addAll(data);
HttpRequest.postRequest(url,data: map,success:success,fail: fail);
}
}
然后就是调用了,这里展示data为map类型和list类型的调用。首先是map类型
RequestUtil.getInstances().doLogin({
"userMobileNo":userPhone,
"userPass":pass,
}, success: (result){//成功回调
LoginResult resultLogin=result.getMapData<LoginResult>();//通过baseresult的getMapData得到data
LogOut.I("${resultLogin.toString()}");
},fail: (message){
});
然后是list类型
void getMessage(){
RequestUtil.getInstances().getMessage({
"pageNum":1,
"pageSize":15
}, success: (result){
controller.refreshCompleted();
List<MessageResult> list=result.getListData<MessageResult>();//通过baseresult的getListData得到data
LogOut.I("解析后 $list");
},fail: (message){
});
}
到这里就将我自己做的flutter关于dio的网络请求与数据解析说完了,我个人觉得比较烦人的还是在数据解析这方面,关于flutter的数据解析还有多种方法,比如使用json_serializable,我这里觉得这种方式比较舒服就是用了这种。
网友评论