flutter之庫Dio

Flutter http請求庫dio穩定版發布

http://www.lxweimin.com/p/1352351c7d08

2018.08.02 08:05:02字數 2,507閱讀 43,166

dioFlutter中文網開源的一個強大的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穩定版不會這么快發布。

文檔語言:?English?|?中文簡體

dio

dio是一個強大的Dart Http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載、超時等...

添加依賴

dependencies:dio:^x.x.x? // 請使用pub上的最新版本

一個極簡的示例

import'package:dio/dio.dart';Dio dio=newDio();Response response=awaitdio.get("https://www.google.com/");print(response.data);

最近更新

文件上傳支持數組。

支持http狀態碼被認為對應成功或失敗的自定義判斷。

添加清空攔截器隊列API?Clear()。

內容列表

示例

Dio APIs

請求配置

響應數據

攔截器

錯誤處理

使用application/x-www-form-urlencoded編碼

FormData

轉換器

設置Http代理

請求取消

Cookie管理

Features and bugs

示例

發起一個?GET?請求 :

Response response;response=awaitdio.get("/test?id=12&name=wendu")print(response.data.toString());// 請求參數也可以通過對象傳遞,上面的代碼等同于:response=awaitdio.get("/test",data:{"id":12,"name":"wendu"})print(response.data.toString());

發起一個?POST?請求:

response=awaitdio.post("/test",data:{"id":12,"name":"wendu"})

發起多個并發請求:

response=awaitFuture.wait([dio.post("/info"),dio.get("/token")]);

下載文件:

response=awaitdio.download("https://www.google.com/","./xx.html")

發送 FormData:

FormData formData=newFormData.from({"name":"wendux","age":25,});response=awaitdio.post("/info",data:formData)

通過FormData上傳多個文件:

FormData formData=newFormData.from({"name":"wendux","age":25,"file1":newUploadFileInfo(newFile("./upload.txt"),"upload1.txt"),"file2":newUploadFileInfo(newFile("./upload.txt"),"upload2.txt"),// 支持文件數組上傳"files":[newUploadFileInfo(newFile("./example/upload.txt"),"upload.txt"),newUploadFileInfo(newFile("./example/upload.txt"),"upload.txt")]});response=awaitdio.post("/info",data:formData)

…你可以在這里獲取所有示例代碼.

Dio APIs

創建一個Dio實例,并配置它

你可以使用默認配置或傳遞一個可選?Options參數來創建一個Dio實例 :

Dio dio=newDio;// 使用默認配置// 配置dio實例dio.options.baseUrl="https://www.xx.com/api"dio.options.connectTimeout=5000;//5sdio.options.receiveTimeout=3000;// 或者通過傳遞一個 `options`來創建dio實例Options options=newOptions(baseUrl:"https://www.xx.com/api",connectTimeout:5000,receiveTimeout:3000);Dio dio=newDio(options);

Dio實例的核心API是 :

Future<Response> request(String path, {data, Options options,CancelToken cancelToken})

response=awaitrequest("/test",data:{"id":12,"name":"xx"},newOptions(method:"GET"));

請求方法別名

為了方便使用,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})

請求配置

下面是所有的請求配置選項。 如果請求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){

????// 當請求失敗時做一些預處理returne;//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?方法來鎖定/解鎖攔截器。一旦請求/響應攔截器被鎖定,接下來的請求/響應將會在進入請求/響應攔截器之前排隊等待,直到解鎖后,這些入隊的請求才會繼續執行(進入攔截器)。這在一些需要串行化請求/響應的場景中非常實用,后面我們將給出一個示例。

token Dio=newDio();

//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=awaittokenDio.get("/token");

//Set the token to headers

????options.headers["token"]=response.data["data"]["token"];

????dio.interceptor.request.unlock()

returnoptions;//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 token

Dio.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 DioErrorcatch(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 leveldio.options.contentType=ContentType.parse("application/x-www-form-urlencoded");//or works oncedio.post("/info",data:{"id":5},options:newOptions(contentType:ContentType.parse("application/x-www-form-urlencoded")))

這里有一個示例.

FormData

Dio支持發送 FormData, 請求數據將會以?multipart/form-data方式編碼, FormData中可以一個或多個包含文件 .

FormData formData=newFormData.from({"name":"wendux","age":25,"file":newUploadFileInfo(newFile("./example/upload.txt"),"upload.txt")});response=awaitdio.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:8888return"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?, 示例代碼如下:

vardio=newDio();dio.cookieJar=newPersistCookieJar("./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.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容