Flutter网络框架DIO的基本使用
1.基本概念
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等…
2.简单示例代码
import 'package:dio/dio.dart';
main() async {
var dio = Dio();
Response response = await dio.get("https://google.com");
print(response.data);
}
3.添加依赖
在pubspec.yaml文件中添加:
dependencies:
dio: 2.1.13 #当前最新版本为2.1.13
4.DIO常见内容列表
- 使用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")));
-
请求示例(get、post)
1、发起get请求
Response response; Dio dio = new Dio(); response = await dio.get("/test?id=12&name=wendu"); print(response.data.toString()); // 请求参数也可以通过对象传递,上面的代码等同于: response = await dio.get("/test", queryParameters: {"id": 12, "name": "wendu"}); print(response.data.toString());
2、发起POST请求
- 普通post方式
response = await dio.post("/test", data: {"id": 12, "name": "wendu"});
- FormData的post请求方式:
Dio支持发送 FormData, 请求数据将会以 multipart/form-data方式编码, FormData中可以一个或多个包含文件
注意: 只有 post 方法支持发送 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"), //支持直接上传字节数组 (List<int>) ,方便直接上传内存中的内容 "file2": new UploadFileInfo.fromBytes( utf8.encode("hello world"), "word.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);
3、发起并发请求
response = await Future.wait([dio.post("/info"), dio.get("/token")]);
4、下载文件
response = await dio.download("https://www.google.com/", "./xx.html");
5、请求接收方式
- a.流形式接收
Response<ResponseBody> rs = await Dio().get<ResponseBody>(url, options: Options(responseType: ResponseType.stream), //设置接收类型为stream); print(rs.data.stream); //响应流
- b.二进制接收
Response<List<int>> rs = await Dio().get<List<int>>(url, options: Options(responseType: ResponseType.bytes), //设置接收类型为bytes); print(rs.data); //二进制数组
6.监听发送(上传)数据速度
response = await dio.post( "http://www.dtworkroom.com/doris/1/2.0.0/test", data: {"aa": "bb" * 22}, onSendProgress: (int sent, int total) { print("$sent $total"); }, );
7.以流的形式提交二进制数据
// 二进制数据 List<int> postData = <int>[...]; await dio.post( url, data: Stream.fromIterable(postData.map((e) => [e])), //创建一个Stream<List<int>> options: Options( headers: { HttpHeaders.contentLengthHeader: postData.length, // 设置content-length }, ), ); 如果要监听提交进度,则必须设置content-length,反之则是可选的。
-
Dio APIs
DIO核心API代码如下:
Future request(String path, {data,Map queryParameters, Options options,CancelToken cancelToken, ProgressCallback onSendProgress, ProgressCallback onReceiveProgress)
response = await request(
"/test",
data: {"id": 12, "name": "xx"},
options: Options(method: "GET"),
);
建议在项目中使用Dio单例,这样便可对同一个dio实例发起的所有请求进行一些统一的配置,比如设置公共header、请求基地址、超时时间等
我们可以使用默认配置或传递一个可选BaseOptions参数来创建一个Dio实例
Dio dio = new Dio(); // 使用默认配置
// 配置dio实例
dio.options.baseUrl = "https://www.xx.com/api";
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
// 或者通过传递一个 `BaseOptions`来创建dio实例
BaseOptions options = new BaseOptions(
baseUrl: "https://www.xx.com/api",
connectTimeout: 5000,
receiveTimeout: 3000,
);
Dio dio = new Dio(options);
- 请求配置
下面是所有的请求配置选项。 如果请求method没有指定,则默认为GET :
{
/// Http method.
String method;
/// 请求基地址,可以包含子路径,如: "https://www.google.com/api/".
String baseUrl;
/// Http请求头.
Map<String, dynamic> headers;
/// 连接服务器超时时间,单位是毫秒.
int connectTimeout;
/// 2.x中为接收数据的最长时限.
int receiveTimeout;
/// 请求路径,如果 `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;
/// 公共query参数
Map<String, dynamic /*String|Iterable<String>*/ > queryParameters;
}
- 响应数据
当请求成功时会返回一个Response对象,它包含如下字段
{
/// 响应数据,可能已经被转换了类型, 详情请参考Options中[ResponseType].
var data;
/// 响应头
HttpHeaders headers;
/// 本次请求信息
Options request;
/// Http status code.
int statusCode;
/// 是否重定向
bool isRedirect;
/// 重定向信息
List<RedirectInfo> redirects ;
/// 最终真正的请求地址(因为可能会重定向)
Uri realUri;
/// 响应对象的自定义字段(可以在拦截器中设置它),调用方可以在`then`中获取.
Map<String, dynamic> extra;
//注:假如有一个url返回的是json数据,返回数据在默认情况下(options.responseType为json)会被自动转为Json对象(Map或List)的:
}
以请求Google为例:
Response response = await dio.get("https://www.google.com");
print(response.data);
print(response.headers);
print(response.request);
print(response.statusCode);
- 拦截器
我们在网络请求中往往需要查看日志信息,此时就需要添加一个日志拦截器
dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
在添加拦截器时需要注意,由于拦截器队列的执行顺序是FIFO,如果把log拦截器添加到了最前面,则后面拦截器对options的更改就不会被打印(但依然会生效),所以建议把log拦截添加到队尾。
- Cookie管理
有时在进行请求中需要拦截cookie来自动管理请求/响应cookie,那么就需要添加依赖
dependencies:
cookie_jar: ^1.0.1
你可以创建一个CookieJar 或 PersistCookieJar 来帮您自动管理cookie, dio 默认使用 CookieJar , 它会将cookie保存在内存中。
如果您想对cookie进行持久化, 请使用 PersistCookieJar , 示例代码如下:
var dio = new Dio();
dio.interceptors.add(CookieManager(CookieJar()))
普遍说来,如果需要创建一些自定义的拦截器,只需要通过继承Intercepto类来实现即可
- 错误处理
当请求过程中发生错误时, 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;
///原始的error或exception对象,通常type为DEFAULT时存在。
dynamic error;
/// 错误栈信息,可能为null
StackTrace stackTrace;
}
- 设置Http代理
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
// config the http client
client.findProxy = (uri) {
//proxy all request to localhost:8888
return "PROXY localhost:8888";
};
// you can also create a new HttpClient to dio
// return new HttpClient();
};
- 请求取消
可以通过 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");