本來一直看書上,但是這部分講的比較模糊不怎么好理解,所以就準(zhǔn)備自己整理一下關(guān)于Dio網(wǎng)絡(luò)請求的知識點(diǎn)。
Dio Github地址:https://github.com/flutterchina/dio
Github上列出了大多數(shù)使用場景,先好好看好好學(xué)。
有人搬運(yùn)并翻譯的官方文檔:
https://blog.csdn.net/mqdxiaoxiao/article/details/102859897
dio: ^3.0.9
一 Json數(shù)據(jù)實(shí)體類
類比于Android原生,我們的網(wǎng)絡(luò)請求 如果是服務(wù)器返回JSON數(shù)據(jù)首先要有一個(gè)實(shí)體類來保存數(shù)據(jù),Android Studio上有JAVA和Kotlin的根據(jù)Json數(shù)據(jù)生成實(shí)體類插件,當(dāng)然也有Dart的生成實(shí)體類插件:
FlutterJsonBeanFactory直接搜(直接搜“ FlutterJson”就可以)
生成實(shí)體類:
https://blog.csdn.net/yuzhiqiang_1993/article/details/88533166

二 Dio請求的基本使用
2.1 get 請求百度首頁“http://www.baidu.com”,獲取其內(nèi)容:
這就是最簡單的一個(gè)Dio使用例子
Future<String> getBaiduContent() async {
try {
Response response = await Dio().get("http://www.baidu.com");
print(response);
return response.toString();
} catch (e) {
print(e);
}
}
可以運(yùn)行看一下:

2.2 get 有Json數(shù)據(jù)的請求返回
這里我采用的是極速數(shù)據(jù)的一個(gè)免費(fèi)開放api:笑話接口
get/post均可
https://api.jisuapi.com/xiaohua/text?pagenum=1&pagesize=1&sort=addtime&appkey=******
它返回的Json數(shù)據(jù)內(nèi)容如下:
{
"status": 0,
"msg": "ok",
"result": {
"total": 79630,
"pagenum": 1,
"pagesize": 1,
"list": [
{
"content": "王自健在節(jié)目中調(diào)侃,對于老婆打自己這件事沒有任何不滿,沒有任何抱怨。 這反映了一個(gè)問題,在中國: 老婆就是用來愛的, 老公就是用來打的。 中國婦女的地位真的提高了,可喜可賀!",
"addtime": "2020-03-28 03:20:02",
"url": "http://m.kaixinhui.com/detail-128412.html"
}
]
}
}
根據(jù)這個(gè)數(shù)據(jù)使用FlutterJsonBeanFactory 生成數(shù)據(jù)實(shí)體類:

