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

基本使用

添加依赖

dependencies:

dio: ^3.x.x // 请使用pub上3.0.0分支的最新版本

复制代码

发起一个 GET 请求 :

Response response;

Dio dio = 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());

复制代码

发起一个 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 = FormData.from({

"name": "wendux",

"age": 25,

});

response = await dio.post("/info", data: formData);

复制代码

通过FormData上传多个文件:

FormData.fromMap({

"name": "wendux",

"age": 25,

"file": await MultipartFile.fromFile("./text.txt",filename: "upload.txt"),

"files": [

await MultipartFile.fromFile("./text1.txt", filename: "text1.txt"),

await MultipartFile.fromFile("./text2.txt", filename: "text2.txt"),

]

});

response = await dio.post("/info", data: formData);

复制代码

封装Dio

为什么要封装 dio

上面看了dio的api,非常灵活和简单,那么为什么还要封装呢?因为我们开发需要统一的配置场景。比如:

全局token验证

自定义拦截器

缓存处理

统一封装业务错误逻辑

代理配置

重试机制

log输出

代理配置:

flutter抓包需要配置dio代理,所以我们实现一个代理配置,proxy.dart:

// 是否启用代理

const PROXY_ENABLE = false;

/// 代理服务IP

// const PROXY_IP = '192.168.1.105';

const PROXY_IP = '172.16.43.74';

/// 代理服务端口

const PROXY_PORT = 8866;

复制代码

错误处理:

一般错误分为网络错误、请求错误、认证错误、服务器错误,所以实现统一的错误处理,认证错误需要登录等认证,所以单独一个类型,请求错误也单独设置一个类型,方便我们定位错误,app_exceptions.dart:

import 'package:dio/dio.dart';

/// 自定义异常

class AppException implements Exception {

final String _message;

final int _code;

AppException([

this._code,

this._message,

]);

String toString() {

return "$_code$_message";

}

factory AppException.create(DioError error) {

switch (error.type) {

case DioErrorType.CANCEL:

{

return BadRequestException(-1, "请求取消");

}

break;

case DioErrorType.CONNECT_TIMEOUT:

{

return BadRequestException(-1, "连接超时");

}

break;

case DioErrorType.SEND_TIMEOUT:

{

return BadRequestException(-1, "请求超时");

}

break;

case DioErrorType.RECEIVE_TIMEOUT:

{

return BadRequestException(-1, "响应超时");

}

break;

case DioErrorType.RESPONSE:

{

try {

int errCode = error.response.statusCode;

// String errMsg = error.response.statusMessage;

// return ErrorEntity(code: errCode, message: errMsg);

switch (errCode) {

case 400:

{

return BadRequestException(errCode, "请求语法错误");

}

break;

case 401:

{

return UnauthorisedException(errCode, "没有权限");

}

break;

case 403:

{

return UnauthorisedException(errCode, "服务器拒绝执行");

}

break;

case 404:

{

return UnauthorisedException(errCode, "无法连接服务器");

}

break;

case 405:

{

return UnauthorisedException(errCode, "请求方法被禁止");

}

break;

case 500:

{

return UnauthorisedException(errCode, "服务器内部错误");

}

break;

case 502:

{

return UnauthorisedException(errCode, "无效的请求");

}

break;

case 503:

{

return UnauthorisedException(errCode, "服务器挂了");

}

break;

case 505:

{

return UnauthorisedException(errCode, "不支持HTTP协议请求");

}

break;

default:

{

// return ErrorEntity(code: errCode, message: "未知错误");

return AppException(errCode, error.response.statusMessage);

}

}

} on Exception catch (_) {

return AppException(-1, "未知错误");

}

}

break;

default:

{

return AppException(-1, error.message);

}

}

}

}

/// 请求错误

class BadRequestException extends AppException {

BadRequestException([int code, String message]) : super(code, message);

}

/// 未认证异常

class UnauthorisedException extends AppException {

UnauthorisedException([int code, String message]) : super(code, message);

}

复制代码

Error拦截器:

有了上面的异常类型,我们要把DioError变成自己定义的异常:

import 'package:dio/dio.dart';

import 'package:flutter/material.dart';

import 'app_exceptions.dart';

/// 错误处理拦截器

