import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; /// <BaseResp<T> 返回 status code msg data. class BaseResp<T> { String status; int code; String msg; T data; T extra; T userType; BaseResp(this.status, this.code, this.msg, this.data, this.extra, this.userType); @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(",\"extra\":\"$extra\""); sb.write(",\"userType\":\"$userType\""); sb.write('}'); return sb.toString(); } } /// <BaseRespR<T> 返回 status code msg data Response. class BaseRespR<T> { String status; int code; String msg; T data; T extra; T userType; Response response; BaseRespR(this.status, this.code, this.msg, this.data, this.extra, this.userType, 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(",\"extra\":\"$extra\""); sb.write(",\"userType\":\"$userType\""); 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, this.nativeCookie, }); /// 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. BaseOptions 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; //缓存 Map nativeCookie; } /// 单例 DioUtil. /// debug模式下可以打印请求日志. DioUtil.openDebug(). /// dio详细使用请查看dio官网(https://github.com/flutterchina/dio). class DioUtil { static final DioUtil _instance = DioUtil._init(); static Dio _dio; /// BaseResp [String status]字段 key, 默认:status. String _statusKey = "status"; /// BaseResp [int code]字段 key, 默认:error = 0 代表成功. String _codeKey = "error"; /// BaseResp [String msg]字段 key, 默认:errorMsg. String _msgKey = "message"; /// BaseResp [T data]字段 key, 默认:data. String _dataKey = "data"; /// BaseResp [T data]字段 key, 默认:extra. String _extraKey = 'extra'; // BaseResp [T data]字段 key, 默认:user_type. String _userType = 'user_type'; /// Options. static BaseOptions _options = getDefOptions(); /// PEM证书内容. String _pem; /// PKCS12 证书路径. String _pKCSPath; /// PKCS12 证书密码. String _pKCSPwd; /// 是否是debug模式. static bool _isDebug = true; static DioUtil getInstance() { return _instance; } factory DioUtil() { return _instance; } 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); _mergeNativeCookie(config); _pem = config.pem ?? _pem; if (_dio != null) { _dio.options = _options; if (_pem != null) { // httpClientAdapter (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { client.badCertificateCallback = (X509Certificate cert, String host, int port) { if (cert.pem == _pem) { // 证书一致,则放行 return true; } return false; }; }; } if (_pKCSPath != null) { (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (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; T _extra; T _userType; _status = response.statusCode.toString(); 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]; _extra = response.data[_extraKey]; } else { Map<String, dynamic> _dataMap = _decodeData(response); _code = (_dataMap[_codeKey] is String) ? int.tryParse(_dataMap[_codeKey]) : _dataMap[_codeKey]; _msg = _dataMap[_msgKey]; _data = _dataMap[_dataKey]; _extra = response.data[_extraKey]; _userType = response.data[_userType]; } return new BaseResp(_status, _code, _msg, _data, _extra, _userType); } catch (e) { return new Future.error(new DioError( response: response, message: "data parsing exception...", type: DioErrorType.RESPONSE, )); } } else { _code = 1; _msg = '请求失败'; } 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; T _extra; T _userType; _status = response.statusCode.toString(); if (response.statusCode == HttpStatus.ok || response.statusCode == HttpStatus.created) { try { if (response.data is Map) { _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); _code = (_dataMap[_codeKey] is String) ? int.tryParse(_dataMap[_codeKey]) : _dataMap[_codeKey]; _msg = _dataMap[_msgKey]; _data = _dataMap[_dataKey]; _extra = response.data[_extraKey]; _userType = response.data[_userType]; } return new BaseRespR(_status, _code, _msg, _data, _extra, _userType, response); } catch (e) { return new Future.error(new DioError( response: response, message: "data parsing exception...", type: DioErrorType.RESPONSE, )); } } else { _code = 1; _msg = '请求失败'; } 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, { CancelToken cancelToken, data, Options options, }) { return _dio.download(urlPath, savePath, 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(BaseOptions 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.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; } void _mergeNativeCookie(HttpConfig config) { //合并native cookie _options.headers = (new Map.from(_options.headers))..addAll(config.nativeCookie); print('cookie---------'); print(_options.headers); } /// 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; } /// 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]) { Dio dio = new Dio(); return dio; } /// get Def Options. static BaseOptions getDefOptions() { BaseOptions options = BaseOptions(); options.connectTimeout = 10 * 1000; options.receiveTimeout = 20 * 1000; options.contentType = ContentType.parse('application/x-www-form-urlencoded'); options.baseUrl = 'https://earth.iyanzhi.com/'; Map<String, dynamic> headers = Map<String, dynamic>(); headers['Accept'] = 'application/json'; return options; } }