Flutter http請求庫dio

轉(zhuǎn)載自:https://segmentfault.com/a/1190000015853959

dioFlutter中文網(wǎng)開源的一個強大的Dart Http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載、超時等...

dio開源至今,收到了大量國內(nèi)外開發(fā)者的反饋,到目前為止,dio在pub倉庫得分96分,github dart語言下開源項目排名已上升到前20,dio現(xiàn)在也是flutter第三方package中star數(shù)最多的。在dio開源的兩個月中,已迭代了18個小版本,國內(nèi)外有多家公司的Flutter APP正在使用dio,已通過了大量的實戰(zhàn)驗證,已經(jīng)在AppStore上架的APP典型代表是gitme

Gitme是一個強大的github客戶端APP,它使用dio作為http client,除了正常的http請求之外,有一個最大的特點是gitme通過dio攔截器,實現(xiàn)了APP內(nèi)統(tǒng)一、隔離的緩存層,完全和上層ui解耦。您可以下載體驗一下Gitme。如有必要,我會單獨出一篇文章詳細介紹一下如何使用dio攔截器實現(xiàn)離線緩存,大家如果有興趣可以在評論中反饋。

所以,今天,我們正式發(fā)布dio的第一個穩(wěn)定版本1.0。下面,我們?nèi)娴慕榻B一下dio 1.0的功能及使用。

建議dio的老用戶都升級到1.0正式版,并同時感謝你們在dio項目初期的支持,沒有你們的反饋與建議,dio穩(wěn)定版不會這么快發(fā)布。

文檔語言:?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);

最近更新

文件上傳支持數(shù)組。

支持http狀態(tài)碼被認為對應(yīng)成功或失敗的自定義判斷。

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

內(nèi)容列表

示例

Dio APIs

請求配置

響應(yīng)數(shù)據(jù)

攔截器

錯誤處理

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

FormData

轉(zhuǎn)換器

設(shè)置Http代理

請求取消

Cookie管理

Features and bugs

示例

發(fā)起一個?GET?請求 :

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

發(fā)起一個?POST?請求:

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

發(fā)起多個并發(fā)請求:

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

下載文件:

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

發(fā)送 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"),// 支持文件數(shù)組上傳"files": [newUploadFileInfo(newFile("./example/upload.txt"),"upload.txt"),newUploadFileInfo(newFile("./example/upload.txt"),"upload.txt")? ? ]});response =awaitdio.post("/info", data: formData)

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

Dio APIs

創(chuàng)建一個Dio實例,并配置它

你可以使用默認配置或傳遞一個可選?Options參數(shù)來創(chuàng)建一個Dio實例 :

Dio dio =newDio;// 使用默認配置// 配置dio實例dio.options.baseUrl="https://www.xx.com/api"dio.options.connectTimeout =5000;//5sdio.options.receiveTimeout=3000;// 或者通過傳遞一個 `options`來創(chuàng)建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=await?request("/test", data: {"id":12,"name":"xx"},?new?Options(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,boolflush:false, Options options,CancelToken cancelToken})**

請求配置

下面是所有的請求配置選項。 如果請求method沒有指定,則默認為GET?:

