Dio 封装网络请求

33 阅读2分钟

在 Flutter 中使用 Dio 封装网络请求时,通常需要暴露以下内容:

  1. 常用 HTTP 方法(GET/POST/PUT/DELETE)
  2. 请求配置(基础 URL、超时时间等)
  3. 错误统一处理
  4. 拦截器(日志、token 处理)
  5. 请求取消能力

以下是完整封装示例:

dart

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';

class HttpUtil {
  static final HttpUtil _instance = HttpUtil._internal();
  late Dio dio;
  final CancelToken _cancelToken = CancelToken();

  factory HttpUtil() => _instance;

  HttpUtil._internal() {
    // Base配置
    BaseOptions options = BaseOptions(
      connectTimeout: const Duration(seconds: 15),
      receiveTimeout: const Duration(seconds: 15),
      sendTimeout: const Duration(seconds: 10),
      responseType: ResponseType.json,
    );

    dio = Dio(options);

    // 添加拦截器
    dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        // 请求前添加token
        options.headers['Authorization'] = 'Bearer your_token';
        return handler.next(options);
      },
      onResponse: (response, handler) {
        // 统一处理响应数据
        if (response.data['code'] != 200) {
          return handler.reject(
            DioException(
              requestOptions: response.requestOptions,
              error: response.data['msg'],
              response: response,
            ),
          );
        }
        return handler.next(response);
      },
      onError: (DioException e, handler) async {
        // 统一错误处理
        if (e.response?.statusCode == 401) {
          // token过期处理
          // await refreshToken();
          // return _retry(e.requestOptions);
        }
        return handler.next(e);
      },
    ));

    // 调试模式下添加日志拦截器
    if (kDebugMode) {
      dio.interceptors.add(LogInterceptor(
        responseBody: true,
        requestHeader: true,
        responseHeader: false,
        request: false,
      ));
    }
  }

  /// 封装GET请求
  Future<dynamic> get(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      Response response = await dio.get(
        path,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
      );
      return response.data;
    } on DioException catch (e) {
      _handleError(e);
      rethrow;
    }
  }

  /// 封装POST请求
  Future<dynamic> post(
    String path, {
    dynamic data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      Response response = await dio.post(
        path,
        data: data,
        queryParameters: params,
        options: options,
        cancelToken: cancelToken ?? _cancelToken,
      );
      return response.data;
    } on DioException catch (e) {
      _handleError(e);
      rethrow;
    }
  }

  // 统一错误处理
  void _handleError(DioException e) {
    String errorMsg = '网络请求异常';
    if (e.type == DioExceptionType.connectionTimeout) {
      errorMsg = '连接超时';
    } else if (e.type == DioExceptionType.receiveTimeout) {
      errorMsg = '接收超时';
    } else if (e.type == DioExceptionType.sendTimeout) {
      errorMsg = '发送超时';
    } else if (e.type == DioExceptionType.badResponse) {
      errorMsg = '服务器异常:${e.response?.statusCode}';
    } else if (e.type == DioExceptionType.cancel) {
      errorMsg = '请求已取消';
    }
    debugPrint('HTTP Error: $errorMsg');
    // 这里可以添加全局错误提示,比如显示SnackBar
  }

  /// 取消所有请求
  void cancelRequests() {
    _cancelToken.cancel('手动取消请求');
  }

  /// 设置基础URL
  void setBaseUrl(String url) {
    dio.options.baseUrl = url;
  }

  /// 更新授权token
  void updateToken(String token) {
    dio.options.headers['Authorization'] = 'Bearer $token';
  }
}

使用示例:

dart

// 初始化(通常在main函数中)
final http = HttpUtil();
http.setBaseUrl('https://api.example.com/v1/');

// GET请求示例
void fetchData() async {
  try {
    final response = await http.get('user/profile');
    print(response);
  } catch (e) {
    print('请求失败: $e');
  }
}

// POST请求示例
void postData() async {
  try {
    final response = await http.post(
      'user/login',
      data: {'email': 'test@example.com', 'password': '123456'},
    );
    print(response);
  } catch (e) {
    print('登录失败: $e');
  }
}

// 取消请求示例
CancelToken cancelToken = CancelToken();
http.get('big-data', cancelToken: cancelToken).catchError((e){
  if (CancelToken.isCancel(e)) {
    print('请求被取消: ${e.message}');
  }
});

// 需要取消时调用
cancelToken.cancel('用户主动取消');

主要封装特点:

  1. 单例模式:确保全局使用同一个Dio实例

  2. 统一配置:超时时间、基础URL等集中管理

  3. 拦截器链

    • 自动添加认证Token
    • 响应状态码统一处理
    • 调试日志输出
  4. 错误处理

    • 网络超时分类处理
    • 服务器错误代码处理
    • 请求取消处理
  5. 扩展能力

    • 支持自定义取消Token
    • 动态更新基础URL
    • 方便添加其他HTTP方法(PUT/DELETE等)

可以根据实际需求添加以下功能:

  • 文件上传/下载进度回调
  • 请求重试机制
  • 更复杂的缓存策略
  • 连接状态监测
  • 响应数据自动序列化(配合json_serializable)