dio是Flutter中文網(wǎng)開(kāi)源的一個(gè)強(qiáng)大的Dart Http請(qǐng)求庫(kù)金闽,支持Restful API暇唾、FormData促脉、攔截器、請(qǐng)求取消策州、Cookie管理瘸味、文件上傳/下載、超時(shí)等...
文檔語(yǔ)言: English | 中文簡(jiǎn)體
dio
一個(gè)強(qiáng)大的Dart Http請(qǐng)求庫(kù)够挂,支持Restful API旁仿、FormData、攔截器孽糖、請(qǐng)求取消枯冈、Cookie管理、文件上傳/下載办悟、超時(shí)等...
添加依賴
dependencies:
dio: ^x.x.x // latest version
一個(gè)極簡(jiǎn)的示例
import 'package:dio/dio.dart';
Dio dio = new Dio();
Response response=await dio.get("https://www.google.com/");
print(response.data);
內(nèi)容列表
示例
發(fā)起一個(gè) GET
請(qǐng)求 :
Response response;
response=await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
// 請(qǐng)求參數(shù)也可以通過(guò)對(duì)象傳遞尘奏,上面的代碼等同于:
response=await dio.get("/test",data:{"id":12,"name":"wendu"})
print(response.data.toString());
發(fā)起一個(gè) POST
請(qǐng)求:
response=await dio.post("/test",data:{"id":12,"name":"wendu"})
發(fā)起多個(gè)并發(fā)請(qǐng)求:
response= await Future.wait([dio.post("/info"),dio.get("/token")]);
下載文件:
response=await dio.download("https://www.google.com/","./xx.html")
發(fā)送 FormData:
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData)
通過(guò)FormData上傳多個(gè)文件:
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
"file1": new UploadFileInfo(new File("./upload.txt"), "upload1.txt")
"file2": new UploadFileInfo(new File("./upload.txt"), "upload2.txt")
});
response = await dio.post("/info", data: formData)
…你可以在這里獲取所有示例代碼.
Dio APIs
創(chuàng)建一個(gè)Dio實(shí)例,并配置它
你可以使用默認(rèn)配置或傳遞一個(gè)可選 Options
參數(shù)來(lái)創(chuàng)建一個(gè)Dio實(shí)例 :
Dio dio = new Dio; // 使用默認(rèn)配置
// 配置dio實(shí)例
dio.options.baseUrl="https://www.xx.com/api"
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout=3000;
// 或者通過(guò)傳遞一個(gè) `options`來(lái)創(chuàng)建dio實(shí)例
Options options= new Options(
baseUrl:"https://www.xx.com/api",
connectTimeout:5000,
receiveTimeout:3000
);
Dio dio = new Dio(options);
Dio實(shí)例的核心API是 :
Future<Response> request(String path, {data, Options options,CancelToken cancelToken})
response=await request("/test", data: {"id":12,"name":"xx"}, new Options(method:"GET"));
請(qǐng)求方法別名
為了方便使用病蛉,Dio提供了一些其它的Restful API, 這些API都是request
的別名炫加。
Future<Response> get(path, {data, Options options,CancelToken cancelToken})
Future<Response> post(path, {data, Options options,CancelToken cancelToken})
Future<Response> put(path, {data, Options options,CancelToken cancelToken})
Future<Response> delete(path, {data, Options options,CancelToken cancelToken})
Future<Response> head(path, {data, Options options,CancelToken cancelToken})
Future<Response> put(path, {data, Options options,CancelToken cancelToken})
Future<Response> path(path, {data, Options options,CancelToken cancelToken})
Future<Response> download(String urlPath, savePath,
{OnDownloadProgress onProgress, data, bool flush: false, Options options,CancelToken cancelToken})
請(qǐng)求配置
下面是所有的請(qǐng)求配置選項(xiàng)瑰煎。 如果請(qǐng)求method
沒(méi)有指定,則默認(rèn)為GET
:
{
/// Http method.
String method;
/// 請(qǐng)求基地址,可以包含子路徑琢感,如: "https://www.google.com/api/".
String baseUrl;
/// Http請(qǐng)求頭.
Map<String, dynamic> headers;
/// 連接服務(wù)器超時(shí)時(shí)間丢间,單位是毫秒.
int connectTimeout;
/// 響應(yīng)流上前后兩次接受到數(shù)據(jù)的間隔探熔,單位為毫秒驹针。如果兩次間隔超過(guò)[receiveTimeout],
/// [Dio] 將會(huì)拋出一個(gè)[DioErrorType.RECEIVE_TIMEOUT]的異常.
/// 注意: 這并不是接收數(shù)據(jù)的總時(shí)限.
int receiveTimeout;
/// 請(qǐng)求數(shù)據(jù),可以是任意類型.
var data;
/// 請(qǐng)求路徑诀艰,如果 `path` 以 "http(s)"開(kāi)始, 則 `baseURL` 會(huì)被忽略柬甥; 否則,
/// 將會(huì)和baseUrl拼接出完整的的url.
String path="";
/// 請(qǐng)求的Content-Type,默認(rèn)值是[ContentType.JSON].
/// 如果您想以"application/x-www-form-urlencoded"格式編碼請(qǐng)求數(shù)據(jù),
/// 可以設(shè)置此選項(xiàng)為 `ContentType.parse("application/x-www-form-urlencoded")`, 這樣[Dio]
/// 就會(huì)自動(dòng)編碼請(qǐng)求體.
ContentType contentType;
/// [responseType] 表示期望以那種格式(方式)接受響應(yīng)數(shù)據(jù)其垄。
/// 目前 [ResponseType] 接受三種類型 `JSON`, `STREAM`, `PLAIN`.
///
/// 默認(rèn)值是 `JSON`, 當(dāng)響應(yīng)頭中content-type為"application/json"時(shí)苛蒲,dio 會(huì)自動(dòng)將響應(yīng)內(nèi)容轉(zhuǎn)化為json對(duì)象。
/// 如果想以二進(jìn)制方式接受響應(yīng)數(shù)據(jù)绿满,如下載一個(gè)二進(jìn)制文件臂外,那么可以使用 `STREAM`.
///
/// 如果想以文本(字符串)格式接收響應(yīng)數(shù)據(jù),請(qǐng)使用 `PLAIN`.
ResponseType responseType;
/// 用戶自定義字段喇颁,可以在 [Interceptor]漏健、[TransFormer] 和 [Response] 中取到.
Map<String, dynamic> extra;
}
這里有一個(gè)完成的示例.
響應(yīng)數(shù)據(jù)
當(dāng)請(qǐng)求成功時(shí)會(huì)返回一個(gè)Response對(duì)象,它包含如下字段:
{
/// 響應(yīng)數(shù)據(jù)橘霎,可能已經(jīng)被轉(zhuǎn)換了類型, 詳情請(qǐng)參考Options中的[ResponseType].
var data;
/// 響應(yīng)頭
HttpHeaders headers;
/// 本次請(qǐng)求信息
Options request;
/// Http status code.
int statusCode;
/// 響應(yīng)對(duì)象的自定義字段(可以在攔截器中設(shè)置它)蔫浆,調(diào)用方可以在`then`中獲取.
Map<String, dynamic> extra;
}
示例如下:
Response response=await dio.get("https://www.google.com");
print(response.data);
print(response.headers);
print(response.request);
print(statusCode);
攔截器
每一個(gè) Dio 實(shí)例都有一個(gè)請(qǐng)求攔截器 RequestInterceptor
和一個(gè)響應(yīng)攔截器 ResponseInterceptor
, 通過(guò)攔截器你可以在請(qǐng)求之前或響應(yīng)之后(但還沒(méi)有被 then
或 catchError
處理)做一些統(tǒng)一的預(yù)處理操作。
dio.interceptor.request.onSend = (Options options){
// 在請(qǐng)求被發(fā)送之前做一些事情
return options; //continue
// 如果你想完成請(qǐng)求并返回一些自定義數(shù)據(jù)姐叁,可以返回一個(gè)`Response`對(duì)象或返回`dio.resolve(data)`瓦盛。
// 這樣請(qǐng)求將會(huì)被終止,上層then會(huì)被調(diào)用外潜,then中返回的數(shù)據(jù)將是你的自定義數(shù)據(jù)data.
//
// 如果你想終止請(qǐng)求并觸發(fā)一個(gè)錯(cuò)誤,你可以返回一個(gè)`DioError`對(duì)象原环,或返回`dio.reject(errMsg)`,
// 這樣請(qǐng)求將被中止并觸發(fā)異常处窥,上層catchError會(huì)被調(diào)用嘱吗。
}
dio.interceptor.response.onSuccess = (Response response) {
// 在返回響應(yīng)數(shù)據(jù)之前做一些預(yù)處理
return response; // continue
};
dio.interceptor.response.onError = (DioError e){
// 當(dāng)請(qǐng)求失敗時(shí)做一些預(yù)處理
return DioError;//continue
}
如果你想移除攔截器,你可以將它們置為null:
dio.interceptor.request.onSend=null;
dio.interceptor.response.onSuccess=null;
dio.interceptor.response.onError=null;
完成和終止請(qǐng)求/響應(yīng)
在所有攔截器中碧库,你都可以改變請(qǐng)求執(zhí)行流柜与, 如果你想完成請(qǐng)求/響應(yīng)并返回自定義數(shù)據(jù),你可以返回一個(gè) Response
對(duì)象或返回 dio.resolve(data)
的結(jié)果嵌灰。 如果你想終止(觸發(fā)一個(gè)錯(cuò)誤弄匕,上層catchError
會(huì)被調(diào)用)一個(gè)請(qǐng)求/響應(yīng),那么可以返回一個(gè)DioError
對(duì)象或返回 dio.reject(errMsg)
的結(jié)果.
dio.interceptor.request.onSend = (Options options){
return dio.resolve("fake data")
}
Response response= await dio.get("/test");
print(response.data);//"fake data"
攔截器中支持異步任務(wù)
攔截器中不僅支持同步任務(wù)沽瞭,而且也支持異步任務(wù), 下面是在請(qǐng)求攔截器中發(fā)起異步任務(wù)的一個(gè)實(shí)例:
dio.interceptor.request.onSend = (Options options) async{
//...If no token, request token firstly.
Response response = await dio.get("/token");
//Set the token to headers
options.headers["token"] = response.data["data"]["token"];
return options; //continue
}
Lock/unlock 攔截器
你可以通過(guò)調(diào)用攔截器的 lock()
/unlock
方法來(lái)鎖定/解鎖攔截器迁匠。一旦請(qǐng)求/響應(yīng)攔截器被鎖定,接下來(lái)的請(qǐng)求/響應(yīng)將會(huì)在進(jìn)入請(qǐng)求/響應(yīng)攔截器之前排隊(duì)等待,直到解鎖后城丧,這些入隊(duì)的請(qǐng)求才會(huì)繼續(xù)執(zhí)行(進(jìn)入攔截器)延曙。這在一些需要串行化請(qǐng)求/響應(yīng)的場(chǎng)景中非常實(shí)用,后面我們將給出一個(gè)示例亡哄。
tokenDio=new Dio(); //Create a new instance to request the token.
tokenDio.options=dio;
dio.interceptor.request.onSend = (Options options) async{
// If no token, request token firstly and lock this interceptor
// to prevent other request enter this interceptor.
dio.interceptor.request.lock();
// We use a new Dio(to avoid dead lock) instance to request token.
Response response = await tokenDio.get("/token");
//Set the token to headers
options.headers["token"] = response.data["data"]["token"];
dio.interceptor.request.unlock()
return options; //continue
}
別名
當(dāng)請(qǐng)求攔截器被鎖定時(shí)枝缔,接下來(lái)的請(qǐng)求將會(huì)暫停,這等價(jià)于鎖住了dio實(shí)例蚊惯,因此愿卸,Dio示例上提供了請(qǐng)求攔截器lock/unlock
的別名方法:
dio.lock() == dio.interceptor.request.lock()
dio.unlock() == dio.interceptor.request.unlock()
示例
假設(shè)這么一個(gè)場(chǎng)景:出于安全原因,我們需要給所有的請(qǐng)求頭中添加一個(gè)csrfToken截型,如果csrfToken不存在,我們先去請(qǐng)求csrfToken,獲取到csrfToken后酝豪,再發(fā)起后續(xù)請(qǐng)求涩搓。 由于請(qǐng)求csrfToken的過(guò)程是異步的市咽,我們需要在請(qǐng)求過(guò)程中鎖定后續(xù)請(qǐng)求(因?yàn)樗鼈冃枰猚srfToken), 直到csrfToken請(qǐng)求成功后载萌,再解鎖,代碼如下:
dio.interceptor.request.onSend = (Options options) {
print('send request:path:${options.path},baseURL:${options.baseUrl}');
if (csrfToken == null) {
print("no token仰迁,request token firstly...");
//lock the dio.
dio.lock();
return tokenDio.get("/token").then((d) {
options.headers["csrfToken"] = csrfToken = d.data['data']['token'];
print("request token succeed, value: " + d.data['data']['token']);
print('continue to perform request:path:${options.path},baseURL:${options.path}');
return options;
}).whenComplete(() => dio.unlock()); // unlock the dio
} else {
options.headers["csrfToken"] = csrfToken;
return options;
}
};
完整的示例代碼請(qǐng)點(diǎn)擊 這里.
錯(cuò)誤處理
當(dāng)請(qǐng)求過(guò)程中發(fā)生錯(cuò)誤時(shí), Dio 會(huì)包裝 Error/Exception
為一個(gè) DioError
:
try {
//404
await dio.get("https://wendux.github.io/xsddddd");
} on DioError catch(e) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx and is also not 304.
if(e.response) {
print(e.response.data)
print(e.response.headers)
print(e.response.request)
} else{
// Something happened in setting up or sending the request that triggered an Error
print(e.request)
print(e.message)
}
}
DioError 字段
{
/// 響應(yīng)信息, 如果錯(cuò)誤發(fā)生在在服務(wù)器返回?cái)?shù)據(jù)之前悬秉,它為 `null`
Response response;
/// 錯(cuò)誤描述.
String message;
/// 錯(cuò)誤類型祠肥,見(jiàn)下文
DioErrorType type;
/// 錯(cuò)誤棧信息,可能為null
StackTrace stackTrace;
}
DioErrorType
enum DioErrorType {
/// Default error type, usually occurs before connecting the server.
DEFAULT,
/// When opening url timeout, it occurs.
CONNECT_TIMEOUT,
/// Whenever more than [receiveTimeout] (in milliseconds) passes between two events from response stream,
/// [Dio] will throw the [DioError] with [DioErrorType.RECEIVE_TIMEOUT].
///
/// Note: This is not the receiving time limitation.
RECEIVE_TIMEOUT,
/// When the server response, but with a incorrect status, such as 404, 503...
RESPONSE,
/// When the request is cancelled, dio will throw a error with this type.
CANCEL
}
使用application/x-www-form-urlencoded編碼
默認(rèn)情況下, Dio 會(huì)將請(qǐng)求數(shù)據(jù)(除過(guò)String類型)序列化為 JSON
. 如果想要以 application/x-www-form-urlencoded
格式編碼, 你可以顯式設(shè)置contentType
:
//Instance level
dio.options.contentType=ContentType.parse("application/x-www-form-urlencoded");
//or works once
dio.post("/info",data:{"id":5}, options: new Options(contentType:ContentType.parse("application/x-www-form-urlencoded")))
這里有一個(gè)示例.
FormData
Dio支持發(fā)送 FormData, 請(qǐng)求數(shù)據(jù)將會(huì)以 multipart/form-data
方式編碼, FormData中可以一個(gè)或多個(gè)包含文件 .
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
"file": new UploadFileInfo(new File("./example/upload.txt"), "upload.txt")
});
response = await dio.post("/info", data: formData)
注意: 只有 post 方法支持發(fā)送 FormData.
這里有一個(gè)完整的示例.
轉(zhuǎn)換器
轉(zhuǎn)換器TransFormer
用于對(duì)請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù)進(jìn)行編解碼處理权逗。Dio實(shí)現(xiàn)了一個(gè)默認(rèn)轉(zhuǎn)換器DefaultTransformer
作為默認(rèn)的 TransFormer
. 如果你想對(duì)請(qǐng)求/響應(yīng)數(shù)據(jù)進(jìn)行自定義編解碼處理,可以提供自定義轉(zhuǎn)換器胯陋,通過(guò) dio.transformer
設(shè)置。
請(qǐng)求轉(zhuǎn)換器
TransFormer.transformRequest(...)
只會(huì)被用于 'PUT'盟萨、 'POST'、 'PATCH'方法艇挨,因?yàn)橹挥羞@些方法才可以攜帶請(qǐng)求體(request body)泉瞻。但是響應(yīng)轉(zhuǎn)換器TransFormer.transformResponse()
會(huì)被用于所有請(qǐng)求方法的返回?cái)?shù)據(jù)侧巨。
執(zhí)行流
雖然在攔截器中也可以對(duì)數(shù)據(jù)進(jìn)行預(yù)處理,但是轉(zhuǎn)換器主要職責(zé)是對(duì)請(qǐng)求/響應(yīng)數(shù)據(jù)進(jìn)行編解碼,之所以將轉(zhuǎn)化器單獨(dú)分離,一是為了和攔截器解耦,二是為了不修改原始請(qǐng)求數(shù)據(jù)(如果你在攔截器中修改請(qǐng)求數(shù)據(jù)(options.data)傀履,會(huì)覆蓋原始請(qǐng)求數(shù)據(jù)碴犬,而在某些時(shí)候您可能需要原始請(qǐng)求數(shù)據(jù)). Dio的請(qǐng)求流是:
請(qǐng)求攔截器 >> 請(qǐng)求轉(zhuǎn)換器 >> 發(fā)起請(qǐng)求 >> 響應(yīng)轉(zhuǎn)換器 >> 響應(yīng)攔截器 >> 最終結(jié)果偿荷。
這是一個(gè)自定義轉(zhuǎn)換器的示例.
設(shè)置Http代理
Dio 是使用 HttpClient發(fā)起的http請(qǐng)求寺庄,所以你可以通過(guò)配置 httpClient
來(lái)支持代理艾蓝,示例如下:
dio.onHttpClientCreate = (HttpClient client) {
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY localhost:8888";
};
};
完整的示例請(qǐng)查看這里.
請(qǐng)求取消
你可以通過(guò) cancel token 來(lái)取消發(fā)起的請(qǐng)求:
CancelToken token = new CancelToken();
dio.get(url, cancelToken: token)
.catchError((DioError err){
if (CancelToken.isCancel(err)) {
print('Request canceled! '+ err.message)
}else{
// handle error.
}
})
// cancel the requests with "cancelled" message.
token.cancel("cancelled");
注意: 同一個(gè)cancel token 可以用于多個(gè)請(qǐng)求,當(dāng)一個(gè)cancel token取消時(shí)斗塘,所有使用該cancel token的請(qǐng)求都會(huì)被取消赢织。
完整的示例請(qǐng)參考取消示例.
Cookie管理
你可以通過(guò) cookieJar
來(lái)自動(dòng)管理請(qǐng)求/響應(yīng)cookie.
dio cookie 管理 API 是基于開(kāi)源庫(kù) cookie_jar.
你可以創(chuàng)建一個(gè)CookieJar
或 PersistCookieJar
來(lái)幫您自動(dòng)管理cookie, dio 默認(rèn)使用 CookieJar
, 它會(huì)將cookie保存在內(nèi)存中。 如果您想對(duì)cookie進(jìn)行持久化, 請(qǐng)使用 PersistCookieJar
, 示例代碼如下:
var dio = new Dio();
dio.cookieJar=new PersistCookieJar("./cookies");
PersistCookieJar
實(shí)現(xiàn)了RFC中標(biāo)準(zhǔn)的cookie策略. PersistCookieJar
會(huì)將cookie保存在文件中馍盟,所以 cookies 會(huì)一直存在除非顯式調(diào)用 delete
刪除.
更多關(guān)于 cookie_jar 請(qǐng)參考 : https://github.com/flutterchina/cookie_jar .
Copyright & License
此開(kāi)源項(xiàng)目為Flutter中文網(wǎng)(https://flutterchina.club) 授權(quán) 于置,license 是 MIT. 如果您喜歡,歡迎star.
Flutter中文網(wǎng)開(kāi)源項(xiàng)目計(jì)劃
開(kāi)發(fā)一系列Flutter SDK之外常用(實(shí)用)的Package朽合、插件俱两,豐富Flutter第三方庫(kù),為Flutter生態(tài)貢獻(xiàn)來(lái)自中國(guó)開(kāi)發(fā)者的力量曹步。所有項(xiàng)目將發(fā)布在 Github Flutter中文網(wǎng) Organization ,所有源碼貢獻(xiàn)者將加入到我們的Organization休讳,成為成員. 目前社區(qū)已有幾個(gè)開(kāi)源項(xiàng)目開(kāi)始公測(cè)讲婚,歡迎您加入開(kāi)發(fā)或測(cè)試,詳情請(qǐng)查看: Flutter中文網(wǎng)開(kāi)源項(xiàng)目俊柔。 如果您想加入到“開(kāi)源項(xiàng)目計(jì)劃”筹麸, 請(qǐng)發(fā)郵件到824783146@qq.com, 并附上自我介紹(個(gè)人基本信息+擅長(zhǎng)/關(guān)注技術(shù))雏婶。
Features and bugs
Please file feature requests and bugs at the issue tracker.