{/// Http method.Stringmethod;/// 請求基地址,可以包含子路徑,如: "https://www.google.com/api/".StringbaseUrl;/// Http請求頭.Map headers;/// 連接服務(wù)器超時時間,單位是毫秒.intconnectTimeout;///? 響應(yīng)流上前后兩次接受到數(shù)據(jù)的間隔,單位為毫秒。如果兩次間隔超過[receiveTimeout],///? [Dio] 將會拋出一個[DioErrorType.RECEIVE_TIMEOUT]的異常.///? 注意: 這并不是接收數(shù)據(jù)的總時限.intreceiveTimeout;/// 請求數(shù)據(jù),可以是任意類型.vardata;/// 請求路徑,如果`path`以 "http(s)"開始, 則`baseURL`會被忽略; 否則,/// 將會和baseUrl拼接出完整的的url.Stringpath="";/// 請求的Content-Type,默認值是[ContentType.JSON]./// 如果您想以"application/x-www-form-urlencoded"格式編碼請求數(shù)據(jù),/// 可以設(shè)置此選項為`ContentType.parse("application/x-www-form-urlencoded")`,? 這樣[Dio]/// 就會自動編碼請求體.ContentType contentType;/// [responseType] 表示期望以那種格式(方式)接受響應(yīng)數(shù)據(jù)。/// 目前 [ResponseType] 接受三種類型`JSON`,`STREAM`,`PLAIN`.////// 默認值是`JSON`, 當響應(yīng)頭中content-type為"application/json"時,dio 會自動將響應(yīng)內(nèi)容轉(zhuǎn)化為json對象。/// 如果想以二進制方式接受響應(yīng)數(shù)據(jù),如下載一個二進制文件,那么可以使用`STREAM`.////// 如果想以文本(字符串)格式接收響應(yīng)數(shù)據(jù),請使用`PLAIN`.ResponseType responseType;///`validateStatus`決定http響應(yīng)狀態(tài)碼是否被dio視為請求成功, 返回`validateStatus`///? 返回`true`, 請求結(jié)果就會按成功處理,否則會按失敗處理.ValidateStatus validateStatus;/// 用戶自定義字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中取到.Map extra;}

這里有一個完成的示例.

響應(yīng)數(shù)據(jù)

當請求成功時會返回一個Response對象,它包含如下字段:

{/// 響應(yīng)數(shù)據(jù),可能已經(jīng)被轉(zhuǎn)換了類型, 詳情請參考Options中的[ResponseType].vardata;/// 響應(yīng)頭HttpHeaders headers;/// 本次請求信息Options request;/// Http status code.intstatusCode;/// 響應(yīng)對象的自定義字段(可以在攔截器中設(shè)置它),調(diào)用方可以在`then`中獲取.Map extra;}

示例如下:

Response response=awaitdio.get("https://www.google.com");print(response.data);print(response.headers);print(response.request);print(statusCode);

攔截器

每一個 Dio 實例都有一個請求攔截器?RequestInterceptor?和一個響應(yīng)攔截器?ResponseInterceptor, 通過攔截器你可以在請求之前或響應(yīng)之后(但還沒有被?then?或?catchError處理)做一些統(tǒng)一的預處理操作。

dio.interceptor.request.onSend = (Options options){// 在請求被發(fā)送之前做一些事情returnoptions;//continue// 如果你想完成請求并返回一些自定義數(shù)據(jù),可以返回一個`Response`對象或返回`dio.resolve(data)`。// 這樣請求將會被終止,上層then會被調(diào)用,then中返回的數(shù)據(jù)將是你的自定義數(shù)據(jù)data.//// 如果你想終止請求并觸發(fā)一個錯誤,你可以返回一個`DioError`對象,或返回`dio.reject(errMsg)`,// 這樣請求將被中止并觸發(fā)異常,上層catchError會被調(diào)用。} dio.interceptor.response.onSuccess = (Response response) {// 在返回響應(yīng)數(shù)據(jù)之前做一些預處理returnresponse;// 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;

完成和終止請求/響應(yīng)

在所有攔截器中,你都可以改變請求執(zhí)行流, 如果你想完成請求/響應(yīng)并返回自定義數(shù)據(jù),你可以返回一個?Response?對象或返回?dio.resolve(data)的結(jié)果。 如果你想終止(觸發(fā)一個錯誤,上層catchError會被調(diào)用)一個請求/響應(yīng),那么可以返回一個DioError?對象或返回?dio.reject(errMsg)?的結(jié)果.

dio.interceptor.request.onSend = (Options options){returndio.resolve("fake data") } Response response=awaitdio.get("/test");print(response.data);//"fake data"

攔截器中支持異步任務(wù)

攔截器中不僅支持同步任務(wù),而且也支持異步任務(wù), 下面是在請求攔截器中發(fā)起異步任務(wù)的一個實例:

dio.interceptor.request.onSend = (Options options)async{//...If no token, request token firstly.Response response =awaitdio.get("/token");//Set the token to headersoptions.headers["token"] = response.data["data"]["token"];returnoptions;//continue}

Lock/unlock 攔截器

你可以通過調(diào)用攔截器的?lock()/unlock?方法來鎖定/解鎖攔截器。一旦請求/響應(yīng)攔截器被鎖定,接下來的請求/響應(yīng)將會在進入請求/響應(yīng)攔截器之前排隊等待,直到解鎖后,這些入隊的請求才會繼續(xù)執(zhí)行(進入攔截器)。這在一些需要串行化請求/響應(yīng)的場景中非常實用,后面我們將給出一個示例。

tokenDio=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 headersoptions.headers["token"] = response.data["data"]["token"];? ? dio.interceptor.request.unlock()returnoptions;//continue}

Clear()

你也可以調(diào)用攔截器的clear()方法來清空等待隊列。

別名

請求攔截器被鎖定時,接下來的請求將會暫停,這等價于鎖住了dio實例,因此,Dio示例上提供了請求攔截器lock/unlock的別名方法:

dio.lock() == dio.interceptor.request.lock()

dio.unlock() == dio.interceptor.request.unlock()

dio.clear() == dio.interceptor.request.clear()

示例

假設(shè)這么一個場景:出于安全原因,我們需要給所有的請求頭中添加一個csrfToken,如果csrfToken不存在,我們先去請求csrfToken,獲取到csrfToken后,再發(fā)起后續(xù)請求。 由于請求csrfToken的過程是異步的,我們需要在請求過程中鎖定后續(xù)請求(因為它們需要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();returntokenDio.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}');returnoptions;? ? ? }).whenComplete(() => dio.unlock());// unlock the dio}else{? ? ? options.headers["csrfToken"] = csrfToken;returnoptions;? ? }? };

完整的示例代碼請點擊?這里.

錯誤處理

當請求過程中發(fā)生錯誤時, Dio 會包裝?Error/Exception?為一個?DioError:

try{//404awaitdio.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 Errorprint(e.request)print(e.message)? ? ? }? }

DioError 字段

{/// 響應(yīng)信息, 如果錯誤發(fā)生在在服務(wù)器返回數(shù)據(jù)之前,它為`null`Response response;/// 錯誤描述.Stringmessage;/// 錯誤類型,見下文DioErrorType type;/// 錯誤棧信息,可能為nullStackTrace stackTrace;}

DioErrorType

enumDioErrorType {/// 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 會將請求數(shù)據(jù)(除過String類型)序列化為?JSON. 如果想要以?application/x-www-form-urlencoded格式編碼, 你可以顯式設(shè)置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支持發(fā)送 FormData, 請求數(shù)據(jù)將會以?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 方法支持發(fā)送 FormData.

這里有一個完整的示例.

轉(zhuǎn)換器

轉(zhuǎn)換器Transformer?用于對請求數(shù)據(jù)和響應(yīng)數(shù)據(jù)進行編解碼處理。Dio實現(xiàn)了一個默認轉(zhuǎn)換器DefaultTransformer作為默認的?Transformer. 如果你想對請求/響應(yīng)數(shù)據(jù)進行自定義編解碼處理,可以提供自定義轉(zhuǎn)換器,通過?dio.transformer設(shè)置。

請求轉(zhuǎn)換器?Transformer.transformRequest(...)?只會被用于 'PUT'、 'POST'、 'PATCH'方法,因為只有這些方法才可以攜帶請求體(request body)。但是響應(yīng)轉(zhuǎn)換器?Transformer.transformResponse()?會被用于所有請求方法的返回數(shù)據(jù)。

執(zhí)行流

雖然在攔截器中也可以對數(shù)據(jù)進行預處理,但是轉(zhuǎn)換器主要職責是對請求/響應(yīng)數(shù)據(jù)進行編解碼,之所以將轉(zhuǎn)化器單獨分離,一是為了和攔截器解耦,二是為了不修改原始請求數(shù)據(jù)(如果你在攔截器中修改請求數(shù)據(jù)(options.data),會覆蓋原始請求數(shù)據(jù),而在某些時候您可能需要原始請求數(shù)據(jù)). Dio的請求流是:

請求攔截器?>>?請求轉(zhuǎn)換器?>>?發(fā)起請求?>>?響應(yīng)轉(zhuǎn)換器?>>?響應(yīng)攔截器?>>?最終結(jié)果。

這是一個自定義轉(zhuǎn)換器的示例.

設(shè)置Http代理

Dio 是使用 HttpClient發(fā)起的http請求,所以你可以通過配置?httpClient來支持代理,示例如下:

dio.onHttpClientCreate = (HttpClient client) {? ? client.findProxy = (uri) {//proxy all request to localhost:8888return"PROXY localhost:8888";? ? };// 你也可以自己創(chuàng)建一個新的HttpClient實例返回。// return new HttpClient(SecurityContext);};

完整的示例請查看這里.

請求取消

你可以通過?cancel token?來取消發(fā)起的請求:

CancelToken token =newCancelToken();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?來自動管理請求/響應(yīng)cookie.

dio cookie 管理 API 是基于開源庫?cookie_jar.

你可以創(chuàng)建一個CookieJar?或?PersistCookieJar?來幫您自動管理cookie, dio 默認使用?CookieJar?, 它會將cookie保存在內(nèi)存中。 如果您想對cookie進行持久化, 請使用?PersistCookieJar?, 示例代碼如下:

vardio =newDio();dio.cookieJar=newPersistCookieJar("./cookies");

PersistCookieJar?實現(xiàn)了RFC中標準的cookie策略.?PersistCookieJar?會將cookie保存在文件中,所以 cookies 會一直存在除非顯式調(diào)用?delete?刪除.

更多關(guān)于?cookie_jar?請參考 :?https://github.com/flutterchi...?.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容