Flutter dio 使用 注意事项
Posted -SOLO-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter dio 使用 注意事项相关的知识,希望对你有一定的参考价值。
dio 配置抓包代理
需要通过以下代码才能设置代理。
//是否开启抓包功能
static const bool isProxyEnable = true;
//设置代理服务器地址和端口
static const String proxy = "192.168.7.134:8888";
init()
...
//配置可以通过Fiddler抓包
if(isProxyEnable)
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client)
client.badCertificateCallback =
(X509Certificate cert, String host, int port)
return isProxyEnable &&DebugModelUtil.isDebugMode;
;
client.findProxy = (url)
return 'PROXY $proxy' ;
;
;
以上方法使用了一个工具用来判断app在debug模式还是release模式下
import 'package:flutter/foundation.dart';
bool _debug = kDebugMode; //constant下的一个常量
bool _release = kReleaseMode; //constant下的一个常量
///用于判断是否在debug模式下
class DebugModelUtil
static bool get isDebugMode
return _debug;
post请求 参数需要放在data中
错误情况
测试代码如下,如果放在queryParameters 中,将会拼接到url中。
Map<String, dynamic> params = ;
params['name']='zhangshan&a b c';
params['age']=24;
params['language']='中文';
HttpUtil.dio.post(
'http://192.168.1.1',
queryParameters: params,);
fiddler抓包如下
正确情况
测试代码
Map<String, dynamic> params = ;
params['name'] = 'zhangshan&a b c';
params['age'] = 24;
params['language'] = '中文';
HttpUtil.dio.post(
'http://192.168.1.1',
data: params,
);
fiddler抓包结果
指定content-type
在默认情况下,dio的content-type使用的是 application/json; charset=utf-8 而要使用其他的content-type。需要如下使用,有两种方法
通过header指定
代码如下
Map<String, dynamic> params = ;
params['name'] = 'zhangshan&a b c';
params['age'] = 24;
params['language'] = '中文';
HttpUtil.dio.post('http://192.168.1.1',
data: params,
options: Options(headers:
Headers.contentTypeHeader:
Headers.formUrlEncodedContentType
));
抓包结果如下:
通过content-type指定
代码如下
Map<String, dynamic> params = ;
params['name'] = 'zhangshan&a b c';
params['age'] = 24;
params['language'] = '中文';
HttpUtil.dio.post('http://192.168.1.1',
data: params,
options: Options(
contentType: 'application/x-www-form-urlencoded'));
抓包结果
以上两种方法需要注意的点如下
-
内容不一样,header中使用的是一个Headers中的常量内容是application/x-www-form-urlencoded;charset=utf-8 而contentType中并没有charset这部分。
-
如果指定了类型为application/x-www-form-urlencoded 则dio会对data中的数据自动编码。比如中文和空格。在抓包结果中可以看到。
-
multipart/form-data 不支持这种方式,下面会介绍。
使用 multipart/form-data (附带上传文件)
上面介绍的方法对multipart/form-data 这种类型不支持。需要单独编码。因为这种类型比较复杂,还可以上传文件。
测试代码
Map params = ;
params['name']='zhangshan&a b c';
params['age']=24;
params['language']='中文';
//上传文件
var mtp= MultipartFile.fromString('abcdef',filename: 'text.file') ;
//携带其他参数
var formData = FormData.fromMap('file':mtp,...params);
HttpUtil.dio.post(
'http://newapp.jyb.cn/app_pub/',
data: formData,
);
抓包结果
MultipartFile类具有很多方便的静态方法,可以很轻松的获取一个文件。
指定返回类型
如果你请求的时候指定了返回类型,dio会自动帮你转化,比如你指定类型为一个json。则拿到的response.data 属性就会是一个Map。 这个主要是通过设置option的responseType来实现的。
代码如下
Map params = ;
params['name']='zhangshan&a b c';
params['age']=24;
params['language']='中文';
HttpUtil.dio.post(
'http://newapp.jyb.cn/app_pub/',
data: params,
options: Options(responseType: ResponseType.json)
);
附带一个网络访问工具的封装
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:dio_log/interceptor/dio_log_interceptor.dart';
import 'package:tibet_wxb/common/http/mock_data_interceptor.dart';
import 'package:tibet_wxb/common/util/dev_model_check.dart';
import 'package:tibet_wxb/common/util/web_util.dart';
typedef JsonParseFun<T> = T Function(Map<String, dynamic> json);
typedef StringParseFun<T> = T Function(String str);
enum DioMethod
get,
post,
put,
delete,
patch,
head,
class HttpUtil
static late Dio _dio;
static const int _connectTimeout = 30 * 1000; //15s
static const int _receiveTimeout = 30 * 1000;
static const int _sendTimeout = 30 * 1000;
static DioCacheManager? _dioCacheManager;
//是否开启抓包功能
static const bool isProxyEnable = true;
//设置代理服务器地址和端口
static const String proxy = "192.168.7.134:8888";
static init()
/// 全局属性:请求前缀、连接超时时间、响应超时时间
var options = BaseOptions(
/// 请求的Content-Type,默认值是"application/json; charset=utf-8".
/// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
/// 可以设置此选项为 `Headers.formUrlEncodedContentType`, 这样[Dio]就会自动编码请求体.
responseType: ResponseType.json,
validateStatus: (status)
// 不使用http状态码判断状态,使用AdapterInterceptor来处理(适用于标准REST风格)
return true;
,
connectTimeout: _connectTimeout,
receiveTimeout: _receiveTimeout,
sendTimeout: _sendTimeout,
);
_dio = Dio(options);
//添加网络日志监听
_dio.interceptors.add(DioLogInterceptor());
//添加网络缓存
if (PlatformUtil.isInMobile)
_dioCacheManager = DioCacheManager(CacheConfig());
_dio.interceptors.add(_dioCacheManager!.interceptor);
//添加对模拟数据的处理
_dio.interceptors.add(MockDataInterceptor());
//配置可以通过Fiddler抓包
if(isProxyEnable)
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client)
client.badCertificateCallback =
(X509Certificate cert, String host, int port)
return isProxyEnable &&DebugModelUtil.isDebugMode;
;
client.findProxy = (url)
return 'PROXY $proxy' ;
;
;
static void clearCache()
_dioCacheManager!.clearAll();
static Dio get dio
return _dio;
//只获取string
static Future<String> requestString<T>(
String path,
DioMethod method = DioMethod.get,
Map<String, dynamic>? params,
Map<String, dynamic>? headers,
data,
CancelToken? cancelToken,
Options? options,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
) async
options??=Options();
var str = await request<String>(
path,
stringParseFun: (str)=>str,
method: method,
params: params,
headers: headers,
data: data,
cancelToken: cancelToken,
options: options.copyWith(
responseType: ResponseType.plain
),
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
return str ?? "";
/// 对网络访问的统一封装
/// 如果请求的数据是string的话,那么需要传入stringParseFun 方法
/// 如果返回的数据格式是json的话,需要传入jsonParseFun 方法
static Future<T?> request<T>(
String path,
JsonParseFun<T>? jsonParseFun,
StringParseFun<T>? stringParseFun,
DioMethod method = DioMethod.get,
Map<String, dynamic>? params,
Map<String, dynamic>? headers,
data,
CancelToken? cancelToken,
Options? options,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
) async
const _methodValues =
DioMethod.get: 'get',
DioMethod.post: 'post',
DioMethod.put: 'put',
DioMethod.delete: 'delete',
DioMethod.patch: 'patch',
DioMethod.head: 'head'
;
options ??= Options();
options=options.copyWith(method: _methodValues[method],headers: headers);
print('options content type is $options.contentType');
try
Response response;
response = await _dio.request(path,
data: data,
queryParameters: params,
cancelToken: cancelToken,
options: options,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress);
if (response.statusCode == 200)
if (stringParseFun != null)
return stringParseFun(response.data);
if (response.data is Map && jsonParseFun != null)
return jsonParseFun(response.data);
else
throw DioError(
requestOptions: response.requestOptions,
type: DioErrorType.other,
response: response,
error: "服务器错误:状态码为" + response.statusCode.toString());
on DioError catch (e)
onErrorInterceptor(e);
rethrow;
// 对错误添加更好的语义化处理
static void onErrorInterceptor(DioError err) async
// 异常分类
switch (err.type)
// 4xx 5xx response
case DioErrorType.response:
err.requestOptions.extra["errorMsg"] = err.response?.data ?? "连接异常";
break;
case DioErrorType.connectTimeout:
err.requestOptions.extra["errorMsg"] = "连接超时";
break;
case DioErrorType.sendTimeout:
err.requestOptions.extra["errorMsg"] = "发送超时";
break;
case DioErrorType.receiveTimeout:
err.requestOptions.extra["errorMsg"] = "接收超时";
break;
case DioErrorType.cancel:
err.requestOptions.extra["errorMsg"] =
err.message.isNotEmpty ? err.message : "取消连接";
break;
case DioErrorType.other:
default:
var connectivityResult = await (Connectivity().checkConnectivity());
//判断是否有网络
if (connectivityResult == ConnectivityResult.none)
err.requestOptions.extra["errorMsg"] = "网络未连接";
break;
err.requestOptions.extra["errorMsg"] = "连接异常";
break;
对模拟数据的支持
使用拦截器实现
import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
class MockDataInterceptor extends Interceptor
static const String local_path='http://127.0.0.1/';
@override
Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async
var path = options.path;
if(path.startsWith(local_path))
try
String relPath= "assets/mock_data/"+path.substring(local_path.length);
String jsonStr=await rootBundle.loadString(relPath);
Response response=Response(requestOptions: options);
response.data=json.decode(jsonStr);
response.statusCode=200;
handler.resolve(response);
catch (e)
DioError dioError=DioError(requestOptions: options,error: "解析模拟数据错误$e");
handler.reject(dioError);
else
handler.next(options);
以上是关于Flutter dio 使用 注意事项的主要内容,如果未能解决你的问题,请参考以下文章