dio是Flutter中文网开源的一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等...html
自dio开源至今,收到了大量国内外开发者的反馈,到目前为止,dio在pub仓库得分96分,github dart语言下开源项目排名已上升到前20,dio如今也是flutter第三方package中star数最多的。在dio开源的两个月中,已迭代了18个小版本,国内外有多家公司的Flutter APP正在使用dio,已经过了大量的实战验证,已经在AppStore上架的APP典型表明是gitme:git
Gitme是一个强大的github客户端APP,它使用dio做为http client,除了正常的http请求以外,有一个最大的特色是gitme经过dio拦截器,实现了APP内统1、隔离的缓存层,彻底和上层ui解耦。您能够下载体验一下Gitme。若有必要,我会单独出一篇文章详细介绍一下如何使用dio拦截器实现离线缓存,你们若是有兴趣能够在评论中反馈。github
因此,今天,咱们正式发布dio的第一个稳定版本1.0。下面,咱们全面的介绍一下dio 1.0的功能及使用。json
建议dio的老用户都升级到1.0正式版,并同时感谢大家在dio项目初期的支持,没有大家的反馈与建议,dio稳定版不会这么快发布。api
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等...缓存
dependencies:
dio: ^x.x.x // 请使用pub上的最新版本
复制代码
import 'package:dio/dio.dart';
Dio dio = new Dio();
Response response=await dio.get("https://www.google.com/");
print(response.data);
复制代码
Clear()
。发起一个 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"),
// 支持文件数组上传
"files": [
new UploadFileInfo(new File("./example/upload.txt"), "upload.txt"),
new UploadFileInfo(new File("./example/upload.txt"), "upload.txt")
]
});
response = await dio.post("/info", data: formData)
复制代码
…你能够在这里获取全部示例代码.
你可使用默认配置或传递一个可选 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;
/// `validateStatus` 决定http响应状态码是否被dio视为请求成功, 返回`validateStatus`
/// 返回`true` , 请求结果就会按成功处理,不然会按失败处理.
ValidateStatus validateStatus;
/// 用户自定义字段,能够在 [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
, 经过拦截器你能够在请求以前或响应以后(但尚未被 then
或 catchError
处理)作一些统一的预处理操做。
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 e;//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
方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器以前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些须要串行化请求/响应的场景中很是实用,后面咱们将给出一个示例。
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
}
复制代码
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();
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)
}
}
复制代码
{
/// 响应信息, 若是错误发生在在服务器返回数据以前,它为 `null`
Response response;
/// 错误描述.
String message;
/// 错误类型,见下文
DioErrorType type;
/// 错误栈信息,可能为null
StackTrace stackTrace;
}
复制代码
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
}
复制代码
默认状况下, 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")))
复制代码
这里有一个示例.
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的请求流是:
请求拦截器 >> 请求转换器 >> 发起请求 >> 响应转换器 >> 响应拦截器 >> 最终结果。
这是一个自定义转换器的示例.
Dio 是使用 HttpClient发起的http请求,因此你能够经过配置 httpClient
来支持代理,示例以下:
dio.onHttpClientCreate = (HttpClient client) {
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY localhost:8888";
};
// 你也能够本身建立一个新的HttpClient实例返回。
// return new HttpClient(SecurityContext);
};
复制代码
完整的示例请查看这里.
你能够经过 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的请求都会被取消。
完整的示例请参考取消示例.
你能够经过 cookieJar
来自动管理请求/响应cookie.
dio cookie 管理 API 是基于开源库 cookie_jar.
你能够建立一个CookieJar
或 PersistCookieJar
来帮您自动管理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) 受权 ,license 是 MIT. 若是您喜欢,欢迎star.
Flutter中文网开源项目计划
开发一系列Flutter SDK以外经常使用(实用)的Package、插件,丰富Flutter第三方库,为Flutter生态贡献来自中国开发者的力量。全部项目将发布在 Github Flutter中文网 Organization ,全部源码贡献者将加入到咱们的Organization,成为成员. 目前社区已有几个开源项目开始公测,欢迎您加入开发或测试,详情请查看: Flutter中文网开源项目。 若是您想加入到“开源项目计划”, 请发邮件到824783146@qq.com, 并附上自我介绍(我的基本信息+擅长/关注技术)。
Please file feature requests and bugs at the issue tracker.