import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:flutter/foundation.dart'; import 'package:news_app/constant/api_const.dart'; import 'package:news_app/util/toast_util.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import '../constant/config.dart'; import '../event/logout_event.dart'; import 'my_exception.dart'; /// @author: bo.zeng /// @email: cnhbwds@gmail.com /// @date: 2025 2025/4/9 16:00 /// @description: class HttpUtil { static final HttpUtil _instance = HttpUtil._internal(); late Dio _dio; factory HttpUtil() => _instance; HttpUtil._internal() { // 初始化 Dio _dio = Dio( BaseOptions( baseUrl: baseUrl, // 替换为你的 API 地址 connectTimeout: const Duration(seconds: 10), // 连接超时 receiveTimeout: const Duration(seconds: 10), // 接收超时 contentType: 'application/json; charset=utf-8', responseType: ResponseType.json, ), ); // 添加拦截器 // 拦截器:统一错误处理和 Toast 提示 _dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { if (uuid.isNotEmpty) { options.headers['uuid'] = uuid; options.headers['cookie'] = "Authorization-M=$uuid"; } options.headers['Content-Type'] = 'application/json'; return handler.next(options); }, onError: (DioException e, handler) { String errorMessage = "Network Error"; if (e.response != null) { // 尝试从服务器错误响应中提取消息 errorMessage = _parseErrorResponse(e.response?.data) ?? "Server Error: ${e.response?.statusCode}"; } else if (e.type == DioExceptionType.connectionTimeout) { errorMessage = "Connection Timeout"; } else if (e.type == DioExceptionType.unknown) { errorMessage = "No Internet Connection"; } // 显示 Toast 提示 _showErrorToast(errorMessage); // 继续抛出错误(可选,如果外层需要捕获) return handler.next(e); }, ), ); // 处理抓包代理 if (enableProxy) { // 使用 IOHttpClientAdapter(适配 Flutter 环境) final adapter = IOHttpClientAdapter(); adapter.createHttpClient = () { final client = HttpClient(); client.findProxy = (uri) => "PROXY 192.168.3.21:9090;"; client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; return client; }; _dio.httpClientAdapter = adapter; } // 日志拦截器(仅在 Debug 模式下启用) if (kDebugMode) { _dio.interceptors.add( PrettyDioLogger(request: false, requestHeader: true), ); } // 开发时设置代理(抓包工具一般默认监听 127.0.0.1:8888) } /// 解析服务器错误响应 String? _parseErrorResponse(dynamic responseData) { try { if (responseData is Map) { return responseData['message'] ?? responseData['error']; } else if (responseData is String) { return responseData; } } catch (e) { return null; } return null; } /// 显示错误 Toast void _showErrorToast(String message) { showToast(message); } /// GET 请求 Future get( String path, { Map? queryParameters, Options? options, }) async { try { final response = await _dio.get( path, queryParameters: queryParameters, options: options, ); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// POST 请求 Future post( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { final response = await _dio.post( path, data: data, queryParameters: queryParameters, options: options, ); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// PUT 请求 Future put( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { final response = await _dio.put( path, data: data, queryParameters: queryParameters, options: options, ); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// DELETE 请求 Future delete( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { final response = await _dio.delete( path, data: data, queryParameters: queryParameters, options: options, ); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// 文件上传 Future upload( String path, { required String filePath, String? fileName, Map? data, Options? options, }) async { try { final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile(filePath, filename: fileName), ...?data, }); final response = await _dio.post(path, data: formData, options: options); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// 文件下载 Future download( String url, String savePath, { ProgressCallback? onProgress, Options? options, }) async { try { final response = await _dio.download( url, savePath, onReceiveProgress: onProgress, options: options, ); return _handleResponse(response); } catch (e) { return _handleError(e); } } /// 统一处理响应 dynamic _handleResponse(Response response) { if (response.statusCode == 200) { final data = response.data; if (data is Map && data['code'] != 200) { throw MyException( code: data['code'], msg: data['msg'] ?? 'Unknown Error', ); } return data['data'] ?? data; } else { throw DioException( requestOptions: response.requestOptions, response: response, error: 'Request failed with status: ${response.statusCode}', ); } } /// 统一处理错误 dynamic _handleError(dynamic error) { if (error is DioException) { if (error.response != null) { // 服务器返回错误(如 404、500) throw 'Server Error: ${error.response?.statusCode}'; } else { // 网络错误(如超时、无网络) throw 'Network Error: ${error.message}'; } } else if (error is MyException) { if (error.code == 401) { eventBus.fire(LogoutEvent(jumLogin: true)); } else { _showErrorToast(error.msg ?? "服务器错误"); } throw error; } else { throw 'Unknown Error: $error'; } } /// 取消请求 void cancelRequests({CancelToken? token}) { token?.cancel('Request cancelled'); } }