class ErrorInterceptor extends Interceptor {

@override

Future onError(DioError err) {

// error统一处理

AppException appException = AppException.create(err);

// 错误提示

debugPrint('DioError===: ${appException.toString()}');

err.error = appException;

return super.onError(err);

}

}

复制代码

http单例操作类:

利用单例和配置,实现一个dio的封装。

class Http {

///超时时间

static const int CONNECT_TIMEOUT = 30000;

static const int RECEIVE_TIMEOUT = 30000;

static Http _instance = Http._internal();

factory Http() => _instance;

Dio dio;

CancelToken _cancelToken = new CancelToken();

Http._internal() {

if (dio == null) {

// BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数

BaseOptions options = new BaseOptions(

connectTimeout: CONNECT_TIMEOUT,

// 响应流上前后两次接受到数据的间隔,单位为毫秒。

receiveTimeout: RECEIVE_TIMEOUT,

// Http请求头.

headers: {},

);

dio = new Dio(options);

// 添加error拦截器

dio.interceptors

.add(ErrorInterceptor());

// 在调试模式下需要抓包调试,所以我们使用代理,并禁用HTTPS证书校验

if (PROXY_ENABLE) {

(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =

(client) {

client.findProxy = (uri) {

return "PROXY $PROXY_IP:$PROXY_PORT";

};

//代理工具会提供一个抓包的自签名证书,会通不过证书校验,所以我们禁用证书校验

client.badCertificateCallback =

(X509Certificate cert, String host, int port) => true;

};

}

}

}

///初始化公共属性

///

/// [baseUrl] 地址前缀

/// [connectTimeout] 连接超时赶时间

/// [receiveTimeout] 接收超时赶时间

/// [interceptors] 基础拦截器

void init(

{String baseUrl,

int connectTimeout,

int receiveTimeout,

List interceptors}) {

dio.options = dio.options.merge(

baseUrl: baseUrl,

connectTimeout: connectTimeout,

receiveTimeout: receiveTimeout,

);

if (interceptors != null && interceptors.isNotEmpty) {

dio.interceptors..addAll(interceptors);

}

}

}

复制代码

增加取消功能:

/*

* 取消请求

*

* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。

* 所以参数可选

*/

void cancelRequests({CancelToken token}) {

token ?? _cancelToken.cancel("cancelled");

}

复制代码

增加认证header:

/// 读取本地配置

Map getAuthorizationHeader() {

var headers;

String accessToken = Global.accessToken;

if (accessToken != null) {

headers = {

'Authorization': 'Bearer $accessToken',

};

}

return headers;

}

复制代码

添加cookie和cache:

添加cookie管理:

cookie_jar: ^1.0.1

dio_cookie_manager: ^1.0.0

复制代码

// Cookie管理

CookieJar cookieJar = CookieJar();

dio.interceptors.add(CookieManager(cookieJar));

// 加内存缓存

dio.interceptors.add(NetCache());

复制代码

利用sp做磁盘缓存:

shared_preferences: ^0.5.6+3

复制代码

编写cache类,net_cache.dart:

class CacheObject {

CacheObject(this.response)

: timeStamp = DateTime.now().millisecondsSinceEpoch;

Response response;

int timeStamp;

@override

bool operator ==(other) {

return response.hashCode == other.hashCode;

}

@override

int get hashCode => response.realUri.hashCode;

}

class NetCacheInterceptor extends Interceptor {

// 为确保迭代器顺序和对象插入时间一致顺序一致,我们使用LinkedHashMap

var cache = LinkedHashMap();

@override

onRequest(RequestOptions options) async {

if (!CACHE_ENABLE) return options;

// refresh标记是否是刷新缓存

bool refresh = options.extra["refresh"] == true;

// 是否磁盘缓存

bool cacheDisk = options.extra["cacheDisk"] == true;

// 如果刷新,先删除相关缓存

if (refresh) {

// 删除uri相同的内存缓存

delete(options.uri.toString());

// 删除磁盘缓存

if (cacheDisk) {

await SpUtil().remove(options.uri.toString());

}

return options;

}

// get 请求,开启缓存

if (options.extra["noCache"] != true &&

options.method.toLowerCase() == 'get') {

String key = options.extra["cacheKey"] ?? options.uri.toString();

// 策略 1 内存缓存优先,2 然后才是磁盘缓存

// 1 内存缓存

var ob = cache[key];

if (ob != null) {

//若缓存未过期,则返回缓存内容

if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <

CACHE_MAXAGE) {

return cache[key].response;

} else {

//若已过期则删除缓存,继续向服务器请求

cache.remove(key);

}

}

// 2 磁盘缓存

if (cacheDisk) {

var cacheData = SpUtil().getJSON(key);

if (cacheData != null) {

return Response(

statusCode: 200,

data: cacheData,

);

}

}

}

}

@override

onError(DioError err) async {

// 错误状态不缓存

}

@override

onResponse(Response response) async {

// 如果启用缓存,将返回结果保存到缓存

if (CACHE_ENABLE) {

await _saveCache(response);

}

}

Future _saveCache(Response object) async {

RequestOptions options = object.request;

// 只缓存 get 的请求

if (options.extra["noCache"] != true &&

options.method.toLowerCase() == "get") {

// 策略:内存、磁盘都写缓存

// 缓存key

String key = options.extra["cacheKey"] ?? options.uri.toString();

// 磁盘缓存

if (options.extra["cacheDisk"] == true) {

await SpUtil().setJSON(key, object.data);

}

// 内存缓存

// 如果缓存数量超过最大数量限制,则先移除最早的一条记录

if (cache.length == CACHE_MAXCOUNT) {

cache.remove(cache[cache.keys.first]);

}

cache[key] = CacheObject(object);

}

}

void delete(String key) {

cache.remove(key);

}

}

