Flutter网络请求Dio封装实战

封装 http_client.dart

Dart 复制代码
import 'package:dio/dio.dart';
import 'dart:async';

/// API异常类,用于处理网络请求中的各种错误
class ApiException implements Exception {
  // 错误码
  final int code;
  // 错误消息
  final String message;

  ApiException(this.code, this.message);

  @override
  String toString() {
    return 'ApiException: {code: $code, message: $message}';
  }

  // 常见的错误类型
  static final networkError = ApiException(-1, '网络连接失败,请检查网络设置');
  static final serverError = ApiException(500, '服务器错误,请稍后重试');
  static final unauthorized = ApiException(401, '未授权,请重新登录');
  static final notFound = ApiException(404, '请求的资源不存在');
}

class HttpClient {
  // 单例模式
  static final HttpClient _instance = HttpClient._internal();
  factory HttpClient() => _instance;
  late Dio _dio;

  HttpClient._internal() {
    // 初始化Dio
    _dio = Dio();

    // 配置基础选项
    _dio.options = BaseOptions(
      baseUrl: 'https://你的域名.com/api/',
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    );

    // 添加拦截器
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) {
          // 可以在这里添加token等认证信息
          // 开发环境添加详细日志
          print('📤 请求URL: ${options.uri}');
          print('📤 请求头: ${options.headers}');
          if (options.queryParameters.isNotEmpty) {
            print('📤 请求参数: ${options.queryParameters}');
          }
          if (options.data != null) {
            print('📤 请求体: ${options.data}');
          }
          return handler.next(options);
        },
        onResponse: (response, handler) {
          // 开发环境添加详细日志
          print('📥 响应URL: ${response.realUri}');
          print('📥 响应状态: ${response.statusCode}');
          print('📥 响应数据: ${response.data}');
          return handler.next(response);
        },
        onError: (DioException e, handler) {
          // 开发环境添加详细日志
          print('❌ 错误URL: ${e.requestOptions.uri}');
          print('❌ 错误类型: ${e.type}');
          print('❌ 错误消息: ${e.message}');
          if (e.response != null) {
            print('❌ 错误状态码: ${e.response?.statusCode}');
            print('❌ 错误响应数据: ${e.response?.data}');
          }
          print('❌ 错误堆栈: ${e.stackTrace}');
          return handler.next(e);
        },
      ),
    );
  }

  // GET请求 - 修改为支持任何类型的响应
  Future<dynamic> get(
    String path, {
    Map<String, dynamic>? queryParameters,
  }) async {
    try {
      Response response = await _dio.get(
        path,
        queryParameters: queryParameters,
      );
      return response.data;
    } catch (e) {
      throw _handleError(e);
    }
  }

  // POST请求 - 修改为支持任何类型的响应
  Future<dynamic> post(
    String path, {
    Map<String, dynamic>? data,
  }) async {
    try {
      Response response = await _dio.post(path, data: data);
      return response.data;
    } catch (e) {
      throw _handleError(e);
    }
  }

  // PUT请求 - 修改为支持任何类型的响应
  Future<dynamic> put(
    String path, {
    Map<String, dynamic>? data,
  }) async {
    try {
      Response response = await _dio.put(path, data: data);
      return response.data;
    } catch (e) {
      throw _handleError(e);
    }
  }

  // DELETE请求 - 修改为支持任何类型的响应
  Future<dynamic> delete(String path) async {
    try {
      Response response = await _dio.delete(path);
      return response.data;
    } catch (e) {
      throw _handleError(e);
    }
  }

  // 错误处理 - 抛出ApiException
  dynamic _handleError(dynamic e) {
    if (e is DioException) {
      switch (e.type) {
        case DioExceptionType.connectionTimeout:
          return ApiException(408, '网络连接超时,请检查网络设置');
        case DioExceptionType.sendTimeout:
          return ApiException(408, '发送请求超时');
        case DioExceptionType.receiveTimeout:
          return ApiException(408, '接收响应超时');
        case DioExceptionType.badCertificate:
          return ApiException(495, '证书验证失败');
        case DioExceptionType.badResponse:
          // 提供更详细的错误信息
          if (e.response != null) {
            return ApiException(e.response?.statusCode ?? 500, '服务器错误: ${e.response?.statusMessage ?? '未知错误'}');
          }
          return ApiException(500, '服务器错误,请稍后重试');
        case DioExceptionType.cancel:
          return ApiException(0, '请求已取消');
        case DioExceptionType.connectionError:
          // 详细的连接错误信息
          String message = '网络连接错误,请检查网络';
          if (e.error != null) {
            message += ' (${e.error.toString().split(':').first})';
          }
          return ApiException(-1, message);
        case DioExceptionType.unknown:
          // 尝试提供更具体的错误信息
          if (e.error != null) {
            String errorStr = e.error.toString();
            if (errorStr.contains('SocketException')) {
              return ApiException(-1, '网络连接失败,请检查网络设置');
            } else if (errorStr.contains('Connection refused')) {
              return ApiException(-1, '服务器连接被拒绝,请检查URL是否正确');
            } else if (errorStr.contains('Failed host lookup')) {
              return ApiException(-1, '域名解析失败,请检查URL是否正确');
            }
          }
          return ApiException(-1, '未知错误,请稍后重试');
      }
    }
    // 非Dio异常也返回具体信息
    return ApiException(-1, '请求失败: ${e.toString()}');
  }
}

