Flutter框架搭建之网络请求封装
网络请求库
常见网络请求库如下:
- 自带HttpClient
- 三方库Http
封装网络请求起码包含RESTful请求和统一拦截器,这也是最基本的需求。Dio功能很强大,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等,具体可参考其Github。本文章将采用Dio,从最基本的需求出发进行封装网络请求。
Dio会对返回的json数据自动进行解析,这是特别要注意的地方。
添加依赖
dependencies:
dio: ^3.0.3
接口定义
首先需要一个dart文件进行接口url的定义。
class Api {
static const host = "http://192.168.3.168:9999";
//登录
static const LOGIN = "$host/users/login";
//测试
static const MODULE_TREE = "$host/auth/acl_module/tree";
}
接口返回数据
{
"code":0,
"subCode":0,
"msg":"OK",
"success":true,
"data":""
}
DioUtil
注释较为详细了。
class DioUtil {
factory DioUtil() => _getInstance();
//一个单例
static DioUtil get instance => _getInstance();
static DioUtil _instance;
static Dio _dio;
DioUtil._internal() {
//初始化Dio
BaseOptions _options = BaseOptions(
baseUrl: Api.host,
connectTimeout: 5000,
sendTimeout: 5000,
receiveTimeout: 5000,
);
_dio = new Dio(_options);
//dio全局拦截器
_dio.interceptors
.add(InterceptorsWrapper(
//请求拦截
onRequest: (RequestOptions options) async {
//携带本地存储的session id
UserInfo.getSession().then((session) {
options.headers["Cookie"] = session;
});
return options;
//错误拦截 错误类型为DioError
}, onError: (dioError) async {
//不是每个错误都有response
if (null == dioError.response) return dioError;
//http请求的状态判断,若接口返回json则response为解析过得json结构
//也就拿不到http请求的状态码了
if (dioError.response.statusCode == 401) {
//session过期
eventBus.fire(ReLoginEvent());//eventbus发送重新登录消息
return 0;//自定义一个返回,在_error()中做判断
} else if (dioError.response.statusCode == 403) {
//无权限
eventBus.fire(UnAuthorityEvent());//eventbus发送无权限的消息
return 0;//自定义一个返回,在_error()中做判断
} else {
return dioError;
}
}));
//开启请求日志
_dio.interceptors.add(LogInterceptor(responseBody: false));
}
static DioUtil _getInstance() {
if (_instance == null) {
_instance = new DioUtil._internal();
}
return _instance;
}
//GET请求
get(url,
{data,
Function onSuccess,
Function onFail,
bool saveSession = false}) async {
await _dio.get(url, queryParameters: data).then((Response res) {
_success(res, onSuccess, onFail, saveSession);
}).catchError((e) {
_error(e, onFail);
});
}
//POST请求
post(url, {data, Function onSuccess, Function onFail}) async {
await _dio.post(url, queryParameters: data).then((Response res) {
_success(res, onSuccess, onFail, false);
}).catchError((e) {
_error(e, onFail);
});
}
//成功处理
_success(Response res, Function onSuccess, Function onFail, saveSession) {
debugPrint("_success : ${res.data}");
var result = BaseResult.fromJsonExceptData(res.data);
if (result.success) {
//本地存储session id
if (saveSession) {
var session = res.headers["set-cookie"]
.toString()
.split(";")[0]
.replaceFirst("[", "")
.toString();
UserInfo.setSession(session);
}
onSuccess(res.data);
} else {
onFail(result.msg);
}
}
//失败处理
_error(dynamic e, Function onFail) {
debugPrint("catchError : $e");
if (e.toString().contains("Connection timed out")) {
onFail("连接超时");
} else if (e.toString().contains("Network is unreachable")) {
onFail("网络不可达");
} else if (e.toString().contains("Connection refused")) {
onFail("拒绝连接");
} else if (e.toString().contains("No route to host")) {
onFail("没有到主机的路由");
} else if (e.message.toString() == "0") { //错误拦截里的判断,防止多次回调
debugPrint("已拦截");
} else {
onFail("未知错误");
}
}
}
使用
DioUtil.instance.get(Api.MODULE_TREE, onSuccess: (resp) {
///TODO
}, onFail: (error) {
///TODO
});
//POST同理
一些问题
- dio错误拦截里必须return出,所以无法真正拦截,项目里return 0,之后在_error中判断e.message即可。
- 本文用的dio^3.0.3,api和2.x和1.x相比差别甚大,需注意区分。