复制代码

dio加入缓存:

// 加内存缓存

dio.interceptors.add(NetCacheInterceptor());

复制代码

重试拦截器:

在网络断开的时候,监听网络,等重连的时候重试:

import 'dart:io';

import 'package:dio/dio.dart';

import 'package:flutter/cupertino.dart';

import 'connectivity_request_retrier.dart';

class RetryOnConnectionChangeInterceptor extends Interceptor {

final DioConnectivityRequestRetrier requestRetrier;

RetryOnConnectionChangeInterceptor({

@required this.requestRetrier,

});

@override

Future onError(DioError err) async {

if (_shouldRetry(err)) {

try {

return requestRetrier.scheduleRequestRetry(err.request);

} catch (e) {

return e;

}

}

return err;

}

bool _shouldRetry(DioError err) {

return err.type == DioErrorType.DEFAULT &&

err.error != null &&

err.error is SocketException;

}

}

复制代码

import 'dart:async';

import 'package:connectivity/connectivity.dart';

import 'package:dio/dio.dart';

import 'package:flutter/material.dart';

class DioConnectivityRequestRetrier {

final Dio dio;

final Connectivity connectivity;

DioConnectivityRequestRetrier({

@required this.dio,

@required this.connectivity,

});

Future scheduleRequestRetry(RequestOptions requestOptions) async {

StreamSubscription streamSubscription;

final responseCompleter = Completer();

streamSubscription = connectivity.onConnectivityChanged.listen(

(connectivityResult) {

if (connectivityResult != ConnectivityResult.none) {

streamSubscription.cancel();

responseCompleter.complete(

dio.request(

requestOptions.path,

cancelToken: requestOptions.cancelToken,

data: requestOptions.data,

onReceiveProgress: requestOptions.onReceiveProgress,

onSendProgress: requestOptions.onSendProgress,

queryParameters: requestOptions.queryParameters,

options: requestOptions,

),

);

}

},

);

return responseCompleter.future;

}

}

复制代码

添加重试拦截器:

if (Global.retryEnable) {

dio.interceptors.add(

RetryOnConnectionChangeInterceptor(

requestRetrier: DioConnectivityRequestRetrier(

dio: Dio(),

connectivity: Connectivity(),

),

),

);

}

复制代码

restful请求:

/// restful get 操作

