Files
oneapp_docs/basic_utils/basic_network.md
2025-09-24 14:08:54 +08:00

20 KiB
Raw Permalink Blame History

Basic Network - 网络通信模块文档

模块概述

basic_network 是 OneApp 基础工具模块群中的网络通信核心模块,基于 Dio 框架封装,提供统一的 HTTP 请求接口、拦截器机制、错误处理和日志记录功能。该模块为整个应用提供标准化的网络通信能力。

基本信息

目录结构

basic_network/
├── lib/
│   ├── basic_network.dart    # 主导出文件
│   ├── common_dos.dart       # 通用数据对象
│   ├── common_dtos.dart      # 通用数据传输对象
│   └── src/                  # 源代码目录
│       ├── client/           # HTTP 客户端实现
│       ├── interceptors/     # 请求拦截器
│       ├── models/           # 数据模型
│       ├── exceptions/       # 异常定义
│       └── utils/            # 工具类
├── pubspec.yaml              # 依赖配置
└── README.md                 # 项目说明

核心功能模块

1. HTTP 客户端封装

基于真实项目的网络引擎架构,使用工厂模式和依赖注入。

网络引擎初始化 (facade.dart)

// 实际的网络引擎初始化
bool initNetwork({NetworkEngineOption? option, NetworkLogOutput? networkLog}) =>
    NetworkEngineContext().init(option: option, networkLog: networkLog);

/// 根据自定义option获取NetworkEngine
RequestApi customNetworkEngine(NetworkEngineOption option) =>
    RequestApi(NetworkEngineFactory.createBy(option));

/// 统一的请求接口
Future<T> request<T>(RequestOptions requestOptions) async =>
    NetworkEngineContext.networkEngine.request<T>(requestOptions);

/// 带回调的请求方法
Future<void> requestWithCallback<T>(
  RequestOptions requestOptions,
  RequestOnSuccessCallback<T> onSuccess,
  RequestOnErrorCallback onError,
) async {
  try {
    final res = await NetworkEngineContext.networkEngine.request<T>(
      requestOptions,
    );
    onSuccess(res);
  } on ErrorBase catch (e) {
    onError(e);
  } catch (e) {
    logger.e(e);
    onError(ErrorGlobalCommon(GlobalCommonErrorType.other, e));
  }
}

默认网络引擎配置 (common_options.dart)

// 实际的默认网络引擎选项
class DefaultNetworkEngineOption extends NetworkEngineOption {
  final Headers _headers = Headers();

  final BaseDtoResponseConvertor _baseDtoResponseConvertor =
      const DefaultBaseJsonResponseConvertor();

  final GlobalErrorBusinessFactory _globalBusinessErrorFactory =
      defaultGlobalBusinessErrorFactory;

  @override
  BaseDtoResponseConvertor get baseDtoConvertor => _baseDtoResponseConvertor;

  @override
  String get baseUrl => '';

  @override
  Headers get headers => _headers;

  @override
  List<Interceptor> get interceptors => [];

  @override
  ProxyConfig? get proxyConfig => null;

  @override
  int get receiveTimeout => 10000;

  @override
  int get retryTime => 3;

  @override
  int get sendTimeout => 10000;

  @override
  int get connectTimeout => 10000;

  @override
  SslConfig? get sslConfig => null;

  @override
  List<GlobalErrorHandler> get globalErrorHandlers => [];

  @override
  GlobalErrorBusinessFactory get globalErrorBusinessFactory =>
      _globalBusinessErrorFactory;

  @override
  bool get debuggable => false;

  @override
  String get environment => 'sit';

  @override
  List<PreRequestInterceptor> get preRequestInterceptors => [];
}

RequestApi 类实现

// 实际的请求API封装类
class RequestApi {
  RequestApi(this._engine);

  final NetworkEngine _engine;

  /// 异步请求,等待结果或抛出异常
  Future<T> request<T>(RequestOptions requestOptions) async =>
      _engine.request(requestOptions);

