yknetworking 0.1.6
yknetworking: ^0.1.6 copied to clipboard
yknetworking.
yknetworking #
Flutter/Dart 网络请求库,基于 dio 5.x 和 connectivity_plus 构建。提供统一的 HTTP 请求、拦截器链、重试、取消、上传/下载、动态头/参以及网络连通性监听。
主页与仓库:https://github.com/yykedward/ykflutternetworking
许可证:MIT
特性 #
- 直接返回
Future<T>,无需switch/when解包 —— 失败抛出YKNetworkException - 拦截器链引擎:请求/响应/错误拦截器,优先级排序,类型安全跳过
- 实例级拦截器注册,测试隔离
- 内置拦截器:动态头、动态参数、数据洗练、重定向
- 拦截器内重试 + 外部
retry()两种重试路径 CancelToken支持,返回YKCancelled<T>(safe API)- 安全 API(
safeRequest()/safeUpload()/safeDownload()/safeRetry())返回YKResult<T>密封类型供需要完全控制的场景 - 上传(
multipart/form-data)、下载(支持保存到本地) - 全局超时、基础地址、公共参数、公共头配置
- 网络连通性监听
YKNetworkConnectivity(基于connectivity_plus)
环境与依赖 #
- Dart
>=3.0.0 <4.0.0 - Flutter
dio: ^5.5.0connectivity_plus: ^6.0.5
安装 #
flutter pub add yknetworking
或在 pubspec.yaml 中添加:
dependencies:
yknetworking: ^0.1.6
快速开始 #
全局配置 #
import 'package:yknetworking/yk_networking.dart';
void setupNetworking() {
final config = YKNetworkingConfig.getInstance();
config.baseUrl = 'https://api.example.com';
config.timeOut = 30;
config.receiveTimeout = 30;
config.commHeader = {'Accept': 'application/json'};
config.commParams = {'locale': 'zh-CN'};
}
Primary API — 直接 await(推荐) #
final api = YKNetworking(
commonHeader: {'Authorization': 'Bearer token'},
);
try {
// 直接获取数据,无需 switch/when
final users = await api.get<Map>('/users', params: {'page': '1'});
final result = await api.post<Map>(
'/login',
params: {'user': 'admin', 'pwd': 'secret'},
contentType: YKNetworkingContentType.applicationJson,
);
final profile = await api.get<User>('/profile');
print(profile.name);
} on YKNetworkException catch (e) {
print('Request failed: ${e.type} - ${e.message}');
}
Safe API — 需要 retry 或完全控制时 #
final result = await api.safeRequest<User>('/profile', method: YKNetworkingMethod.get);
switch (result) {
case YKSuccess(:final data, :final statusCode):
print('OK $statusCode: $data');
case YKFailure(:final exception, :final retryContext, :final retryCount):
if (retryContext != null) {
await api.safeRetry<User>(retryContext,
headers: {'Authorization': 'Bearer refreshed'},
);
}
case YKCancelled():
print('Request cancelled');
}
拦截器 #
自定义拦截器 #
class LogInterceptor extends YKInterceptor {
@override int get priority => 100;
@override String get name => 'logger';
@override YKInterceptorType get type => YKInterceptorType.all;
@override Future<void> onRequest(YKNetworkingRequest request) async {
print('→ ${request.method.value()} ${request.path}');
}
@override Future<void> onResponse(YKInterceptorResponse response) async {
print('← ${response.statusCode}');
}
}
final api = YKNetworking()
..addInterceptor(LogInterceptor())
..addInterceptor(HandleDataInterceptor(
handler: (req, resp) {
final d = resp.data;
if (d is Map && d['code'] != 0) {
return Exception(d['message'] ?? 'business error');
}
return null;
},
));
拦截器优先级范围 #
| 范围 | 用途 |
|---|---|
| 0-99 | 库内置:请求预处理(DynamicHeader、DynamicParams) |
| 100-199 | 用户:请求拦截器 |
| 200-299 | 用户:响应拦截器 |
| 300-399 | 库内置:数据洗练(HandleData) |
| 400-499 | 用户:错误拦截器 |
| 500-599 | 库内置:重定向 |
跳过拦截器 #
// 按名称跳过
api.get<Map>('/users',
skipInterceptors: [const ByName(BuiltInInterceptors.handleData)],
);
// 按类型跳过
api.post<Map>('/login',
skipInterceptors: [const ByType<LogInterceptor>()],
);
拦截器内重试 #
class TokenRefreshInterceptor extends YKInterceptor {
@override int get priority => 400;
@override String get name => 'tokenRefresh';
@override YKInterceptorType get type => YKInterceptorType.error;
@override Future<void> onError(
YKNetworkingRequest request,
YKNetworkException error,
YKRetryHandler retry,
) async {
if (error.statusCode == 401) {
final newToken = await refreshToken();
request.commheader!['Authorization'] = 'Bearer $newToken';
await retry(); // 重新执行完整链,自动跳过自身
}
}
}
外部 retry #
final result = await api.safeRequest<User>('/profile', method: YKNetworkingMethod.get);
if (result case YKFailure(:final retryContext) when retryContext != null) {
final newToken = await refreshToken();
// throwing API:直接返回 T
final user = await api.retry<User>(retryContext,
headers: {'Authorization': 'Bearer $newToken'},
);
}
请求取消 #
final cancelToken = CancelToken();
final future = api.safeRequest<Map>('/slow', method: YKNetworkingMethod.get, cancelToken: cancelToken);
cancelToken.cancel('no longer needed');
// 返回 YKCancelled<Map>,或 throwing API 抛出 YKNetworkException(type: cancelled)
上传与下载 #
// 上传
final data = await api.upload<Map>(
'/files', 'local.png', 'local.png', 'image/png', 'file',
params: {'dir': 'images'},
progressCallBack: (sent, total) => print('${sent}/${total}'),
);
// 下载到本地
final bytes = await api.download<Uint8List>(
'https://host/file.bin',
downloadPath: '/tmp/file.bin',
progressCallBack: (received, total) => print('${received}/${total}'),
);
网络连通性 #
final connectivity = YKNetworkConnectivity.getInstance();
await connectivity.setup();
final isOnline = await connectivity.check();
在 Flutter 中可结合 ChangeNotifier 订阅 didConnect 状态变化。
生命周期 #
final api = YKNetworking()
..addInterceptor(myInterceptor);
// 完成后释放资源(取消进行中请求,dispose 所有拦截器)
api.dispose();
开发 #
flutter test # 运行测试
flutter analyze # 静态分析
API 结构 #
| 文件 | 职责 |
|---|---|
lib/yk_networking.dart |
公共 API:YKNetworking 类(throwing API + safeRequest()/safeUpload()/safeDownload()/safeRetry() 安全入口) |
lib/yk_base_networking.dart |
静态执行层:拦截器链引擎 + Dio 通信 |
lib/yk_networking_config.dart |
单例:全局 Dio 配置 |
lib/yk_networking_request.dart |
请求模型 + YKNetworkingMethod / YKNetworkingContentType |
lib/yk_result.dart |
YKResult<T> 密封类 + RetryContext |
lib/yk_interceptor.dart |
拦截器抽象类 + InterceptorRef + 内置拦截器 |
lib/yk_interceptor_response.dart |
响应包装类型 |
lib/yk_network_exception.dart |
YKNetworkException + YKErrorType |
lib/yk_network_connectivity.dart |
网络连通性监听 |