Future get(

String path, {

Map params,

Options options,

CancelToken cancelToken,

bool refresh = false,

bool noCache = !CACHE_ENABLE,

String cacheKey,

bool cacheDisk = false,

}) async {

Options requestOptions = options ?? Options();

requestOptions = requestOptions.merge(extra: {

"refresh": refresh,

"noCache": noCache,

"cacheKey": cacheKey,

"cacheDisk": cacheDisk,

});

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

Response response;

response = await dio.get(path,

queryParameters: params,

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

/// restful post 操作

Future post(

String path, {

Map params,

data,

Options options,

CancelToken cancelToken,

}) async {

Options requestOptions = options ?? Options();

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

var response = await dio.post(path,

data: data,

queryParameters: params,

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

/// restful put 操作

Future put(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

Options requestOptions = options ?? Options();

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

var response = await dio.put(path,

data: data,

queryParameters: params,

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

/// restful patch 操作

Future patch(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

Options requestOptions = options ?? Options();

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

var response = await dio.patch(path,

data: data,

queryParameters: params,

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

/// restful delete 操作

Future delete(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

Options requestOptions = options ?? Options();

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

var response = await dio.delete(path,

data: data,

queryParameters: params,

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

/// restful post form 表单提交操作

Future postForm(

String path, {

Map params,

Options options,

CancelToken cancelToken,

}) async {

Options requestOptions = options ?? Options();

Map _authorization = getAuthorizationHeader();

if (_authorization != null) {

requestOptions = requestOptions.merge(headers: _authorization);

}

var response = await dio.post(path,

data: FormData.fromMap(params),

options: requestOptions,

cancelToken: cancelToken ?? _cancelToken);

return response.data;

}

复制代码

通常我们喜欢静态方法调用,所以新建一个类:

import 'package:dio/dio.dart';

import 'package:flutter_dio/http/http.dart';

import 'app_exceptions.dart';

import 'cache.dart';

class HttpUtils {

static void init(

{String baseUrl,

int connectTimeout,

int receiveTimeout,

List interceptors}) {

Http().init(

baseUrl: baseUrl,

connectTimeout: connectTimeout,

receiveTimeout: receiveTimeout,

interceptors: interceptors);

}

static void setHeaders(Map map) {

Http().setHeaders(map);

}

static void cancelRequests({CancelToken token}) {

Http().cancelRequests(token: token);

}

static Future get(

String path, {

Map params,

Options options,

CancelToken cancelToken,

bool refresh = false,

bool noCache = !CACHE_ENABLE,

String cacheKey,

bool cacheDisk = false,

}) async {

return await Http().get(

path,

params: params,

options: options,

cancelToken: cancelToken,

refresh: refresh,

noCache: noCache,

cacheKey: cacheKey,

);

}

static Future post(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

return await Http().post(

path,

data: data,

params: params,

options: options,

cancelToken: cancelToken,

);

}

static Future put(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

return await Http().put(

path,

data: data,

params: params,

options: options,

cancelToken: cancelToken,

);

}

static Future patch(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

return await Http().patch(

path,

data: data,

params: params,

options: options,

cancelToken: cancelToken,

);

}

static Future delete(

String path, {

data,

Map params,

Options options,

CancelToken cancelToken,

}) async {

return await Http().delete(

path,

data: data,

params: params,

options: options,

cancelToken: cancelToken,

);

}

static Future postForm(

String path, {

Map params,

Options options,

CancelToken cancelToken,

}) async {

return await Http().postForm(

path,

params: params,

options: options,

cancelToken: cancelToken,

);

}

}

复制代码

使用:

初始化:

void main() {

HttpUtils.init(

baseUrl: "https://gan.io/",

);

runApp(MyApp());

}

复制代码

在model里写接口请求:

static Future> getCategories() async {

try {

final response = await HttpUtils.get(categories);

var data = CategoryEntity.fromJson(response);

return ApiResponse.completed(data);

} on DioError catch (e) {

return ApiResponse.error(e.error);

}

}

复制代码

调用:

void getCategories() async {

ApiResponse entity = await GanRepository.getCategories();

print(entity.data.data.length);

}

复制代码

dio设置自定义post请求_强大的dio封装,可能满足你的一切需要相关推荐

  1. dio设置自定义post请求_基于dio库封装flutter项目的标准网络框架

    网络框架是每个应用的基石,封装一个好的网络框架不仅是项目的一个好的开始,并且直接影响到随后项目的稳定性和可扩展性.在移动开发的各个端都有非常赞的网络请求基础框架,比如Android的okhttp库.s ...

  2. dio设置自定义post请求_使用Dio封装ajax的post和get操作

    dio是经常使用到的一个包,为flutter的日常开发提供了很多支持.本文主要描述如何使用Dio封装ajax请求. dio的依赖:dependencies: dio: 3.0.8 dio包的导入:im ...

  3. dio设置自定义post请求_Flutter中的http网络请求

    前言 由于刚过完年工作比较紧张,文章已经四周没有更新了,非常抱歉!接下来依然会尽量保持每周更新! 本篇介绍Flutter中网络请求相关api的使用,Flutter在Flutter engine中提供了 ...

  4. dio设置自定义post请求_Flutter 使用dio来发起网络请求以及Cookie管理

    说点儿闲话 Flutter官方建议您使用 dio 来发起网络请求,在学习过程中,也尝试过用dart io中的HttpClient发起的请求,这里主要讲一下dio的使用以及CookieJar.Cooki ...

  5. dio设置自定义post请求_flutter中dio的post请求方式使用总结

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天. 重要消息 1 引言 dio用来在flutter跨平台开发中访问网络的框架,在使用的时候,我们首先是引入依赖 depen ...

  6. dio设置自定义post请求_Flutter Dio简单二次封装和自定义Header

    话不多说自己看代码封装的比较简单,比较适合入门学习Dio. import 'package:dio/dio.dart'; import 'Api.dart'; /* * 封装 restful 请求 * ...

  7. dio设置自定义post请求_flutter Dio封装get/post请求

    最近刚学flutter,参考简书大佬们的文章,使用dio简单封装了一下网络请求工具类. 依赖版本 9683BF5F-1627-47F1-AE3A-ECBC55ED49CF.png 网络请求单例 A2F ...

  8. 同时多个axios请求_用 React+Antd 封装 Axios 实现全局 Loading 效果

    前言 今天在做 react 后台管理的时候要实现一个全局 Loading 效果,通常使用 axios 库与后端进行数据交互.为了更好的用户体验,在每次请求前添加一个加载效果,让用户知道在等待加载. 要 ...

  9. android 设置允许http请求_网络请求框架----OkHttp原理

    一.前言 在 Android 中,网络请求是一个必不可少的功能,因此就有许多代表网络请求客户端的组件库,具有代表性的有下面三种: Apache 的 HTTP 客户端组件 HttpClient. Jav ...

最新文章

  1. pycharm变量存_20190909 pycharm快捷键与变量
  2. S5PV210体系结构与接口07:中断系统编程
  3. ActiveMQ此例简单介绍基于docker的activemq安装与集群搭建
  4. 【数据结构笔记】归并排序(merge_sort)+ 堆排序 -- python2.7
  5. WPF 使用MultiBinding ,TwoWay ,ValidationRule ,需要注意的事项
  6. 苹果ppt_从苹果发布会到抖音,火遍互联网的快闪视频如何用PPT轻松制作?
  7. android 动态壁纸 例子,调用android动态壁纸的实例介绍
  8. WAREZ无形帝国(盗版之源)
  9. 数字化城管信息系统项目需求说明书
  10. 摩尔庄园同一服务器怎么显示好友,摩尔庄园手游怎么搜索别人搜索好友步骤详解...
  11. java使用pdfbox将图片转换成pdf
  12. 蓝牙(三)蓝牙协议的初始化
  13. 柴浩然妙用三物黄芩汤
  14. HTTP 405 错误 – 方法不被允许 (Method not allowed)【转载】
  15. 白光模块?彩光模块?
  16. 官网下载Eclipse历史版本
  17. Django案例:显示图书的信息books
  18. 一图了解英特尔® 超能云终端的12个问题!
  19. 【面向学渣编程】囚犯问题(十分详解,含表格,适合新手)
  20. mysql创建用户并赋权(亲测)

热门文章

  1. matlab遍历矩阵(读取矩阵每一个数据)
  2. 计算机 调 应用统计,山东工商学院统计学院2020年应用统计专硕调剂信息
  3. syntax和semantics的区别
  4. 充分条件,必要条件,充分必要条件
  5. Windows API 常量定义
  6. optaplanner学习笔记(九)分数计算表现技巧
  7. 流媒体客户端的流传送原理
  8. Win10和Ubuntu的引导修复,Ubuntu引导Win10
  9. 文本表达进击:从BERT-flow到BERT-whitening、SimCSE
  10. base64的加密和解密方法