  /// 带回调的请求方法
  Future<void> requestWithCallback<T>(
    RequestOptions requestOptions,
    RequestOnSuccessCallback<T> onSuccess,
    RequestOnErrorCallback onError,
  ) async {
    try {
      final res = await _engine.request<T>(requestOptions);
      onSuccess(res);
    } on ErrorBase catch (e) {
      onError(e);
    } catch (e) {
      logger.e(e);
      onError(ErrorGlobalCommon(GlobalCommonErrorType.other, e));
    }
  }

  /// 文件下载,带进度回调
  Future<HttpResponse> download({
    required dynamic savePath,
    required RequestOptions options,
    ProgressCallback? onReceiveProgress,
    CancelToken? cancelToken,
  }) =>
      _engine.download(
        savePath: savePath,
        options: options,
        onReceiveProgress: onReceiveProgress,
        cancelToken: cancelToken,
      );

  /// 文件上传,带进度回调
  Future<HttpResponse> upload({
    required RequestOptions options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
  }) =>
      _engine.upload(
        options: options,
        onSendProgress: onSendProgress,
        cancelToken: cancelToken,
      );

  /// 获取当前引擎配置
  NetworkEngineOption get option => _engine.option;
}

2. 请求拦截器系统

请求拦截器

// 请求拦截器
class RequestInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // 添加公共请求头
    _addCommonHeaders(options);
    
    // 添加认证信息
    _addAuthenticationHeaders(options);
    
    // 添加设备信息
    _addDeviceInfo(options);
    
    // 请求签名
    _signRequest(options);
    
    super.onRequest(options, handler);
  }
  
  void _addCommonHeaders(RequestOptions options) {
    options.headers.addAll({
      'User-Agent': _getUserAgent(),
      'Accept-Language': _getAcceptLanguage(),
      'X-Request-ID': _generateRequestId(),
      'X-Timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
    });
  }
  
  void _addAuthenticationHeaders(RequestOptions options) {
    final token = AuthManager.instance.accessToken;
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
  }
  
  void _addDeviceInfo(RequestOptions options) {
    options.headers.addAll({
      'X-Device-ID': DeviceInfo.instance.deviceId,
      'X-App-Version': AppInfo.instance.version,
      'X-Platform': Platform.isAndroid ? 'android' : 'ios',
    });
  }
}

响应拦截器

// 响应拦截器
class ResponseInterceptor extends Interceptor {
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // 统一响应格式处理
    _processCommonResponse(response);
    
    // 更新认证状态
    _updateAuthenticationStatus(response);
    
    // 缓存响应数据
    _cacheResponseIfNeeded(response);
    
    super.onResponse(response, handler);
  }
  
  void _processCommonResponse(Response response) {
    if (response.data is Map<String, dynamic>) {
      final data = response.data as Map<String, dynamic>;
      
      // 检查业务状态码
      final code = data['code'] as int?;
      final message = data['message'] as String?;
      
      if (code != null && code != 0) {
        throw BusinessException(code, message ?? '业务处理失败');
      }
      
      // 提取实际数据
      if (data.containsKey('data')) {
        response.data = data['data'];
      }
    }
  }
}

错误拦截器

// 错误拦截器
class ErrorInterceptor extends Interceptor {
  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // 网络错误处理
    if (_isNetworkError(err)) {
      _handleNetworkError(err);
    }
    
    // 认证错误处理
    if (_isAuthenticationError(err)) {
      _handleAuthenticationError(err);
    }
    
    // 服务器错误处理
    if (_isServerError(err)) {
      _handleServerError(err);
    }
    
    // 请求重试机制
    if (_shouldRetry(err)) {
      _retryRequest(err, handler);
      return;
    }
    
    super.onError(err, handler);
  }
  
  bool _shouldRetry(DioException err) {
    // 网络超时重试
    if (err.type == DioExceptionType.connectionTimeout ||
        err.type == DioExceptionType.receiveTimeout) {
      return _getRetryCount(err) < 3;
    }
    
    // 5xx 服务器错误重试
    if (err.response?.statusCode != null &&
        err.response!.statusCode! >= 500) {
      return _getRetryCount(err) < 2;
    }
    
    return false;
  }
  
  Future<void> _retryRequest(
    DioException err,
    ErrorInterceptorHandler handler,
  ) async {
    final retryCount = _getRetryCount(err) + 1;
    final delay = Duration(seconds: retryCount * 2);
    
    await Future.delayed(delay);
    
    try {
      final options = err.requestOptions;
      options.extra['retry_count'] = retryCount;
      
      final response = await Dio().request(
        options.path,
        data: options.data,
        queryParameters: options.queryParameters,
        options: Options(
          method: options.method,
          headers: options.headers,
          extra: options.extra,
        ),
      );
      
      handler.resolve(response);
    } catch (e) {
      handler.next(err);
    }
  }
}