創(chuàng)建接口請求方法:
Future<void> getJiSuJoke() async {
Dio dio = Dio();
try {
Response response = await Dio()
.get("https://api.jisuapi.com/xiaohua/text", queryParameters: {
"pagenum": 1,
"pagesize": 1,
"sort": "rand",
"appkey": "你的APPKEY"
});
print(response.data.toString());
} catch (e) {
print(e);
}
}
調(diào)用一下這個(gè)方法就可以看到請求結(jié)果了。
這里我們可以看到使用了queryParameters屬性,類似于Retrofit中的@Query,將get方法“?”后邊的值以map的形式傳入,這樣的好處是可以動(dòng)態(tài)修改請求參數(shù),靈活的修改請求方法傳入的參數(shù)針對不同情況的接口調(diào)用.稍微修改一下之前的方法:
Future<void> getJiSuJoke(int pagesize) async {
Dio dio = Dio();
int pagenum=1;
Map<String,dynamic> mData = {
"pagenum": pagenum,
"pagesize": pagesize,
"sort": "rand",
"appkey": "35dc30ebaa5940ce"
};
try {
Response response = await Dio()
.get("https://api.jisuapi.com/xiaohua/text", queryParameters: mData);
print(response.data.toString());
} catch (e) {
print(e);
}
}
到這里其實(shí)我們還沒有用到Json數(shù)據(jù)轉(zhuǎn)化的實(shí)體類,請看:
JisuJokeEntity jokeEntity;
Future<void> getJiSuJoke(int pagesize) async {
Dio dio = Dio(); //創(chuàng)建dio對象
int pagenum = 1; //設(shè)置請求參數(shù)
Map<String, dynamic> mData = {
"pagenum": pagenum,
"pagesize": pagesize,
"sort": "rand",
"appkey": "你的APPKEY"
};
try {
//開始請求
Response response = await dio
.get("https://api.jisuapi.com/xiaohua/text",
queryParameters: mData);
//請求體結(jié)果response,將數(shù)據(jù)轉(zhuǎn)化為實(shí)體類
jokeEntity =
JisuJokeEntity().fromJson(json.decode(response.data.toString()));
print(response);
print(jokeEntity.result.xList[0].content);
} catch (e) {
print(e);
}
}
想要在日志里看到請求過程,只需要添加打印日志攔截即可:
Dio dio = Dio(); //創(chuàng)建dio對象
//添加請求攔截 LogInterceptor內(nèi) 想看什么將什么傳入ture
dio.interceptors.add(LogInterceptor(responseBody: true,requestBody: true)); //開啟請求日志
post的用法和post傳輸數(shù)據(jù)官網(wǎng)上寫的很清楚,后續(xù)補(bǔ)充
二 Dio的單例模式和封裝
建議在項(xiàng)目中使用Dio單例,這樣便可對同一個(gè)dio實(shí)例發(fā)起的所有請求進(jìn)行一些統(tǒng)一的配置,比如設(shè)置公共header、請求基地址、超時(shí)時(shí)間等;
之前我們在每個(gè)請求方法中都新建了一個(gè)Dio對象,這樣其實(shí)是不推薦的,因?yàn)槲覀冃枰谡麄€(gè)項(xiàng)目中統(tǒng)一配置添加header,或者配置BaseUrl這些,所以推薦在一個(gè)項(xiàng)目中只使用一個(gè)Dio對象,方便統(tǒng)一管理。既然是這樣,這就要求我們對Dio進(jìn)行封裝。
這是我在網(wǎng)上找到了一個(gè)封裝類,自己稍微修改了一下下,內(nèi)置了get/post/downloadFile三個(gè)方法,待完善
import 'package:dio/dio.dart';
import 'api.dart';
class HttpUtil {
static HttpUtil instance;
Dio dio;
BaseOptions options;
CancelToken cancelToken = CancelToken();
static HttpUtil getInstance() {
if (null == instance) instance = HttpUtil();
return instance;
}
/*
* config it and create
*/
HttpUtil() {
//BaseOptions、Options、RequestOptions 都可以配置參數(shù),優(yōu)先級別依次遞增,且可以根據(jù)優(yōu)先級別覆蓋參數(shù)
options = BaseOptions(
//請求基地址,可以包含子路徑
baseUrl: Api.BASE_URL,
//連接服務(wù)器超時(shí)時(shí)間,單位是毫秒.
connectTimeout: 10000,
//響應(yīng)流上前后兩次接受到數(shù)據(jù)的間隔,單位為毫秒。
receiveTimeout: 5000,
//Http請求頭.
headers: {
//do something
"version": "1.0.0"
},
//請求的Content-Type,默認(rèn)值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType會(huì)自動(dòng)編碼請求體.
contentType: Headers.formUrlEncodedContentType,
//表示期望以那種格式(方式)接受響應(yīng)數(shù)據(jù)。接受四種類型 `json`, `stream`, `plain`, `bytes`. 默認(rèn)值是 `json`,
responseType: ResponseType.plain,
);
dio = Dio(options);
//添加日志請求攔截 顯示日志
dio.interceptors.add(LogInterceptor(responseBody: true,requestBody: true)); //開啟請求日志
//Cookie管理 這個(gè)暫時(shí)不清楚
//dio.interceptors.add(CookieManager(CookieJar()));
//添加攔截器
dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
//print("請求之前");
// Do something before request is sent
return options; //continue
}, onResponse: (Response response) {
// print("響應(yīng)之前");
// Do something with response data
return response; // continue
}, onError: (DioError e) {
// print("錯(cuò)誤之前");
// Do something with response error
return e; //continue
}));
}
/*
* get請求
* options:單個(gè)請求自定義配置
* data:query ?后的數(shù)據(jù)
*
*/
get(url, {data, options, cancelToken}) async {
Response response;
try {
response = await dio.get(url,
queryParameters: data, options: options, cancelToken: cancelToken);
// print('get success---------${response.statusCode}');
// print('get success---------${response.data}');
// response.data; 響應(yīng)體
// response.headers; 響應(yīng)頭
// response.request; 請求體
// response.statusCode; 狀態(tài)碼
} on DioError catch (e) {
print('get error---------$e');
formatError(e);
}
return response;
}
/*
* post請求
*
* formData:POST傳遞form表單
*/
post(url, {queryData,formData, options, cancelToken}) async {
Response response;
try {
response = await dio.post(url,data: formData,
queryParameters: queryData, options: options, cancelToken: cancelToken);
print('post success---------${response.data}');
} on DioError catch (e) {
print('post error---------$e');
formatError(e);
}
return response;
}
/*
* 下載文件
*/
downloadFile(urlPath, savePath) async {
Response response;
try {
response = await dio.download(urlPath, savePath,
onReceiveProgress: (int count, int total) {
//進(jìn)度
print("$count $total");
});
print('downloadFile success---------${response.data}');
} on DioError catch (e) {
print('downloadFile error---------$e');
formatError(e);
}
return response.data;
}
/*
* error統(tǒng)一處理
*/
void formatError(DioError e) {
if (e.type == DioErrorType.CONNECT_TIMEOUT) {
// It occurs when url is opened timeout.
print("連接超時(shí)");
} else if (e.type == DioErrorType.SEND_TIMEOUT) {
// It occurs when url is sent timeout.
print("請求超時(shí)");
} else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
//It occurs when receiving timeout
print("響應(yīng)超時(shí)");
} else if (e.type == DioErrorType.RESPONSE) {
// When the server response, but with a incorrect status, such as 404, 503...
print("出現(xiàn)異常");
} else if (e.type == DioErrorType.CANCEL) {
// When the request is cancelled, dio will throw a error with this type.
print("請求取消");
} else {
//DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
print("未知錯(cuò)誤");
}
}
/*
* 取消請求
*
* 同一個(gè)cancel token 可以用于多個(gè)請求,當(dāng)一個(gè)cancel token取消時(shí),所有使用該cancel token的請求都會(huì)被取消。
* 所以參數(shù)可選
*/
void cancelRequests(CancelToken token) {
token.cancel("cancelled");
}
}
然后我們需要一個(gè)統(tǒng)一管理URL的API
class Api {
//應(yīng)該根據(jù)當(dāng)前的編譯環(huán)境確定BASE_URL
static const String BASE_URL = "https://api.jisuapi.com/";
static const String JISUJOKE = "xiaohua/text";
}
然后就可以用了,還是剛剛的笑話接口:
Future<void> getJiSuJoke2() async {
Map<String, dynamic> mData = {
"pagenum": 1,
"pagesize": 1,
"sort": "rand",
"appkey": APPKEY
};
try {
//開始請求
var response =await HttpUtil().post(Api.JISUJOKE,
queryData: mData);
//請求體結(jié)果response,將數(shù)據(jù)轉(zhuǎn)化為實(shí)體類
jokeEntity =
JisuJokeEntity().fromJson(json.decode(response.data.toString()));
//print(response);
print(jokeEntity.result.xList[0].content);
} catch (e) {
print(e);
}
}
這里有點(diǎn)Android MVP的那種感覺了 不過目前只是demo項(xiàng)目 沒有完全分開,之后嘗試自己搭建一套Flutter的MVP或者M(jìn)VVM架構(gòu)。
在封裝的HttpUtil中,預(yù)留了許多空,比如請求攔截的操作呀,請求成功之后服務(wù)器的返回碼判斷呀,header的添加呀,各個(gè)項(xiàng)目都有不同所以暫時(shí)先預(yù)留空位。
HttpUtil中的請求方法中都保留了一個(gè)參數(shù)options可以為每個(gè)請求單獨(dú)配置請求參數(shù)