20 KiB
20 KiB
Basic Network - 网络通信模块文档
模块概述
basic_network 是 OneApp 基础工具模块群中的网络通信核心模块,基于 Dio 框架封装,提供统一的 HTTP 请求接口、拦截器机制、错误处理和日志记录功能。该模块为整个应用提供标准化的网络通信能力。
基本信息
- 模块名称: basic_network
- 版本: 0.2.3+4
- 仓库: https://gitlab-rd0.maezia.com/eziahz/oneapp/ezia-oneapp-basic-network
- Dart 版本: >=2.17.0 <4.0.0
目录结构
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 # 代码规范检查
实际的版本信息
- 模块名称: basic_network
- 版本: 0.2.3+4
- 仓库: https://gitlab-rd0.maezia.com/eziahz/oneapp/ezia-oneapp-basic-network
- Dart 版本: >=2.17.0 <4.0.0
性能优化
网络性能优化
- 连接池复用
- HTTP/2 支持
- 请求合并和批处理
- 智能重试机制
缓存优化
- 多级缓存策略
- 缓存过期管理
- 条件请求支持
- 离线缓存机制
内存优化
- 流式数据处理
- 大文件分块传输
- 及时释放响应数据
- 内存泄漏检测
测试策略
单元测试
- 网络服务接口测试
- 拦截器功能测试
- 错误处理测试
- 缓存机制测试
集成测试
- 端到端请求测试
- 网络环境切换测试
- 错误恢复测试
Mock 测试
- 网络请求 Mock
- 错误场景模拟
- 性能测试
总结
basic_network 模块为 OneApp 提供了强大而灵活的网络通信能力。通过统一的接口抽象、完善的错误处理、智能的缓存机制和丰富的拦截器功能,大大简化了网络请求的开发工作,提高了应用的稳定性和用户体验。模块的设计充分考虑了可扩展性和可维护性,为大型应用的网络通信提供了坚实的基础。