3. 数据传输对象 (DTOs)

通用响应模型

// 通用 API 响应模型
@freezed
class ApiResponse<T> with _$ApiResponse<T> {
  const factory ApiResponse({
    required int code,
    required String message,
    T? data,
    @JsonKey(name: 'request_id') String? requestId,
    @JsonKey(name: 'timestamp') int? timestamp,
  }) = _ApiResponse<T>;
  
  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
  ) => _$ApiResponseFromJson(json, fromJsonT);
}

// 分页响应模型
@freezed
class PagedResponse<T> with _$PagedResponse<T> {
  const factory PagedResponse({
    required List<T> items,
    required int total,
    required int page,
    required int pageSize,
    @JsonKey(name: 'has_more') required bool hasMore,
  }) = _PagedResponse<T>;
  
  factory PagedResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
  ) => _$PagedResponseFromJson(json, fromJsonT);
}

请求参数模型

// 分页请求参数
@freezed
class PageRequest with _$PageRequest {
  const factory PageRequest({
    @Default(1) int page,
    @Default(20) int pageSize,
    String? sortBy,
    @Default('desc') String sortOrder,
  }) = _PageRequest;
  
  factory PageRequest.fromJson(Map<String, dynamic> json) =>
      _$PageRequestFromJson(json);
}

// 搜索请求参数
@freezed
class SearchRequest with _$SearchRequest {
  const factory SearchRequest({
    required String keyword,
    List<String>? filters,
    @Default(1) int page,
    @Default(20) int pageSize,
  }) = _SearchRequest;
  
  factory SearchRequest.fromJson(Map<String, dynamic> json) =>
      _$SearchRequestFromJson(json);
}

4. 错误处理系统

基于真实项目的多层次错误处理架构,包含全局错误、业务错误和自定义错误。

错误基类定义 (model/error.dart)

// 实际的错误基类
abstract class ErrorBase implements Exception {
  /// 调用栈
  StackTrace? stackTrace;

  /// 错误消息
  String get errorMessage;
}

/// 全局的错误基类
abstract class ErrorGlobal extends ErrorBase {}

/// 通用全局错误
class ErrorGlobalCommon extends ErrorGlobal {
  ErrorGlobalCommon(
    this.errorType,
    this._originalCause, {
    this.httpCode,
    this.response,
    this.message,
  }) {
    stackTrace = StackTrace.current;
  }

  /// 封装的原始error
  final dynamic _originalCause;

  /// 错误类型
  final GlobalCommonErrorType errorType;

  /// http返回的值
  final int? httpCode;

  /// 返回的response
  final HttpResponse? response;

  /// 返回的消息
  late String? message;

  /// 返回原始的错误原因如DioError
  dynamic get originalCause => _originalCause;

  @override
  String get errorMessage => message ?? response?.statusMessage ?? '';

  @override
  String toString() => 'ErrorGlobalCommon{_originalCause: $_originalCause, '
      'errorType: $errorType, httpCode: $httpCode, response: $response}';
}

/// 业务错误通用基类
abstract class ErrorGlobalBusiness extends ErrorGlobal {
  /// 业务错误码
  String get businessCode;

  @override
  String get errorMessage;

  /// 错误配置
  Map<String, dynamic> get errorConfig;

  @override
  String toString() =>
      'ErrorGlobalBusiness{businessCode:$businessCode, message:$errorMessage}';
}

/// 自定义错误
class ErrorCustom extends ErrorBase {
  // 自定义错误实现
  // ...
}

全局错误处理器

