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),
        );
      }
    }
  }
相关推荐
馨谙4 小时前
OpenSSH 安全配置核心概念解析
linux·服务器·网络
半桔4 小时前
【IO多路转接】IO 多路复用之 select:从接口解析到服务器实战
linux·服务器·c++·github·php
烧冻鸡翅QAQ4 小时前
HTTP 1.0版本的webserver自主实现
网络·网络协议·http
开开心心就好4 小时前
Word转PDF工具,免费生成图片型文档
前端·网络·笔记·pdf·word·powerpoint·excel
消失的旧时光-19435 小时前
Flutter 响应式 + Clean Architecture / MVU 模式 实战指南
android·flutter·架构
你听得到115 小时前
卷不动了?我写了一个 Flutter 全链路监控 SDK,从卡顿、崩溃到性能,一次性搞定!
前端·flutter·性能优化
YUFENGSHI.LJ6 小时前
Flutter 高性能 Tab 导航:懒加载与状态保持的最佳实践
开发语言·flutter·1024程序员节
qq_479875436 小时前
网络管理中的名词
网络
想不明白的过度思考者6 小时前
TCP三次握手与四次挥手通俗理解
网络·网络协议·tcp/ip