不得不说的DIO

5,605 阅读6分钟

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