// 错误工厂方法类型定义
typedef GlobalErrorBusinessFactory = ErrorGlobalBusiness? Function(
  BaseDtoModel model,
);

typedef CustomErrorFactory = ErrorCustom? Function(BaseDtoModel model);

// 全局错误处理流程
// 在网络引擎中自动调用错误处理器链
// 支持自定义全局错误处理器扩展

错误类型枚举

// 全局通用错误类型
enum GlobalCommonErrorType {
  timeout,
  noNetwork,
  serverError,
  parseError,
  other,
}

5. 缓存机制

请求缓存管理

// 网络缓存管理器
class NetworkCacheManager {
  static final Map<String, CacheItem> _cache = {};
  static const Duration defaultCacheDuration = Duration(minutes: 5);
  
  // 设置缓存
  static void setCache(
    String key,
    dynamic data, {
    Duration? duration,
  }) {
    _cache[key] = CacheItem(
      data: data,
      timestamp: DateTime.now(),
      duration: duration ?? defaultCacheDuration,
    );
  }
  
  // 获取缓存
  static T? getCache<T>(String key) {
    final item = _cache[key];
    if (item == null) return null;
    
    if (item.isExpired) {
      _cache.remove(key);
      return null;
    }
    
    return item.data as T?;
  }
  
  // 清除缓存
  static void clearCache([String? key]) {
    if (key != null) {
      _cache.remove(key);
    } else {
      _cache.clear();
    }
  }
  
  // 生成缓存键
  static String generateCacheKey(
    String path,
    Map<String, dynamic>? queryParameters,
  ) {
    final uri = Uri(path: path, queryParameters: queryParameters);
    return uri.toString();
  }
}

// 缓存项
class CacheItem {
  final dynamic data;
  final DateTime timestamp;
  final Duration duration;
  
  CacheItem({
    required this.data,
    required this.timestamp,
    required this.duration,
  });
  
  bool get isExpired => DateTime.now().difference(timestamp) > duration;
}

6. 请求配置管理

环境配置

// 网络环境配置
enum NetworkEnvironment {
  development,
  testing,
  staging,
  production,
}

class NetworkConfig {
  final String baseUrl;
  final Duration connectTimeout;
  final Duration receiveTimeout;
  final Duration sendTimeout;
  final bool enableLogging;
  final bool enableCache;
  final int maxRetries;
  
  const NetworkConfig({
    required this.baseUrl,
    this.connectTimeout = const Duration(seconds: 30),
    this.receiveTimeout = const Duration(seconds: 30),
    this.sendTimeout = const Duration(seconds: 30),
    this.enableLogging = false,
    this.enableCache = true,
    this.maxRetries = 3,
  });
  
  // 开发环境配置
  static const NetworkConfig development = NetworkConfig(
    baseUrl: 'https://dev-api.oneapp.com',
    enableLogging: true,
    enableCache: false,
  );
  
  // 测试环境配置
  static const NetworkConfig testing = NetworkConfig(
    baseUrl: 'https://test-api.oneapp.com',
    enableLogging: true,
  );
  
  // 生产环境配置
  static const NetworkConfig production = NetworkConfig(
    baseUrl: 'https://api.oneapp.com',
    enableLogging: false,
  );
  
  static NetworkConfig forEnvironment(NetworkEnvironment env) {
    switch (env) {
      case NetworkEnvironment.development:
        return development;
      case NetworkEnvironment.testing:
        return testing;
      case NetworkEnvironment.staging:
        return testing; // 使用测试环境配置
      case NetworkEnvironment.production:
        return production;
    }
  }
}

使用示例

基于实际项目的网络请求使用方法。

基本初始化和使用

// 初始化网络库
final option = DefaultNetworkEngineOption()
  ..baseUrl = 'https://api.oneapp.com'
  ..debuggable = true
  ..environment = 'production';

// 初始化默认网络引擎
initNetwork(option: option);

// 直接使用全局方法发起请求
try {
  final response = await request<UserModel>(
    RequestOptions(
      path: '/users/profile',
      method: 'GET',
    ),
  );
  print('用户信息: ${response.name}');
} on ErrorBase catch (e) {
  print('请求失败: ${e.errorMessage}');
}

