Flutter Http請求開源庫-dio

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

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

添加依賴

<code>dependencies: dio: ^x.x.x // latest version複製代碼/<code>

一個極簡的示例

<code>import 'package:dio/dio.dart';Dio dio = new Dio();Response response=await dio.get("https://www.google.com/");print(response.data);複製代碼/<code>

內容列表

示例Dio APIs請求配置響應數據攔截器錯誤處理使用application/x-www-form-urlencoded編碼FormData轉換器設置Http代理請求取消Cookie管理Features and bugs

示例

發起一個 GET 請求 :

<code>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());複製代碼/<code>

發起一個 POST 請求:

<code>response=await dio.post("/test",data:{"id":12,"name":"wendu"})複製代碼/<code>

發起多個併發請求:

<code>response= await Future.wait([dio.post("/info"),dio.get("/token")]);複製代碼/<code>

下載文件:

<code>response=await dio.download("https://www.google.com/","./xx.html")複製代碼/<code>

發送 FormData:

<code>FormData formData = new FormData.from({ "name": "wendux", "age": 25,});response = await dio.post("/info", data: formData)複製代碼/<code>

通過FormData上傳多個文件:

<code>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)複製代碼/<code>

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

Dio APIs

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

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

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

Dio實例的核心API是 :

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

<code>response=await request("/test", data: {"id":12,"name":"xx"}, new Options(method:"GET"));複製代碼/<code>

請求方法別名

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

<code>{ /// Http method. String method; /// 請求基地址,可以包含子路徑,如: "https://www.google.com/api/". String baseUrl; /// Http請求頭. Map<string> 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; /// 用戶自定義字段,可以在 [Interceptor]、[TransFormer] 和 [Response] 中取到. Map<string> extra;}複製代碼/<string>/<string>/<code>

這裡有一個完成的示例.

響應數據

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

<code>{ /// 響應數據,可能已經被轉換了類型, 詳情請參考Options中的[ResponseType]. var data; /// 響應頭 HttpHeaders headers; /// 本次請求信息 Options request; /// Http status code. int statusCode; /// 響應對象的自定義字段(可以在攔截器中設置它),調用方可以在`then`中獲取. Map<string> extra;}複製代碼/<string>/<code>

示例如下:

<code>Response response=await dio.get("https://www.google.com");print(response.data);print(response.headers);print(response.request);print(statusCode);複製代碼/<code>

攔截器

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

<code> 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 DioError;//continue } 複製代碼/<code>

如果你想移除攔截器,你可以將它們置為null:

<code>dio.interceptor.request.onSend=null;dio.interceptor.response.onSuccess=null;dio.interceptor.response.onError=null;複製代碼/<code>

完成和終止請求/響應

在所有攔截器中,你都可以改變請求執行流, 如果你想完成請求/響應並返回自定義數據,你可以返回一個 Response 對象或返回 dio.resolve(data)的結果。 如果你想終止(觸發一個錯誤,上層catchError會被調用)一個請求/響應,那麼可以返回一個DioError 對象或返回 dio.reject(errMsg) 的結果.

<code> dio.interceptor.request.onSend = (Options options){ return dio.resolve("fake data") } Response response= await dio.get("/test"); print(response.data);//"fake data"複製代碼/<code>

攔截器中支持異步任務

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

<code> 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 }複製代碼/<code>

Lock/unlock 攔截器

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

<code>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 }複製代碼/<code>

別名

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

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

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

示例

假設這麼一個場景:出於安全原因,我們需要給所有的請求頭中添加一個csrfToken,如果csrfToken不存在,我們先去請求csrfToken,獲取到csrfToken後,再發起後續請求。 由於請求csrfToken的過程是異步的,我們需要在請求過程中鎖定後續請求(因為它們需要csrfToken), 直到csrfToken請求成功後,再解鎖,代碼如下:

<code>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; } };複製代碼/<code>

錯誤處理

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

<code> 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) } }複製代碼/<code>

DioError 字段

<code> { /// 響應信息, 如果錯誤發生在在服務器返回數據之前,它為 `null` Response response; /// 錯誤描述. String message; /// 錯誤類型,見下文 DioErrorType type; /// 錯誤棧信息,可能為null StackTrace stackTrace;}複製代碼/<code>

DioErrorType

<code>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}複製代碼/<code>

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

默認情況下, Dio 會將請求數據(除過String類型)序列化為 JSON. 如果想要以 application/x-www-form-urlencoded格式編碼, 你可以顯式設置contentType :

<code>//Instance leveldio.options.contentType=ContentType.parse("application/x-www-form-urlencoded");//or works oncedio.post("/info",data:{"id":5}, options: new Options(contentType:ContentType.parse("application/x-www-form-urlencoded"))) 複製代碼/<code>

這裡有一個示例.

FormData

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

<code>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)複製代碼/<code>

注意: 只有 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來支持代理,示例如下:

<code> dio.onHttpClientCreate = (HttpClient client) { client.findProxy = (uri) { //proxy all request to localhost:8888 return "PROXY localhost:8888"; }; };複製代碼/<code>

完整的示例請查看這裡.

請求取消

你可以通過 cancel token 來取消發起的請求:

<code>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");複製代碼/<code>

注意: 同一個cancel token 可以用於多個請求,當一個cancel token取消時,所有使用該cancel token的請求都會被取消。

完整的示例請參考取消示例.

Cookie管理

你可以通過 cookieJar 來自動管理請求/響應cookie.

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

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

<code>var dio = new Dio();dio.cookieJar=new PersistCookieJar("./cookies");複製代碼/<code>

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