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),
        );
      }
    }
  }
相关推荐
火云代理3 小时前
Python 使用代理 IP 前,先弄清 Requests 的配置来源与验证方法
网络·python·网络协议·tcp/ip·ip
如夏倾城3 小时前
QUIC——HTTP/3 的唯一底层传输协议 及 0-RTT恢复
网络·网络协议·tcp/ip·安全·http
科研邪修法4 小时前
培训PDF保护配置:把需求转换成可验收权限
linux·服务器·网络
笑容掩饰我的失落5 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
传奇开心果编程18 小时前
【Flutter踩坑排雷】第25课:ScrollController 误用坑|跳转失效、监听泄漏与“控制器未附加”
android·flutter·ios·移动开发·跨平台·dart·声明式ui
程序员老刘1 天前
Flutter 3.50更新,开发者应该关注什么?
flutter·ai编程·客户端
略懂而已1 天前
安装SSL后网站速度变慢?
网络·网络协议·ssl
笑容掩饰我的失落1 天前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
云上工程笔记1 天前
RDMA / IB 高速互联 GPU 云对比:多机训练网络性能与价格
网络·gpu算力·gpu