/// 时间: 2025-03-21 /// 功能: HTTP客户端工具类(使用纯Dart dio库) /// 介绍: 提供统一的HTTP请求方法,支持GET、POST等请求,用于项目中的网络请求 /// 最新变化: 移除CORS代理,需后台服务器配置CORS响应头 import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; class HttpClient { static const String _baseUrl = 'https://yy.vogov.cn/api/'; static const Duration _timeout = Duration(seconds: 30); static final BaseOptions _options = BaseOptions( baseUrl: _baseUrl, connectTimeout: _timeout, receiveTimeout: _timeout, sendTimeout: _timeout, headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Accept': 'application/json', 'User-Agent': 'Poes-Flutter/1.0.0', }, ); static final Dio _dio = Dio(_options); /// 添加调试日志 static void _debugLog(String message) { if (kDebugMode) { print('HttpClient: $message'); } } /// GET请求 static Future get( String path, { Map? queryParameters, Map? headers, Duration? timeout, }) async { return _request( 'GET', path, queryParameters: queryParameters, headers: headers, timeout: timeout, ); } /// POST请求 - JSON格式 static Future post( String path, { Map? data, Map? headers, Duration? timeout, }) async { return _request( 'POST', path, data: data, headers: headers, timeout: timeout, ); } /// POST请求 - FormData格式 static Future postForm( String path, { Map? data, Map? headers, Duration? timeout, }) async { return _requestForm( 'POST', path, data: data, headers: headers, timeout: timeout, ); } /// 通用请求方法 - JSON格式 static Future _request( String method, String path, { Map? queryParameters, Map? data, Map? headers, Duration? timeout, }) async { try { final url = '$_baseUrl$path'; _debugLog('请求 $method $url'); if (queryParameters != null) { _debugLog('查询参数: $queryParameters'); } final options = Options( method: method, headers: headers != null ? {..._options.headers!, ...headers} : _options.headers, ); if (timeout != null) { options.connectTimeout = timeout; options.receiveTimeout = timeout; options.sendTimeout = timeout; } Response response; if (method.toUpperCase() == 'GET') { response = await _dio.get( url, queryParameters: queryParameters, options: options, ); } else if (method.toUpperCase() == 'POST') { response = await _dio.post( url, data: data, queryParameters: queryParameters, options: options, ); } else { throw UnsupportedError('HTTP method $method is not supported'); } _debugLog('响应状态: ${response.statusCode}'); _debugLog('响应数据: ${response.data}'); return HttpResponse( statusCode: response.statusCode ?? 0, body: response.data is String ? response.data : json.encode(response.data), headers: response.headers.map.cast(), ); } on DioException catch (e) { _debugLog('Dio异常: ${e.type} - ${e.message}'); String message; switch (e.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: message = '请求超时,请检查网络连接'; break; case DioExceptionType.connectionError: message = '网络连接失败,请检查网络设置'; break; case DioExceptionType.badResponse: message = '服务器错误: ${e.response?.statusCode} - ${e.response?.data}'; break; default: message = '请求失败: ${e.message}'; } throw HttpException(message); } catch (e) { _debugLog('未知异常: $e'); throw HttpException('请求失败:$e'); } } /// FormData格式请求方法 static Future _requestForm( String method, String path, { Map? queryParameters, Map? data, Map? headers, Duration? timeout, }) async { try { final url = '$_baseUrl$path'; _debugLog('FormData请求 $method $url'); if (queryParameters != null) { _debugLog('查询参数: $queryParameters'); } if (data != null) { _debugLog('表单数据: $data'); } final formData = FormData.fromMap(data ?? {}); final options = Options( method: method, headers: headers != null ? {..._options.headers!, ...headers} : _options.headers, ); options.headers?['Content-Type'] = 'multipart/form-data'; if (timeout != null) { options.connectTimeout = timeout; options.receiveTimeout = timeout; options.sendTimeout = timeout; } Response response; if (method.toUpperCase() == 'POST') { response = await _dio.post( url, data: formData, queryParameters: queryParameters, options: options, ); } else { throw UnsupportedError('FormData only supports POST method'); } _debugLog('响应状态: ${response.statusCode}'); _debugLog('响应数据: ${response.data}'); return HttpResponse( statusCode: response.statusCode ?? 0, body: response.data is String ? response.data : json.encode(response.data), headers: response.headers.map.cast(), ); } on DioException catch (e) { _debugLog('Dio异常: ${e.type} - ${e.message}'); String message; switch (e.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: message = '请求超时,请检查网络连接'; break; case DioExceptionType.connectionError: message = '网络连接失败,请检查网络设置'; break; case DioExceptionType.badResponse: message = '服务器错误: ${e.response?.statusCode} - ${e.response?.data}'; break; default: message = '请求失败: ${e.message}'; } throw HttpException(message); } catch (e) { _debugLog('未知异常: $e'); throw HttpException('请求失败:$e'); } } } /// HTTP响应类 class HttpResponse { final int statusCode; final String body; final Map headers; HttpResponse({ required this.statusCode, required this.body, required this.headers, }); /// 是否成功 (2xx状态码) bool get isSuccess => statusCode >= 200 && statusCode < 300; /// 解析JSON响应 Map get jsonData { try { return json.decode(body) as Map; } catch (e) { throw FormatException('Invalid JSON response: $body'); } } /// 获取响应消息 String get message { if (isSuccess) { try { return jsonData['msg'] ?? 'Success'; } catch (e) { return 'Success'; } } else { return body; } } /// 获取响应数据 dynamic get data { if (isSuccess) { return jsonData['data']; } return null; } /// 获取响应代码 int get code { if (isSuccess) { try { return jsonData['code'] ?? 0; } catch (e) { return 0; } } return statusCode; } } /// HTTP异常类 class HttpException implements Exception { final String message; const HttpException(this.message); @override String toString() => 'HttpException: $message'; }