dio是Flutter中文網開源的一個強大的Dart Http請求庫,支援Restful API、FormData、攔截器、請求取消、Cookie管理、檔案上傳/下載、超時等...
自dio開源至今,收到了大量國內外開發者的反饋,到目前為止,dio在pub倉庫得分96分,github dart語言下開源專案排名已上升到前20,dio現在也是flutter第三方package中star數最多的。在dio開源的兩個月中,已迭代了18個小版本,國內外有多家公司的Flutter APP正在使用dio,已通過了大量的實戰驗證,已經在AppStore上架的APP典型代表是gitme:
Gitme是一個強大的github客戶端APP,它使用dio作為http client,除了正常的http請求之外,有一個最大的特點是gitme通過dio攔截器,實現了APP內統一、隔離的快取層,完全和上層ui解耦。您可以下載體驗一下Gitme。如有必要,我會單獨出一篇文章詳細介紹一下如何使用dio攔截器實現離線快取,大家如果有興趣可以在評論中反饋。
所以,今天,我們正式釋出dio的第一個穩定版本1.0。下面,我們全面的介紹一下dio 1.0的功能及使用。
建議dio的老使用者都升級到1.0正式版,並同時感謝你們在dio專案初期的支援,沒有你們的反饋與建議,dio穩定版不會這麼快釋出。
dio
dio是一個強大的Dart Http請求庫,支援Restful API、FormData、攔截器、請求取消、Cookie管理、檔案上傳/下載、超時等...
新增依賴
dependencies:
dio: ^x.x.x // 請使用pub上的最新版本
複製程式碼
一個極簡的示例
import 'package:dio/dio.dart';
Dio dio = new Dio();
Response response=await dio.get("https://www.google.com/");
print(response.data);
複製程式碼
最近更新
- 檔案上傳支援陣列。
- 支援http狀態碼被認為對應成功或失敗的自定義判斷。
- 新增清空攔截器佇列API
Clear()
。
內容列表
示例
發起一個 GET
請求 :
Response response;
response=await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
// 請求引數也可以通過物件傳遞,上面的程式碼等同於:
response=await dio.get("/test",data:{"id":12,"name":"wendu"})
print(response.data.toString());
複製程式碼
發起一個 POST
請求:
response=await dio.post("/test",data:{"id":12,"name":"wendu"})
複製程式碼
發起多個併發請求:
response= await Future.wait([dio.post("/info"),dio.get("/token")]);
複製程式碼
下載檔案:
response=await dio.download("https://www.google.com/","./xx.html")
複製程式碼
傳送 FormData:
FormData formData = new FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData)
複製程式碼
通過FormData上傳多個檔案:
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"),
// 支援檔案陣列上傳
"files": [
new UploadFileInfo(new File("./example/upload.txt"), "upload.txt"),
new UploadFileInfo(new File("./example/upload.txt"), "upload.txt")
]
});
response = await dio.post("/info", data: formData)
複製程式碼
…你可以在這裡獲取所有示例程式碼.
Dio APIs
建立一個Dio例項,並配置它
你可以使用預設配置或傳遞一個可選 Options
引數來建立一個Dio例項 :
Dio dio = new Dio; // 使用預設配置
// 配置dio例項
dio.options.baseUrl="https://www.xx.com/api"
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout=3000;
// 或者通過傳遞一個 `options`來建立dio例項
Options options= new Options(
baseUrl:"https://www.xx.com/api",
connectTimeout:5000,
receiveTimeout:3000
);
Dio dio = new Dio(options);
複製程式碼
Dio例項的核心API是 :
Future request(String path, {data, Options options,CancelToken cancelToken})
response=await request("/test", data: {"id":12,"name":"xx"}, new Options(method:"GET"));
複製程式碼
請求方法別名
為了方便使用,Dio提供了一些其它的Restful API, 這些API都是request
的別名。
Future get(path, {data, Options options,CancelToken cancelToken})
Future post(path, {data, Options options,CancelToken cancelToken})
Future put(path, {data, Options options,CancelToken cancelToken})
Future delete(path, {data, Options options,CancelToken cancelToken})
Future head(path, {data, Options options,CancelToken cancelToken})
Future put(path, {data, Options options,CancelToken cancelToken})
Future path(path, {data, Options options,CancelToken cancelToken})
Future download(String urlPath, savePath, {OnDownloadProgress onProgress, data, bool flush: false, Options options,CancelToken cancelToken})
請求配置
下面是所有的請求配置選項。 如果請求method
沒有指定,則預設為GET
:
{
/// Http method.
String method;
/// 請求基地址,可以包含子路徑,如: "https://www.google.com/api/".
String baseUrl;
/// Http請求頭.
Map<String, dynamic> headers;
/// 連線伺服器超時時間,單位是毫秒.
int connectTimeout;
/// 響應流上前後兩次接受到資料的間隔,單位為毫秒。如果兩次間隔超過[receiveTimeout],
/// [Dio] 將會丟擲一個[DioErrorType.RECEIVE_TIMEOUT]的異常.
/// 注意: 這並不是接收資料的總時限.
int receiveTimeout;
/// 請求資料,可以是任意型別.
var data;
/// 請求路徑,如果 `path` 以 "http(s)"開始, 則 `baseURL` 會被忽略; 否則,
/// 將會和baseUrl拼接出完整的的url.
String path="";
/// 請求的Content-Type,預設值是[ContentType.JSON].
/// 如果您想以"application/x-www-form-urlencoded"格式編碼請求資料,
/// 可以設定此選項為 `ContentType.parse("application/x-www-form-urlencoded")`, 這樣[Dio]
/// 就會自動編碼請求體.
ContentType contentType;
/// [responseType] 表示期望以那種格式(方式)接受響應資料。
/// 目前 [ResponseType] 接受三種型別 `JSON`, `STREAM`, `PLAIN`.
///
/// 預設值是 `JSON`, 當響應頭中content-type為"application/json"時,dio 會自動將響應內容轉化為json物件。
/// 如果想以二進位制方式接受響應資料,如下載一個二進位制檔案,那麼可以使用 `STREAM`.
///
/// 如果想以文字(字串)格式接收響應資料,請使用 `PLAIN`.
ResponseType responseType;
/// `validateStatus` 決定http響應狀態碼是否被dio視為請求成功, 返回`validateStatus`
/// 返回`true` , 請求結果就會按成功處理,否則會按失敗處理.
ValidateStatus validateStatus;
/// 使用者自定義欄位,可以在 [Interceptor]、[Transformer] 和 [Response] 中取到.
Map<String, dynamic> extra;
}
複製程式碼
這裡有一個完成的示例.
響應資料
當請求成功時會返回一個Response物件,它包含如下欄位:
{
/// 響應資料,可能已經被轉換了型別, 詳情請參考Options中的[ResponseType].
var data;
/// 響應頭
HttpHeaders headers;
/// 本次請求資訊
Options request;
/// Http status code.
int statusCode;
/// 響應物件的自定義欄位(可以在攔截器中設定它),呼叫方可以在`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);
複製程式碼
攔截器
每一個 Dio 例項都有一個請求攔截器 RequestInterceptor
和一個響應攔截器 ResponseInterceptor
, 通過攔截器你可以在請求之前或響應之後(但還沒有被 then
或 catchError
處理)做一些統一的預處理操作。
dio.interceptor.request.onSend = (Options options){
// 在請求被髮送之前做一些事情
return options; //continue
// 如果你想完成請求並返回一些自定義資料,可以返回一個`Response`物件或返回`dio.resolve(data)`。
// 這樣請求將會被終止,上層then會被呼叫,then中返回的資料將是你的自定義資料data.
//
// 如果你想終止請求並觸發一個錯誤,你可以返回一個`DioError`物件,或返回`dio.reject(errMsg)`,
// 這樣請求將被中止並觸發異常,上層catchError會被呼叫。
}
dio.interceptor.response.onSuccess = (Response response) {
// 在返回響應資料之前做一些預處理
return response; // continue
};
dio.interceptor.response.onError = (DioError e){
// 當請求失敗時做一些預處理
return e;//continue
}
複製程式碼
如果你想移除攔截器,你可以將它們置為null:
dio.interceptor.request.onSend=null;
dio.interceptor.response.onSuccess=null;
dio.interceptor.response.onError=null;
複製程式碼
完成和終止請求/響應
在所有攔截器中,你都可以改變請求執行流, 如果你想完成請求/響應並返回自定義資料,你可以返回一個 Response
物件或返回 dio.resolve(data)
的結果。 如果你想終止(觸發一個錯誤,上層catchError
會被呼叫)一個請求/響應,那麼可以返回一個DioError
物件或返回 dio.reject(errMsg)
的結果.
dio.interceptor.request.onSend = (Options options){
return dio.resolve("fake data")
}
Response response= await dio.get("/test");
print(response.data);//"fake data"
複製程式碼
攔截器中支援非同步任務
攔截器中不僅支援同步任務,而且也支援非同步任務, 下面是在請求攔截器中發起非同步任務的一個例項:
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 攔截器
你可以通過呼叫攔截器的 lock()
/unlock
方法來鎖定/解鎖攔截器。一旦請求/響應攔截器被鎖定,接下來的請求/響應將會在進入請求/響應攔截器之前排隊等待,直到解鎖後,這些入隊的請求才會繼續執行(進入攔截器)。這在一些需要序列化請求/響應的場景中非常實用,後面我們將給出一個示例。
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
}
複製程式碼
Clear()
你也可以呼叫攔截器的clear()
方法來清空等待佇列。
別名
當請求攔截器被鎖定時,接下來的請求將會暫停,這等價於鎖住了dio例項,因此,Dio示例上提供了請求攔截器lock/unlock
的別名方法:
dio.lock() == dio.interceptor.request.lock()
dio.unlock() == dio.interceptor.request.unlock()
dio.clear() == dio.interceptor.request.clear()
示例
假設這麼一個場景:出於安全原因,我們需要給所有的請求頭中新增一個csrfToken,如果csrfToken不存在,我們先去請求csrfToken,獲取到csrfToken後,再發起後續請求。 由於請求csrfToken的過程是非同步的,我們需要在請求過程中鎖定後續請求(因為它們需要csrfToken), 直到csrfToken請求成功後,再解鎖,程式碼如下:
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;
}
};
複製程式碼
完整的示例程式碼請點選 這裡.
錯誤處理
當請求過程中發生錯誤時, Dio 會包裝 Error/Exception
為一個 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 欄位
{
/// 響應資訊, 如果錯誤發生在在伺服器返回資料之前,它為 `null`
Response response;
/// 錯誤描述.
String message;
/// 錯誤型別,見下文
DioErrorType type;
/// 錯誤棧資訊,可能為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編碼
預設情況下, Dio 會將請求資料(除過String型別)序列化為 JSON
. 如果想要以 application/x-www-form-urlencoded
格式編碼, 你可以顯式設定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")))
複製程式碼
這裡有一個示例.
FormData
Dio支援傳送 FormData, 請求資料將會以 multipart/form-data
方式編碼, FormData中可以一個或多個包含檔案 .
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 方法支援傳送 FormData.
這裡有一個完整的示例.
轉換器
轉換器Transformer
用於對請求資料和響應資料進行編解碼處理。Dio實現了一個預設轉換器DefaultTransformer
作為預設的 Transformer
. 如果你想對請求/響應資料進行自定義編解碼處理,可以提供自定義轉換器,通過 dio.transformer
設定。
請求轉換器
Transformer.transformRequest(...)
只會被用於 'PUT'、 'POST'、 'PATCH'方法,因為只有這些方法才可以攜帶請求體(request body)。但是響應轉換器Transformer.transformResponse()
會被用於所有請求方法的返回資料。
執行流
雖然在攔截器中也可以對資料進行預處理,但是轉換器主要職責是對請求/響應資料進行編解碼,之所以將轉化器單獨分離,一是為了和攔截器解耦,二是為了不修改原始請求資料(如果你在攔截器中修改請求資料(options.data),會覆蓋原始請求資料,而在某些時候您可能需要原始請求資料). Dio的請求流是:
請求攔截器 >> 請求轉換器 >> 發起請求 >> 響應轉換器 >> 響應攔截器 >> 最終結果。
這是一個自定義轉換器的示例.
設定Http代理
Dio 是使用 HttpClient發起的http請求,所以你可以通過配置 httpClient
來支援代理,示例如下:
dio.onHttpClientCreate = (HttpClient client) {
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY localhost:8888";
};
// 你也可以自己建立一個新的HttpClient例項返回。
// return new HttpClient(SecurityContext);
};
複製程式碼
完整的示例請檢視這裡.
請求取消
你可以通過 cancel token 來取消發起的請求:
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");
複製程式碼
注意: 同一個cancel token 可以用於多個請求,當一個cancel token取消時,所有使用該cancel token的請求都會被取消。
完整的示例請參考取消示例.
Cookie管理
你可以通過 cookieJar
來自動管理請求/響應cookie.
dio cookie 管理 API 是基於開源庫 cookie_jar.
你可以建立一個CookieJar
或 PersistCookieJar
來幫您自動管理cookie, dio 預設使用 CookieJar
, 它會將cookie儲存在記憶體中。 如果您想對cookie進行持久化, 請使用 PersistCookieJar
, 示例程式碼如下:
var dio = new Dio();
dio.cookieJar=new PersistCookieJar("./cookies");
複製程式碼
PersistCookieJar
實現了RFC中標準的cookie策略. PersistCookieJar
會將cookie儲存在檔案中,所以 cookies 會一直存在除非顯式呼叫 delete
刪除.
更多關於 cookie_jar 請參考 : https://github.com/flutterchina/cookie_jar .
Copyright & License
此開源專案為Flutter中文網(https://flutterchina.club) 授權 ,license 是 MIT. 如果您喜歡,歡迎star.
Flutter中文網開源專案計劃
開發一系列Flutter SDK之外常用(實用)的Package、外掛,豐富Flutter第三方庫,為Flutter生態貢獻來自中國開發者的力量。所有專案將釋出在 Github Flutter中文網 Organization ,所有原始碼貢獻者將加入到我們的Organization,成為成員. 目前社群已有幾個開源專案開始公測,歡迎您加入開發或測試,詳情請檢視: Flutter中文網開源專案。 如果您想加入到“開源專案計劃”, 請發郵件到824783146@qq.com, 並附上自我介紹(個人基本資訊+擅長/關注技術)。
Features and bugs
Please file feature requests and bugs at the issue tracker.