使用GET请求

Dart 复制代码
// GET请求方法
  void getHttp() async {
    try {
      // 添加详细日志
      print('📱 用户点击了GET请求按钮');
      final response = await _httpClient.get('getHome2');
      // 这里可以处理响应数据,更新UI等
      try {
        Map<String, dynamic> responseData;
        // 检查响应类型并进行适当处理
        if (response is String) {
          // 将JSON字符串解析为Map对象
          responseData = jsonDecode(response) as Map<String, dynamic>;
        } else if (response is Map) {
          responseData = response as Map<String, dynamic>;
        } else if (response is List) {
          return; // 如果是列表,无法按预期结构处理
        } else {
          return;
        }

        // 提取advert数据 - 根据JSON结构解析
        if (responseData.containsKey('resultData') &&
            responseData['resultData'] is Map) {
          Map<String, dynamic> resultData =
              responseData['resultData'] as Map<String, dynamic>;

          if (resultData.containsKey('advert') &&
              resultData['advert'] is List) {
            List<dynamic> advertData = resultData['advert'] as List<dynamic>;
            print('$advertData');
          }
        }
      } catch (e) {}

      // 显示成功提示
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('请求成功'), backgroundColor: Colors.green),
        );
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('请求失败'), backgroundColor: Colors.red),
        );
      }
    }
  }

使用POST请求

Dart 复制代码
// POST请求方法 - 演示如何提交数据
  void postHttp() async {
    try {
      // 添加详细日志
      print('📱 用户点击了POST请求按钮');

      // 显示加载提示
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(const SnackBar(content: Text('正在提交数据...')));
      }

      // 准备要提交的数据
      Map<String, dynamic> postData = {
        'name': '测试用户',
        'age': 25,
        'email': 'test@example.com',
        'active': true,
      };

      print('📤 准备提交的数据: $postData');

      // 发送POST请求
      // 注意:这里使用一个示例路径,实际使用时需要替换为您真实的API路径
      final response = await _httpClient.post(
        'examplePostEndpoint', // 替换为实际的POST接口路径
        data: postData,
      );

      // 处理响应数据
      print('📊 响应数据类型: ${response.runtimeType}');
      print('📊 响应数据: $response');

      // 显示成功提示
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('POST请求成功'),
            backgroundColor: Colors.green,
          ),
        );
      }
    } catch (e) {
      print('❌ 页面层捕获POST错误: $e');
      print('❌ 错误类型: ${e.runtimeType}');

      // 显示错误提示
      if (mounted) {
        String errorMessage = 'POST请求失败';
        if (e is ApiException) {
          errorMessage = e.message;
        } else {
          errorMessage = e.toString();
        }

        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
        );
      }
    }
  }
相关推荐
Fanmeang21 小时前
异世界网络:BGP联邦的建立
网络
M1582276905521 小时前
工业互联利器!EtherNet/IP 转 ModbusTCP 网关,让跨协议通信零门槛
服务器·网络·tcp/ip
阿巴~阿巴~21 小时前
基于UDP协议的英汉翻译服务系统:从网络通信到字典查询的完整机制
linux·服务器·网络·网络协议·udp协议·套接字绑定·英汉翻译服务系统
阿巴~阿巴~21 小时前
简易回声服务器实现与网络测试指南
linux·服务器·网络·udp协议·网络测试·udp套接字编程
star_start_sky21 小时前
住宅代理网络:我最近用来数据采集和自动化的小工具
网络·爬虫·自动化
科技智驱1 天前
误分区数据恢复:3种方法,按需选择更高效
网络·电脑·数据恢复
云边云科技5341 天前
云边云科技SD-WAN解决方案 — 构建安全、高效、智能的云网基石
网络·科技·安全·架构·it·sdwan
慧慧吖@1 天前
sse,短轮询,长轮询,webSocket
网络·websocket·网络协议
catchadmin1 天前
PHP 依赖管理器 Composer 2.9 发布
开发语言·php·composer
悠悠~飘1 天前
PHP基础-字符串(第16天)
php