强大的Flutter Http请求开源库-dio

DIO是扑网中文English开源的一个强大的飞镖的Http请求库,支持RESTful API,因此FORMDATA,拦截器,请求取消,饼干管理,文件上传/下载,超时等...

文档语言:English | 中文简体

DIO

图片

一个强大的Dart Http请求库,支持Restful API,FormData,拦截器,请求取消,Cookie管理,文件上传/下载,超时等...

添加依赖

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 API

创建一个迪欧实例,并配置它

你可以使用默认配置或传递一个可选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 <Response>请求(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>路径(路径,{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;

  /// 用户自定义字段,可以在 [Interceptor]、[TransFormer] 和 [Response] 中取到.
  Map<String, dynamic> extra;
}

有这里一个完成的示例

响应数据

当请求成功时会返回一个响应对象,它包含如下字段:

{
  /// 响应数据,可能已经被转换了类型, 详情请参考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
 }    

如果你想移除拦截器,你可以将它们置为空:

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方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。

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
}

使用应用程序/ x-WWW窗体-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)

注意:只有帖子方法支持发送FormData。

有这里一个完整的示例

转换器

器转换TransFormer用于对请求数据状语从句:响应数据进行关系编解码处理.Dio了实现默认一个转换器DefaultTransformer作为默认的TransFormer。如果你想对请求/响应数据进行自定义编解码处理,可以提供自定义转换器,通过dio.transformer设置。

请求转换器 TransFormer.transformRequest(...) 只会被用于'PUT','POST','PATCH'方法,因为只有这些方法才可以携带请求体(request body)。但是响应转换器TransFormer.transformResponse()会被用于所有请求方法的返回数据。

执行流

虽然在拦截器中也可以对数据进行预处理,但是转换器主要职责是对请求/响应数据进行编解码,之所以将转化器单独分离,一是为了和拦截器解耦,二是为了不修改原始请求数据(如果你在拦截器中修改请求数据(options.data),会覆盖原始请求数据,而在某些时候您可能需要原始请求数据)。迪欧的请求流是:

请求拦截器 >> 请求转换器 >> 发起请求 >> 响应转换器 >> 响应拦截器 >> 最终结果

的英文这自一个定义转换器的示例

设置http代理

Dio是使用HttpClient发起的http请求,所以你可以通过配置httpClient来支持代理,示例如下:

  dio.onHttpClientCreate = (HttpClient client) {
    client.findProxy = (uri) {
      //proxy all request to localhost:8888
      return "PROXY localhost:8888";
    };
  };

的完整请示例查看这里

请求取消

你可以通过取消令牌来取消发起的请求:

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");

注意:同一个取消令牌可以用于多个请求,当一个取消令牌取消时,所有使用取消令牌的请求都会被取消。

的完整请示例参考取消示例

曲奇管理

可以你通过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 请参考:https//github.com/flutterchina/cookie_jar

版权和许可

此开源项目为Flutter中文网(https://flutterchina.club)授权,许可是麻省理工学院。如果您喜欢,欢迎的明星。

扑中文网开源项目计划

开发一系列Flutter SDK之外常用(实用)的包,插件,丰富Flutter第三方库,为Flutter生态贡献来自中国开发者的力量。所有项目将发布在Github Flutter中文网组织,所有源码贡献者将加入到我们的组织,成为成员。目前社区已有几个开源项目开始公测,欢迎您加入开发或测试,详情请查看:扑中文网开源项目。如果您想加入到“开源项目计划”,发请邮件到824783146@qq.com,并附上自我介绍(个人基本信息+擅长/关注技术)。

功能和错误

请在问题跟踪器上记录功能请求和错误。



作者:lazydu
链接:https://www.jianshu.com/p/bd4c2dc5e97f
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值