Flutter http请求库dio

转载自:https://segmentfault.com/a/1190000015853959

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=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 =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=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;/// 连接服务器超时时间,单位是毫秒.intconnectTimeout;///  响应流上前后两次接受到数据的间隔,单位为毫秒。如果两次间隔超过[receiveTimeout],///  [Dio] 将会抛出一个[DioErrorType.RECEIVE_TIMEOUT]的异常.///  注意: 这并不是接收数据的总时限.intreceiveTimeout;/// 请求数据,可以是任意类型.vardata;/// 请求路径,如果`path`以 "http(s)"开始, 则`baseURL`会被忽略; 否则,/// 将会和baseUrl拼接出完整的的url.Stringpath="";/// 请求的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 extra;}

这里有一个完成的示例.

响应数据

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

{/// 响应数据,可能已经被转换了类型, 详情请参考Options中的[ResponseType].vardata;/// 响应头HttpHeaders headers;/// 本次请求信息Options request;/// Http status code.intstatusCode;/// 响应对象的自定义字段(可以在拦截器中设置它),调用方可以在`then`中获取.Map extra;}

示例如下:

Response response=awaitdio.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){// 在请求被发送之前做一些事情returnoptions;//continue// 如果你想完成请求并返回一些自定义数据,可以返回一个`Response`对象或返回`dio.resolve(data)`。// 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义数据data.//// 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象,或返回`dio.reject(errMsg)`,// 这样请求将被中止并触发异常,上层catchError会被调用。} dio.interceptor.response.onSuccess = (Response response) {// 在返回响应数据之前做一些预处理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;

完成和终止请求/响应

在所有拦截器中,你都可以改变请求执行流, 如果你想完成请求/响应并返回自定义数据,你可以返回一个 Response 对象或返回 dio.resolve(data)的结果。 如果你想终止(触发一个错误,上层catchError会被调用)一个请求/响应,那么可以返回一个DioError 对象或返回 dio.reject(errMsg) 的结果.

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

拦截器中支持异步任务

拦截器中不仅支持同步任务,而且也支持异步任务, 下面是在请求拦截器中发起异步任务的一个实例:

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 拦截器

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

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()

你也可以调用拦截器的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();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;    }  };

完整的示例代码请点击 这里.

错误处理

当请求过程中发生错误时, 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 字段

{/// 响应信息, 如果错误发生在在服务器返回数据之前,它为`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 会将请求数据(除过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 =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 来自动管理请求/响应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/flutterchi... .

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容