|
|
@@ -0,0 +1,423 @@
|
|
|
+import 'dart:convert';
|
|
|
+import 'dart:io';
|
|
|
+
|
|
|
+import 'package:dio/dio.dart';
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Author: thl
|
|
|
+ * @GitHub: https://github.com/Sky24n
|
|
|
+ * @JianShu: https://www.jianshu.com/u/cbf2ad25d33a
|
|
|
+ * @Email: 863764940@qq.com
|
|
|
+ * @Description: Dio Util.
|
|
|
+ * @Date: 2018/12/19
|
|
|
+ */
|
|
|
+
|
|
|
+/// <BaseResp<T> 返回 status code msg data.
|
|
|
+class BaseResp<T> {
|
|
|
+ String status;
|
|
|
+ int code;
|
|
|
+ String msg;
|
|
|
+ T data;
|
|
|
+
|
|
|
+ BaseResp(this.status, this.code, this.msg, this.data);
|
|
|
+
|
|
|
+ @override
|
|
|
+ String toString() {
|
|
|
+ StringBuffer sb = new StringBuffer('{');
|
|
|
+ sb.write("\"status\":\"$status\"");
|
|
|
+ sb.write(",\"code\":$code");
|
|
|
+ sb.write(",\"msg\":\"$msg\"");
|
|
|
+ sb.write(",\"data\":\"$data\"");
|
|
|
+ sb.write('}');
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// <BaseRespR<T> 返回 status code msg data Response.
|
|
|
+class BaseRespR<T> {
|
|
|
+ String status;
|
|
|
+ int code;
|
|
|
+ String msg;
|
|
|
+ T data;
|
|
|
+ Response response;
|
|
|
+
|
|
|
+ BaseRespR(this.status, this.code, this.msg, this.data, this.response);
|
|
|
+
|
|
|
+ @override
|
|
|
+ String toString() {
|
|
|
+ StringBuffer sb = new StringBuffer('{');
|
|
|
+ sb.write("\"status\":\"$status\"");
|
|
|
+ sb.write(",\"code\":$code");
|
|
|
+ sb.write(",\"msg\":\"$msg\"");
|
|
|
+ sb.write(",\"data\":\"$data\"");
|
|
|
+ sb.write('}');
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// 请求方法.
|
|
|
+class Method {
|
|
|
+ static final String get = "GET";
|
|
|
+ static final String post = "POST";
|
|
|
+ static final String put = "PUT";
|
|
|
+ static final String head = "HEAD";
|
|
|
+ static final String delete = "DELETE";
|
|
|
+ static final String patch = "PATCH";
|
|
|
+}
|
|
|
+
|
|
|
+///Http配置.
|
|
|
+class HttpConfig {
|
|
|
+ /// constructor.
|
|
|
+ HttpConfig({
|
|
|
+ this.status,
|
|
|
+ this.code,
|
|
|
+ this.msg,
|
|
|
+ this.data,
|
|
|
+ this.options,
|
|
|
+ this.pem,
|
|
|
+ this.pKCSPath,
|
|
|
+ this.pKCSPwd,
|
|
|
+ });
|
|
|
+
|
|
|
+ /// BaseResp [String status]字段 key, 默认:status.
|
|
|
+ String status;
|
|
|
+
|
|
|
+ /// BaseResp [int code]字段 key, 默认:errorCode.
|
|
|
+ String code;
|
|
|
+
|
|
|
+ /// BaseResp [String msg]字段 key, 默认:errorMsg.
|
|
|
+ String msg;
|
|
|
+
|
|
|
+ /// BaseResp [T data]字段 key, 默认:data.
|
|
|
+ String data;
|
|
|
+
|
|
|
+ /// Options.
|
|
|
+ Options options;
|
|
|
+
|
|
|
+ /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
|
|
|
+ /// PEM证书内容.
|
|
|
+ String pem;
|
|
|
+
|
|
|
+ /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
|
|
|
+ /// PKCS12 证书路径.
|
|
|
+ String pKCSPath;
|
|
|
+
|
|
|
+ /// 详细使用请查看dio官网 https://github.com/flutterchina/dio/blob/flutter/README-ZH.md#Https证书校验.
|
|
|
+ /// PKCS12 证书密码.
|
|
|
+ String pKCSPwd;
|
|
|
+}
|
|
|
+
|
|
|
+/// 单例 DioUtil.
|
|
|
+/// debug模式下可以打印请求日志. DioUtil.openDebug().
|
|
|
+/// dio详细使用请查看dio官网(https://github.com/flutterchina/dio).
|
|
|
+class DioUtil {
|
|
|
+ static final DioUtil _singleton = DioUtil._init();
|
|
|
+ static Dio _dio;
|
|
|
+
|
|
|
+ /// BaseResp [String status]字段 key, 默认:status.
|
|
|
+ String _statusKey = "status";
|
|
|
+
|
|
|
+ /// BaseResp [int code]字段 key, 默认:errorCode.
|
|
|
+ String _codeKey = "errorCode";
|
|
|
+
|
|
|
+ /// BaseResp [String msg]字段 key, 默认:errorMsg.
|
|
|
+ String _msgKey = "errorMsg";
|
|
|
+
|
|
|
+ /// BaseResp [T data]字段 key, 默认:data.
|
|
|
+ String _dataKey = "data";
|
|
|
+
|
|
|
+ /// Options.
|
|
|
+ Options _options = getDefOptions();
|
|
|
+
|
|
|
+ /// PEM证书内容.
|
|
|
+ String _pem;
|
|
|
+
|
|
|
+ /// PKCS12 证书路径.
|
|
|
+ String _pKCSPath;
|
|
|
+
|
|
|
+ /// PKCS12 证书密码.
|
|
|
+ String _pKCSPwd;
|
|
|
+
|
|
|
+ /// 是否是debug模式.
|
|
|
+ static bool _isDebug = false;
|
|
|
+
|
|
|
+ static DioUtil getInstance() {
|
|
|
+ return _singleton;
|
|
|
+ }
|
|
|
+
|
|
|
+ factory DioUtil() {
|
|
|
+ return _singleton;
|
|
|
+ }
|
|
|
+
|
|
|
+ DioUtil._init() {
|
|
|
+ _dio = new Dio(_options);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 打开debug模式.
|
|
|
+ static void openDebug() {
|
|
|
+ _isDebug = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// set Config.
|
|
|
+ void setConfig(HttpConfig config) {
|
|
|
+ _statusKey = config.status ?? _statusKey;
|
|
|
+ _codeKey = config.code ?? _codeKey;
|
|
|
+ _msgKey = config.msg ?? _msgKey;
|
|
|
+ _dataKey = config.data ?? _dataKey;
|
|
|
+ _mergeOption(config.options);
|
|
|
+ _pem = config.pem ?? _pem;
|
|
|
+ if (_dio != null) {
|
|
|
+ _dio.options = _options;
|
|
|
+ if (_pem != null) {
|
|
|
+ _dio.onHttpClientCreate = (HttpClient client) {
|
|
|
+ client.badCertificateCallback =
|
|
|
+ (X509Certificate cert, String host, int port) {
|
|
|
+ if (cert.pem == _pem) {
|
|
|
+ // 证书一致,则放行
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ };
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (_pKCSPath != null) {
|
|
|
+ _dio.onHttpClientCreate = (HttpClient client) {
|
|
|
+ SecurityContext sc = new SecurityContext();
|
|
|
+ //file为证书路径
|
|
|
+ sc.setTrustedCertificates(_pKCSPath, password: _pKCSPwd);
|
|
|
+ HttpClient httpClient = new HttpClient(context: sc);
|
|
|
+ return httpClient;
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Make http request with options.
|
|
|
+ /// [method] The request method.
|
|
|
+ /// [path] The url path.
|
|
|
+ /// [data] The request data
|
|
|
+ /// [options] The request options.
|
|
|
+ /// <BaseResp<T> 返回 status code msg data .
|
|
|
+ Future<BaseResp<T>> request<T>(String method, String path,
|
|
|
+ {data, Options options, CancelToken cancelToken}) async {
|
|
|
+ Response response = await _dio.request(path,
|
|
|
+ data: data,
|
|
|
+ options: _checkOptions(method, options),
|
|
|
+ cancelToken: cancelToken);
|
|
|
+ _printHttpLog(response);
|
|
|
+ String _status;
|
|
|
+ int _code;
|
|
|
+ String _msg;
|
|
|
+ T _data;
|
|
|
+ if (response.statusCode == HttpStatus.ok ||
|
|
|
+ response.statusCode == HttpStatus.created) {
|
|
|
+ try {
|
|
|
+ if (response.data is Map) {
|
|
|
+ _status = (response.data[_statusKey] is int)
|
|
|
+ ? response.data[_statusKey].toString()
|
|
|
+ : response.data[_statusKey];
|
|
|
+ _code = (response.data[_codeKey] is String)
|
|
|
+ ? int.tryParse(response.data[_codeKey])
|
|
|
+ : response.data[_codeKey];
|
|
|
+ _msg = response.data[_msgKey];
|
|
|
+ _data = response.data[_dataKey];
|
|
|
+ } else {
|
|
|
+ Map<String, dynamic> _dataMap = _decodeData(response);
|
|
|
+ _status = (_dataMap[_statusKey] is int)
|
|
|
+ ? _dataMap[_statusKey].toString()
|
|
|
+ : _dataMap[_statusKey];
|
|
|
+ _code = (_dataMap[_codeKey] is String)
|
|
|
+ ? int.tryParse(_dataMap[_codeKey])
|
|
|
+ : _dataMap[_codeKey];
|
|
|
+ _msg = _dataMap[_msgKey];
|
|
|
+ _data = _dataMap[_dataKey];
|
|
|
+ }
|
|
|
+ return new BaseResp(_status, _code, _msg, _data);
|
|
|
+ } catch (e) {
|
|
|
+ return new Future.error(new DioError(
|
|
|
+ response: response,
|
|
|
+ message: "data parsing exception...",
|
|
|
+ type: DioErrorType.RESPONSE,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new Future.error(new DioError(
|
|
|
+ response: response,
|
|
|
+ message: "statusCode: $response.statusCode, service error",
|
|
|
+ type: DioErrorType.RESPONSE,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Make http request with options.
|
|
|
+ /// [method] The request method.
|
|
|
+ /// [path] The url path.
|
|
|
+ /// [data] The request data
|
|
|
+ /// [options] The request options.
|
|
|
+ /// <BaseRespR<T> 返回 status code msg data Response.
|
|
|
+ Future<BaseRespR<T>> requestR<T>(String method, String path,
|
|
|
+ {data, Options options, CancelToken cancelToken}) async {
|
|
|
+ Response response = await _dio.request(path,
|
|
|
+ data: data,
|
|
|
+ options: _checkOptions(method, options),
|
|
|
+ cancelToken: cancelToken);
|
|
|
+ _printHttpLog(response);
|
|
|
+ String _status;
|
|
|
+ int _code;
|
|
|
+ String _msg;
|
|
|
+ T _data;
|
|
|
+ if (response.statusCode == HttpStatus.ok ||
|
|
|
+ response.statusCode == HttpStatus.created) {
|
|
|
+ try {
|
|
|
+ if (response.data is Map) {
|
|
|
+ _status = (response.data[_statusKey] is int)
|
|
|
+ ? response.data[_statusKey].toString()
|
|
|
+ : response.data[_statusKey];
|
|
|
+ _code = (response.data[_codeKey] is String)
|
|
|
+ ? int.tryParse(response.data[_codeKey])
|
|
|
+ : response.data[_codeKey];
|
|
|
+ _msg = response.data[_msgKey];
|
|
|
+ _data = response.data[_dataKey];
|
|
|
+ } else {
|
|
|
+ Map<String, dynamic> _dataMap = _decodeData(response);
|
|
|
+ _status = (_dataMap[_statusKey] is int)
|
|
|
+ ? _dataMap[_statusKey].toString()
|
|
|
+ : _dataMap[_statusKey];
|
|
|
+ _code = (_dataMap[_codeKey] is String)
|
|
|
+ ? int.tryParse(_dataMap[_codeKey])
|
|
|
+ : _dataMap[_codeKey];
|
|
|
+ _msg = _dataMap[_msgKey];
|
|
|
+ _data = _dataMap[_dataKey];
|
|
|
+ }
|
|
|
+ return new BaseRespR(_status, _code, _msg, _data, response);
|
|
|
+ } catch (e) {
|
|
|
+ return new Future.error(new DioError(
|
|
|
+ response: response,
|
|
|
+ message: "data parsing exception...",
|
|
|
+ type: DioErrorType.RESPONSE,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new Future.error(new DioError(
|
|
|
+ response: response,
|
|
|
+ message: "statusCode: $response.statusCode, service error",
|
|
|
+ type: DioErrorType.RESPONSE,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Download the file and save it in local. The default http method is "GET",you can custom it by [Options.method].
|
|
|
+ /// [urlPath]: The file url.
|
|
|
+ /// [savePath]: The path to save the downloading file later.
|
|
|
+ /// [onProgress]: The callback to listen downloading progress.please refer to [OnDownloadProgress].
|
|
|
+ Future<Response> download(
|
|
|
+ String urlPath,
|
|
|
+ savePath, {
|
|
|
+ OnDownloadProgress onProgress,
|
|
|
+ CancelToken cancelToken,
|
|
|
+ data,
|
|
|
+ Options options,
|
|
|
+ }) {
|
|
|
+ return _dio.download(urlPath, savePath,
|
|
|
+ onProgress: onProgress,
|
|
|
+ cancelToken: cancelToken,
|
|
|
+ data: data,
|
|
|
+ options: options);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// decode response data.
|
|
|
+ Map<String, dynamic> _decodeData(Response response) {
|
|
|
+ if (response == null ||
|
|
|
+ response.data == null ||
|
|
|
+ response.data.toString().isEmpty) {
|
|
|
+ return new Map();
|
|
|
+ }
|
|
|
+ return json.decode(response.data.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /// check Options.
|
|
|
+ Options _checkOptions(method, options) {
|
|
|
+ if (options == null) {
|
|
|
+ options = new Options();
|
|
|
+ }
|
|
|
+ options.method = method;
|
|
|
+ return options;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// merge Option.
|
|
|
+ void _mergeOption(Options opt) {
|
|
|
+ _options.method = opt.method ?? _options.method;
|
|
|
+ _options.headers = (new Map.from(_options.headers))..addAll(opt.headers);
|
|
|
+ _options.baseUrl = opt.baseUrl ?? _options.baseUrl;
|
|
|
+ _options.connectTimeout = opt.connectTimeout ?? _options.connectTimeout;
|
|
|
+ _options.receiveTimeout = opt.receiveTimeout ?? _options.receiveTimeout;
|
|
|
+ _options.responseType = opt.responseType ?? _options.responseType;
|
|
|
+ _options.data = opt.data ?? _options.data;
|
|
|
+ _options.extra = (new Map.from(_options.extra))..addAll(opt.extra);
|
|
|
+ _options.contentType = opt.contentType ?? _options.contentType;
|
|
|
+ _options.validateStatus = opt.validateStatus ?? _options.validateStatus;
|
|
|
+ _options.followRedirects = opt.followRedirects ?? _options.followRedirects;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// print Http Log.
|
|
|
+ void _printHttpLog(Response response) {
|
|
|
+ if (!_isDebug) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ print("----------------Http Log----------------" +
|
|
|
+ "\n[statusCode]: " +
|
|
|
+ response.statusCode.toString() +
|
|
|
+ "\n[request ]: " +
|
|
|
+ _getOptionsStr(response.request));
|
|
|
+ _printDataStr("reqdata ", response.request.data);
|
|
|
+ _printDataStr("response", response.data);
|
|
|
+ } catch (ex) {
|
|
|
+ print("Http Log" + " error......");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// get Options Str.
|
|
|
+ String _getOptionsStr(Options request) {
|
|
|
+ return "method: " +
|
|
|
+ request.method +
|
|
|
+ " baseUrl: " +
|
|
|
+ request.baseUrl +
|
|
|
+ " path: " +
|
|
|
+ request.path;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// print Data Str.
|
|
|
+ void _printDataStr(String tag, Object value) {
|
|
|
+ String da = value.toString();
|
|
|
+ while (da.isNotEmpty) {
|
|
|
+ if (da.length > 512) {
|
|
|
+ print("[$tag ]: " + da.substring(0, 512));
|
|
|
+ da = da.substring(512, da.length);
|
|
|
+ } else {
|
|
|
+ print("[$tag ]: " + da);
|
|
|
+ da = "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// get dio.
|
|
|
+ Dio getDio() {
|
|
|
+ return _dio;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// create new dio.
|
|
|
+ static Dio createNewDio([Options options]) {
|
|
|
+ options = options ?? getDefOptions();
|
|
|
+ Dio dio = new Dio(options);
|
|
|
+ return dio;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// get Def Options.
|
|
|
+ static Options getDefOptions() {
|
|
|
+ Options options = new Options();
|
|
|
+ options.contentType =
|
|
|
+ ContentType.parse("application/x-www-form-urlencoded");
|
|
|
+ options.connectTimeout = 1000 * 10;
|
|
|
+ options.receiveTimeout = 1000 * 20;
|
|
|
+ return options;
|
|
|
+ }
|
|
|
+}
|