Flutter Http請求開源庫-dio

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

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

dio

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

添加依賴

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

一個極簡的示例

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

內容列表

示例

發起一個 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")
});
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;

  /// 用戶自定義字段,能夠在 [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, 經過攔截器你能夠在請求以前或響應以後(但尚未被 thencatchError處理)作一些統一的預處理操做。

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

若是你想移除攔截器,你能夠將它們置爲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 
 }
複製代碼

別名

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

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

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

示例

假設這麼一個場景:出於安全緣由,咱們須要給全部的請求頭中添加一個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";
    };
  };
複製代碼

完整的示例請查看這裏.

請求取消

你能夠經過 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.

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

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

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

更多關於 cookie_jar 請參考 : github.com/flutterchin… .

Copyright & License

此開源項目爲Flutter中文網(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.

相關文章
相關標籤/搜索