dio 配置抓包代理
需要通过以下代码才能设置代理。
static const bool isProxyEnable = true;
static const String proxy = "192.168.7.134:8888";
init(){
...
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;
bool _release = kReleaseMode;
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;
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(
responseType: ResponseType.json,
validateStatus: (status) {
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());
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;
}
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 ?? "";
}
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) {
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);
}
}
}
|