import 'dart:io'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; import 'package:dio/adapter.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:marking_app/common/config/request_config.dart'; import 'package:marking_app/routes/RouterManager.dart'; import 'package:marking_app/utils/index.dart'; import 'package:marking_app/utils/the_global.dart'; import 'package:package_info/package_info.dart'; class RestDio { late Dio _dio; // 单例模式 static final RestDio _instance = RestDio._internal(); factory RestDio() => _instance; RestDio._internal() { init(); } // 初始化请求配置 init() { BaseOptions options = BaseOptions( connectTimeout: RequestConfig.connectTimeout, receiveTimeout: RequestConfig.receiveTimeout, ); _dio = Dio(options); _dio.interceptors.add(AuthInterceptor()); // 添加 token _dio.interceptors.add(ResponseHandle()); // 添加 数据返回拦截 _dio.interceptors.add(TheError()); // 添加 数据返回拦截 const isProd = bool.fromEnvironment('dart.vm.product'); if (!isProd && RequestConfig.requestDataPrinting) { _dio.interceptors.add(LogInterceptor(responseBody: true, requestBody: true)); //添加日志 } // 添加https证书 setHttpsPEM(); // setHttpsPKCS12(); // 使用代理 // setFindProxy(); } String PEM = "XXXXX"; // certificate content String PKCS12File = "XXXXX"; // certificate content //dio 基本方法 Future getDio() async { return _dio; } //添加证书 setHttpsPEM() async { (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { client.badCertificateCallback = (X509Certificate cert, String host, int port) { // if (cert.pem == PEM) { // // Verify the certificate // return true; // } // return false; return true; }; }; } setHttpsPKCS12() async { (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { SecurityContext sc = new SecurityContext(); //file is the path of certificate sc.setTrustedCertificates(PKCS12File); HttpClient httpClient = new HttpClient(context: sc); return httpClient; }; } //设置代理 setFindProxy() async { // 设置请求拦截器 _dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) { // 判断 URI 的路径是否包含指定字符串 if (options.uri.path.contains(RequestConfig.hwProxyKeywords)) { // 设置代理地址 _dio.httpClientAdapter = DefaultHttpClientAdapter(); (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { client.findProxy = (uri) { print('进入开始代理...............'); return RequestConfig.proBaseUrlOfHomework; }; }; } return handler.next(options); // 继续发送请求 })); } } /* * *AuthInterceptor *添加header认证 * */ class AuthInterceptor extends Interceptor { String PLATFORM = "android"; //可根据代码进行判断 @override onRequest(RequestOptions options, RequestInterceptorHandler handler) async { //获取app版本 PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; if (Platform.isIOS) { PLATFORM = "ios"; } else if (Platform.isAndroid) { PLATFORM = "android"; } else if (Platform.isWindows) { PLATFORM = "Windows"; } else if (Platform.isMacOS) { PLATFORM = "macos"; } else if (Platform.isLinux) { PLATFORM = "Linux"; } Map headers = {}; headers["Accept-Charset"] = "utf-8"; headers["Connection"] = "keep-alive"; headers["Accept"] = "*/*"; headers["x-version"] = version; //自己更改配置 headers["x-platform"] = PLATFORM; //获取存储数据 保存header token String? token = await FastData.getInstance().getToken(); if (null != token && token.isNotEmpty) { headers["Authorization"] = 'Bearer $token'; //添加自己项目中的请求头 进行保存 } options.headers = headers; Uri uri = options.uri; print(uri.toString()); // 判断请求的 URL 是否包含指定字符串 if (uri.toString().contains(RequestConfig.hwProxyKeywords)) { String newPath = options.path.replaceFirst(RegExp('^${RequestConfig.hwProxyKeywords}'), ''); options.path = newPath; // 修改请求的 URL options.baseUrl = RequestConfig.proBaseUrlOfHomework; } // print('请求参数query:' + jsonEncode(options.queryParameters)); // print('请求参数data:' + jsonEncode(options.data)); return super.onRequest(options, handler); } } /* * ResponseHandle * 监听返回响应 **/ class ResponseHandle extends Interceptor { @override void onResponse(Response response, ResponseInterceptorHandler handler) { const isProd = bool.fromEnvironment('dart.vm.product'); if (!isProd && RequestConfig.requestDataPrinting) { printJson(response.data); } super.onResponse(response, handler); } } /* * ResponseHandle * 监听返回响应 **/ class TheError extends Interceptor { // 是否有网 Future isConnected() async { var connectivityResult = await (Connectivity().checkConnectivity()); return connectivityResult != ConnectivityResult.none; } @override Future onError(DioError err, ErrorInterceptorHandler handler) async { // 自定义一个socket实例,因为dio原生的实例,message属于是只读的 // 这里是我单独加的,因为默认的dio err实例,的几种类型,缺少无网络情况下的错误提示信息 if (err.error is SocketException) { err.error = MyDioSocketException( err.message.contains('111') ? "服务器拒绝连接,请重试" : err.message, osError: err.error?.osError, address: err.error?.address, port: err.error?.port, ); } // dio默认的错误实例,如果是没有网络,只能得到一个未知错误,无法精准的得知是否是无网络的情况 if (err.type == DioErrorType.other) { bool isConnectNetWork = await isConnected(); if (!isConnectNetWork && err.error is MyDioSocketException) { err.error.message = "当前网络不可用,请检查您的网络"; } } // error统一处理 AppException appException = AppException.create(err); // 错误提示 // debugPrint('DioError===: ${appException.toString()}'); err = appException; return super.onError(err, handler); } } String getDioErrorTypeStr(DioError err) { DioErrorType errorType = err.type; String str; switch (errorType) { case DioErrorType.connectTimeout: str = '连接超时,请检查网络再重试'; break; case DioErrorType.sendTimeout: str = '发送时间超时,请重试'; break; case DioErrorType.receiveTimeout: str = '接收数据超时,请重试'; break; case DioErrorType.response: str = '请求返回失败'; break; case DioErrorType.cancel: str = '请求取消'; break; case DioErrorType.other: str = '请求其他错误'; break; } return str; } /// 自定义异常 class AppException extends DioError { final String _message; final int _code; AppException(this._code, this._message, StackTrace? theStackTrace, {required super.requestOptions, super.type, super.error}) { super.stackTrace = theStackTrace; } @override String toString() { return "$_code$_message" + super.toString(); } String getMessage() { return _message; } factory AppException.create(DioError error) { switch (error.type) { case DioErrorType.cancel: { return AppException(-1, "请求取消", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case DioErrorType.connectTimeout: { return AppException(-1, "连接超时", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case DioErrorType.sendTimeout: { return AppException(-1, "请求超时", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case DioErrorType.receiveTimeout: { return AppException(-1, "响应超时", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } 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 AppException(errCode!, "请求语法错误", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 401: { var currentContext = TheGlobal.navigatorKey.currentState?.overlay?.context; if (currentContext != null) { var routePath = ModalRoute.of(currentContext)?.settings; if (routePath != null) { } else { if (TheGlobal.navigatorKey.currentContext != null) { ExceptionHandle.toLogin(TheGlobal.navigatorKey.currentContext); } } } return AppException(errCode!, "登录信息过期,请重新登录", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 403: { return AppException(errCode!, "服务器拒绝执行", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 404: { return AppException(errCode!, "无法连接服务器", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 405: { return AppException(errCode!, "请求方法被禁止", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 500: { return AppException(errCode!, "服务器内部错误", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 502: { return AppException(errCode!, "无效的请求", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 503: { return AppException(errCode!, "服务器挂了", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } case 505: { return AppException(errCode!, "不支持HTTP协议请求", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } default: { // return ErrorEntity(code: errCode, message: "未知错误"); return AppException(errCode!, error.response!.statusMessage!, error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } } } on Exception catch (_) { return AppException(-1, "未知错误", error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } } default: { return AppException(-1, error.error.message, error.stackTrace, requestOptions: error.requestOptions, type: error.type, error: error.error); } } } } class ExceptionHandle { // 异常处理信息 static void exceptionPrompt(BuildContext context, AppException err, {bool logicHandle = true}) { ToastUtils.getFluttertoast( context: context, msg: err._message, backgroundColor: Colors.grey[350], toastLength: Toast.LENGTH_LONG, ); if (!logicHandle) return; switch (err._code) { case 401: toLogin(context); // 重新前往登录 break; default: } } static void toLogin(context, {int timeer = 800}) { setTimeOut( timeer, () => RouterManager.router.navigateTo(context, RouterManager.loginPath, clearStack: true), ); } } // 这里是一个我单独写得soket错误实例,因为dio默认生成的是不允许修改message内容的,我只能自定义一个使用 class MyDioSocketException extends SocketException { String message; MyDioSocketException( this.message, { osError, address, port, }) : super( message, osError: osError, address: address, port: port, ); }