使用RequestApi

// 创建自定义网络引擎
final customOption = DefaultNetworkEngineOption()
  ..baseUrl = 'https://custom-api.com'
  ..retryTime = 5
  ..connectTimeout = 15000;

final api = customNetworkEngine(customOption);

// 使用回调方式处理请求
await api.requestWithCallback<List<ArticleModel>>(
  RequestOptions(
    path: '/articles',
    method: 'GET',
    queryParameters: {'page': 1, 'limit': 20},
  ),
  (articles) {
    print('获取到 ${articles.length} 篇文章');
  },
  (error) {
    print('请求失败: ${error.errorMessage}');
    
    // 处理特定类型的错误
    if (error is ErrorGlobalBusiness) {
      print('业务错误码: ${error.businessCode}');
    }
  },
);

文件上传和下载

// 文件上传
final uploadResponse = await api.upload(
  options: RequestOptions(
    path: '/upload',
    method: 'POST',
    data: FormData.fromMap({
      'file': await MultipartFile.fromFile('/path/to/file.jpg'),
      'description': '头像上传',
    }),
  ),
  onSendProgress: (sent, total) {
    final progress = (sent / total * 100).toStringAsFixed(1);
    print('上传进度: $progress%');
  },
);

// 文件下载
await download(
  savePath: '/path/to/save/file.pdf',
  options: RequestOptions(
    path: '/download/document.pdf',
    method: 'GET',
  ),
  onReceiveProgress: (received, total) {
    final progress = (received / total * 100).toStringAsFixed(1);
    print('下载进度: $progress%');
  },
);

自定义配置示例

// 创建带有自定义拦截器的配置
class MyNetworkOption extends DefaultNetworkEngineOption {
  @override
  String get baseUrl => 'https://my-api.com';

  @override
  List<Interceptor> get interceptors => [
    MyAuthInterceptor(),
    MyLoggingInterceptor(),
  ];

  @override
  List<GlobalErrorHandler> get globalErrorHandlers => [
    MyCustomErrorHandler(),
  ];

  @override
  bool get debuggable => kDebugMode;

  @override
  int get retryTime => 3;
}

依赖管理

基于实际的pubspec.yaml配置展示真实的项目依赖关系。

核心依赖

# 实际项目依赖配置
dependencies:
  http_parser: ^4.0.2          # HTTP 解析工具
  dio: ^5.7.0                  # HTTP 客户端库(核心)
  pretty_dio_logger: ^1.3.1    # 美化的请求日志
  freezed_annotation: ^2.2.0   # 不可变类注解
  json_annotation: ^4.8.1      # JSON 序列化注解

dev_dependencies:
  json_serializable: ^6.7.0    # JSON 序列化生成器
  build_runner: ^2.4.5         # 代码生成引擎
  test: ^1.24.3                # 单元测试框架
  coverage: ^1.6.3             # 测试覆盖率工具
  freezed: ^2.3.5              # 不可变类生成器
  flutter_lints: ^5.0.0        # 代码规范检查

实际的版本信息

性能优化

网络性能优化

  • 连接池复用
  • HTTP/2 支持
  • 请求合并和批处理
  • 智能重试机制

缓存优化

  • 多级缓存策略
  • 缓存过期管理
  • 条件请求支持
  • 离线缓存机制

内存优化

  • 流式数据处理
  • 大文件分块传输
  • 及时释放响应数据
  • 内存泄漏检测

测试策略

单元测试

  • 网络服务接口测试
  • 拦截器功能测试
  • 错误处理测试
  • 缓存机制测试

集成测试

  • 端到端请求测试
  • 网络环境切换测试
  • 错误恢复测试

Mock 测试

  • 网络请求 Mock
  • 错误场景模拟
  • 性能测试

总结

basic_network 模块为 OneApp 提供了强大而灵活的网络通信能力。通过统一的接口抽象、完善的错误处理、智能的缓存机制和丰富的拦截器功能,大大简化了网络请求的开发工作,提高了应用的稳定性和用户体验。模块的设计充分考虑了可扩展性和可维护性,为大型应用的网络通信提供了坚实的基础。