# http **Repository Path**: dxm_code/http ## Basic Information - **Project Name**: http - **Description**: 一个基于Dio插件封装的Flutter网络请求组件 - **Primary Language**: Dart - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-01-16 - **Last Updated**: 2026-04-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # HttpUtils > 轻量级 Dart HTTP 网络库,基于 Dio 封装,专为 Flutter 应用设计。 ## 组件简介 HttpUtils 是一个开箱即用的网络请求工具,目标是让开发者**只关心业务逻辑**,不必处理底层网络细节。 ### 核心能力 | 能力 | 说明 | |------|------| | 🎯 **类型安全** | 泛型 + 自动转换器,请求直接返回强类型对象 | | 🔄 **智能认证** | 401 自动刷新 Token 并重试,并发安全 | | 📦 **多实例** | 一个项目对接多个后端,实例间完全隔离 | | 📤 **文件操作** | 单文件/多文件上传、文件下载 + 进度回调 | | 🛡️ **统一错误处理** | 超时/断网/4xx/5xx/业务错误,全部封装为 `ApiResponse` | | 🏓 **主动心跳监测** | 5s 自定义频率探测,接口请求前秒级拦截,避免无效等待 | | 🚀 **连接复用** | 基于 HTTP HEAD 探测,自动复用连接池,性能零损耗 | | 🔍 **服务器 Ping** | 支持单次 HTTP 探测,自动处理 SSL 与代理 | ### 文件结构 ``` ├── lib/ │ ├── http_utils.dart # 核心网络请求工具 │ ├── api_response.dart # 统一响应模型 │ └── network_connections.dart # 本机网络监控工具 (可选) ``` ### 依赖 ```yaml dependencies: dio: ^5.0.0 # 以下为 network_connections 依赖 [可选] connectivity_plus: ^7.0.0 network_info_plus: ^7.0.0 uuid: ^4.5.3 shared_preferences: ^2.5.4 ``` --- ## 基础使用 > 只需 3 步即可开始使用:初始化 → 发请求 → 处理结果。 ### 第 1 步:初始化 在 `main.dart` 中初始化默认实例: ```dart void main() { HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', ); runApp(MyApp()); } ``` ### 第 2 步:发起请求 ```dart // GET final response = await HttpUtils().get('/users'); // POST final response = await HttpUtils().post('/users', data: {'name': 'John'}); ``` ### 第 3 步:处理结果 所有请求返回统一的 `ApiResponse`: ```dart if (response.isSuccess) { print('数据: ${response.data}'); } else { print('错误码: ${response.code}'); print('错误信息: ${response.message}'); } ``` **就是这么简单。** 以下是完整的功能文档。 --- ## 详细功能 ### 目录 - [1. 请求方法](#1-请求方法) - [2. 类型转换器](#2-类型转换器) - [3. Token 认证](#3-token-认证) - [4. 多实例管理](#4-多实例管理) - [5. 文件上传与下载](#5-文件上传与下载) - [6. 错误处理](#6-错误处理) - [7. 服务器连通检测 (Ping)](#7-服务器连通检测-ping) - [8. 高级配置](#8-高级配置) - [9. 最佳实践](#9-最佳实践) --- ### 1. 请求方法 #### 所有 HTTP 方法 ```dart // GET(带查询参数) await HttpUtils().get('/users', params: {'page': 1, 'size': 20}); // POST(带请求体) await HttpUtils().post('/users', data: {'name': 'John', 'age': 28}); // PUT await HttpUtils().put('/users/123', data: {'name': 'Jane'}); // DELETE await HttpUtils().delete('/users/123'); // PATCH await HttpUtils().patch('/users/123', data: {'name': 'Updated'}); ``` #### 通用参数 所有请求方法都支持以下可选参数: | 参数 | 类型 | 说明 | |------|------|------| | `params` | `Map?` | URL 查询参数 | | `data` | `dynamic` | 请求体(POST/PUT/PATCH/DELETE) | | `headers` | `Map?` | 自定义请求头 | | `requireAuth` | `bool?` | 是否需要 Token 认证(覆盖实例默认值) | | `fromJson` | `T Function(dynamic)?` | 数据转换函数(一次性,不注册) | | `cancelToken` | `CancelToken?` | 用于取消请求 | #### 表单提交 ```dart await HttpUtils().postForm('/submit', formData: { 'field1': 'value1', 'field2': 'value2', }); ``` --- ### 2. 类型转换器 类型转换器让你的请求直接返回强类型对象,而不是 `Map`。 #### 定义模型 ```dart class User { final int id; final String name; User({required this.id, required this.name}); factory User.fromJson(dynamic json) { final map = json as Map; return User(id: map['id'], name: map['name']); } } ``` #### 方式一:注册全局转换器(推荐) ```dart // 在 main() 中注册一次 HttpUtils.registerConverter(User.fromJson); // 之后所有请求自动转换 final response = await HttpUtils().get('/users/1'); User user = response.data!; // 类型安全 ✅ ``` > **自动列表支持**:注册 `User` 后,`List` 自动可用,无需额外注册。 ```dart final response = await HttpUtils().get>('/users'); List users = response.data!; // 自动转换列表 ✅ ``` #### 方式二:请求时直传 fromJson 无需提前注册,在请求时直接传入: ```dart final response = await HttpUtils().get( '/users/1', fromJson: User.fromJson, ); ``` #### 批量注册 ```dart HttpUtils.registerConverters({ User: User.fromJson, Product: Product.fromJson, Order: Order.fromJson, }); ``` #### 管理转换器 ```dart HttpUtils.removeConverter(); // 移除指定转换器 HttpUtils.clearConverters(); // 清空所有转换器 ``` --- ### 3. Token 认证 HttpUtils 内置了完整的 Token 认证体系,包括自动注入、自动刷新、并发安全。 #### 3.1 开启认证 ```dart HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', enableAuth: true, // 开启全局认证 ); ``` #### 3.2 配置 Token 获取函数 ```dart HttpUtils().onGetToken = ([bool refresh = false, HttpUtils? http]) async { if (refresh) { // 刷新 Token(当收到 401 时自动调用) final newToken = await refreshTokenFromServer(); await saveTokenToLocal(newToken); return newToken; } // 获取本地缓存的 Token(每次请求前自动调用) return await getTokenFromLocal(); }; ``` #### 3.3 自定义认证头格式 默认格式为 `{'Authorization': token}`,可自定义: ```dart HttpUtils().onGenerateAuthToken = (token) { return { 'Authorization': 'Bearer $token', 'X-Custom-Header': 'value', }; }; ``` #### 3.4 认证失败回调 当 Token 刷新失败时触发: ```dart HttpUtils.onAuthFailed = () { // 跳转登录页 Navigator.pushReplacementNamed(context, '/login'); }; ``` #### 3.5 请求级别控制 每个请求可以单独控制是否需要认证,**覆盖**实例级配置: ```dart // 不启用认证的实例,此次请求临时要求认证 await HttpUtils().get('/profile', requireAuth: true); // 启用认证的实例,此次请求跳过认证 await HttpUtils().get('/public/data', requireAuth: false); ``` #### 3.6 自动刷新机制 当请求返回 **401** 时,HttpUtils 会自动执行以下流程: ``` 请求返回 401 ↓ 调用 onGetToken(refresh: true) 刷新 Token ↓ ┌───────────────────────────────┐ │ 刷新成功 │ 刷新失败 │ ↓ │ ↓ │ 用新 Token 重试原请求 │ 触发 onAuthFailed │ 同时重试队列中其他等待的请求 │ 清空等待队列 └───────────────────────────────┘ ``` **并发安全**:多个请求同时收到 401 时,仅触发**一次**刷新,其余请求排队等待。 **防死循环**:内置最多重试 2 次限制。 #### 3.7 完整示例 ```dart void main() { // 初始化 HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', enableAuth: true, ); // Token 获取/刷新 HttpUtils().onGetToken = ([bool refresh = false, HttpUtils? http]) async { final prefs = await SharedPreferences.getInstance(); if (refresh) { final refreshToken = prefs.getString('refresh_token'); final response = await HttpUtils().post( '/auth/refresh', data: {'refreshToken': refreshToken}, requireAuth: false, // 刷新请求本身不需要认证 ); final newToken = response.data['token']; await prefs.setString('token', newToken); return newToken; } return prefs.getString('token'); }; // 认证失败处理 HttpUtils.onAuthFailed = () { Get.offAllNamed('/login'); }; runApp(MyApp()); } ``` --- ### 4. 多实例管理 适用于一个项目需要对接**多个后端服务**的场景。每个实例拥有独立的 BaseUrl、Token 状态和拦截器。 #### 创建命名实例 ```dart // 主应用 API HttpUtils.named( name: 'app', baseUrl: 'https://app-api.example.com', enableAuth: true, ); // 支付 API(独立的认证体系) HttpUtils.named( name: 'payment', baseUrl: 'https://pay-api.example.com', enableAuth: true, ); // 第三方 API(无需认证) HttpUtils.named( name: 'third_party', baseUrl: 'https://api.third-party.com', enableAuth: false, ); ``` #### 使用命名实例 ```dart final appApi = HttpUtils.getInstance('app')!; final payApi = HttpUtils.getInstance('payment')!; await appApi.get('/user/profile'); await payApi.post('/orders', data: orderData); ``` #### 为不同实例配置独立的 Token ```dart final payApi = HttpUtils.getInstance('payment')!; // 支付实例独立的 Token 获取逻辑 payApi.onGetToken = ([bool refresh = false, HttpUtils? http]) async { // ... 支付系统的 Token 逻辑 }; // 支付实例独立的认证头格式 payApi.onGenerateAuthToken = (token) { return {'Authorization': 'Bearer $token', 'X-Payment-App': 'MyApp'}; }; ``` #### 实例管理 ```dart HttpUtils.removeInstance('third_party'); // 移除指定实例 HttpUtils.clearAllInstances(); // 清除所有实例(含默认实例) HttpUtils.clearPendingRequests(); // 清理所有实例的待重试队列 ``` --- ### 5. 文件上传与下载 #### 单文件上传 ```dart final response = await HttpUtils().uploadFile( '/upload', filePath: '/path/to/image.jpg', fieldName: 'avatar', // 默认 'file' data: {'userId': '123'}, // 附加表单字段 onSendProgress: (sent, total) { print('上传进度: ${(sent / total * 100).toStringAsFixed(1)}%'); }, ); ``` #### 多文件上传 ```dart final response = await HttpUtils().uploadFiles( '/upload/batch', files: { 'photo1': '/path/to/photo1.jpg', 'photo2': '/path/to/photo2.jpg', }, data: {'albumId': '456'}, ); ``` #### 文件下载 ```dart final response = await HttpUtils().downloadFile( 'https://example.com/file.pdf', '/local/path/file.pdf', onReceiveProgress: (received, total) { if (total > 0) { print('下载进度: ${(received / total * 100).toStringAsFixed(1)}%'); } }, ); if (response.isSuccess) { print('下载完成!'); } ``` --- ### 6. 错误处理 HttpUtils 将所有错误统一封装为 `ApiResponse`,你**永远不需要 try-catch**。 #### 响应模型 ```dart class ApiResponse { final bool success; // 是否成功 final int code; // 状态码 final String message; // 消息 final T? data; // 数据 bool get isSuccess; // success == true bool get isError; // success == false bool get hasData; // data != null } ``` #### 错误码对照表 | 错误码 | 含义 | 触发场景 | |-------|------|---------| | `-102` | 设备网络未连接 | 自定义 `deviceLinkState` 检测到设备无网络 | | `-101` | 服务器无法连接 | 启用 Ping 检测后,服务器不可达 | | `-100` | BaseUrl 未设置 | 未调用 `defaultInstance()` 就发起请求 | | `-2` | 数据解析错误 | `fromJson` 转换失败 | | `-1` | 网络/未知错误 | 网络断开、未分类的异常 | | `401` | 未授权 | Token 过期(自动触发刷新) | | `403` | 无权限 | 服务器拒绝访问 | | `404` | 资源不存在 | 接口路径错误 | | `500` | 服务器错误 | 后端异常 | | `5001+` | 业务错误码 | 后端返回的自定义错误 | #### 错误消息自动翻译 HttpUtils 为常见错误提供了中文提示: | 类型 | 自动消息 | |------|---------| | 超时 | "连接超时,请检查网络后重试" | | 断网 | "网络连接失败,请检查网络设置" | | 取消 | "请求已取消" | | 404 | "请求的资源不存在" | | 500 | "服务器内部错误,请稍后重试" | | 403 | "没有访问权限" | #### 支持的后端响应格式 HttpUtils 自动适配多种常见 JSON 格式: ```json // 格式 1:success 字段 {"success": true, "code": 200, "message": "成功", "data": {...}} // 格式 2:status 字段 {"status": 200, "msg": "OK", "list": [...]} // 格式 3:code 字段 + info {"code": 200, "message": "Success", "info": {...}} ``` 数据自动从 `data` → `list` → `info` 字段中提取。 #### 取消请求 ```dart final cancelToken = CancelToken(); // 发起请求 final future = HttpUtils().get('/data', cancelToken: cancelToken); // 取消请求 cancelToken.cancel('用户取消了操作'); final response = await future; // response.isError == true, response.message == "请求已取消" ``` --- ### 7. 网络连通检测 当服务器不可用时,HTTP 请求会等待直到超时(默认 15 秒),导致 UI 长时间无响应。HttpUtils 提供两层检测机制: | 层级 | 检测方式 | 耗时 | 默认 | |------|---------|------|------| | 第 1 层 | `deviceLinkState` 设备网络状态 | 立即 | 不检测(返回 true) | | 第 2 层 | **主动心跳监测 (Heartbeat)** | **0 延迟** | 不检测(需开启) | | 第 3 层 | `pingServerState` 单次 HTTP 探测 | ≤ 1 秒 | 不检测(需开启) | #### 7.1 主动心跳监测 (Heartbeat) - 推荐 这是最优雅的方案。通过在后台启动一个定时器,定期发送轻量级的 `HEAD` 请求来更新服务器存活状态。 **优势**:业务请求无需等待,直接读取内存状态,瞬间拦截异常请求。 ```dart HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', enableHeartbeat: true, // 开启心跳 heartbeatInterval: Duration(seconds: 5), // 心跳频率 heartbeatPath: '/health', // 心跳路径 onServerStatusChanged: (isAlive) { // 状态变更回调 showToast(isAlive ? "网络恢复" : "网络连接中断"); }, ); // 手动检查状态 bool isAlive = HttpUtils().isServerAlive; ``` #### 7.2 设备网络状态检测 (deviceLinkState) 用于在请求前快速判断设备是否已连接网络,默认不检测,用户可自定义: ```dart // 使用内置的 NetworkConnections 模块 HttpUtils.deviceLinkState = () => NetworkConnections.status; // 或者使用自己的网络检测逻辑 HttpUtils.deviceLinkState = () => MyNetworkManager.isConnected; ``` #### 7.3 服务器 Ping 探测 (pingServerState) 这种方式会在**每个**请求发起前执行一次真实的探测。 初始化时开启: ```dart HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', enablePingBeforeRequest: true, ); ``` #### 7.4 工作流程 ``` 发起请求 ↓ BaseUrl 是否设置? ── 否 → 返回错误 (code: -100) ↓ 是 deviceLinkState() == true? ── 否 → 返回错误 (code: -102) ↓ 是 心跳状态是否正常? (如开启) ── 否 → **立即拦截** (code: -101) ↓ 是 enablePingBeforeRequest == true? ↓ 是 ↓ 否 HTTP HEAD 探测 直接发送请求 (2 秒超时) ↓ ┌────────────┐ │ 探测成功 │ 探测失败 → 返回错误 (code: -101) │ ↓ │ │ 发送请求 │ └────────────┘ ``` #### 7.5 支持的 URL 格式 Ping 内部使用 `Uri.parse()` 解析地址,支持所有标准格式: | 格式 | 示例 | |------|------| | HTTP + IP | `http://192.168.1.1` | | HTTPS + 域名 | `https://api.example.com` | | 自定义端口 | `http://192.168.1.1:8080` | | 带路径 | `https://api.example.com/v1` | | IPv6 | `http://[::1]:8080` | | 带认证信息 | `http://user:pass@host:8080` | --- ### 8. 高级配置 #### 初始化参数 ```dart HttpUtils.defaultInstance( baseUrl: 'https://api.example.com', // 必填:接口基础地址 enableAuth: true, // 是否启用 Token 认证(默认 false) connectTimeout: 15000, // 连接超时(毫秒) receiveTimeout: 15000, // 接收超时(毫秒) enableLogging: true, // 是否打印日志 enableHeartbeat: true, // 是否开启心跳监测 (默认 false) heartbeatInterval: Duration(seconds: 5), // 心跳间隔 onServerStatusChanged: (alive) {}, // 服务器存活状态变更回调 enablePingBeforeRequest: false, // 请求前是否单次 Ping (默认 false) ); ``` `HttpUtils.named()` 支持完全相同的参数,额外需要一个 `name`。 #### 动态修改 BaseUrl ```dart HttpUtils().setBaseUrl('https://new-api.example.com'); print(HttpUtils().baseUrl); // https://new-api.example.com ``` #### 自定义拦截器 ```dart class LoggingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { print('→ ${options.method} ${options.uri}'); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { print('← ${response.statusCode} ${response.requestOptions.uri}'); handler.next(response); } } HttpUtils.defaultInstance( baseUrl: '...', interceptors: [LoggingInterceptor()], ); ``` #### 直接访问 Dio 实例 用于高级自定义或测试: ```dart HttpUtils().dio.options.headers['X-Custom'] = 'value'; ``` #### 环境切换 ```dart abstract class AppConfig { static const String baseUrl = String.fromEnvironment( 'API_URL', defaultValue: 'https://api.example.com', ); } void main() { HttpUtils.defaultInstance(baseUrl: AppConfig.baseUrl); runApp(MyApp()); } ``` 运行时指定: ```bash flutter run --dart-define=API_URL=https://test-api.example.com ``` --- ### 9. 最佳实践 #### Repository 模式 将网络请求封装在 Repository 中,隔离业务逻辑: ```dart class UserRepository { Future> getUsers() async { final response = await HttpUtils().get>('/users'); if (response.isSuccess) { return response.data ?? []; } throw Exception(response.message); } Future createUser(String name) async { final response = await HttpUtils().post( '/users', data: {'name': name}, fromJson: User.fromJson, ); if (response.isSuccess) { return response.data!; } throw Exception(response.message); } } ``` #### 统一错误处理工具 ```dart Future safeRequest( Future> Function() request, ) async { final response = await request(); if (response.isSuccess) { return response.data; } else { showToast(response.message); return null; } } // 使用 final users = await safeRequest( () => HttpUtils().get>('/users'), ); ``` #### 实例资源清理 如果使用了心跳监测,在不再需要实例时建议手动清理,防止定时器泄露: ```dart void onClose() { HttpUtils().dispose(); // 停止心跳并关闭 Dio 实例 // 或者只停止心跳 HttpUtils().stopHeartbeat(); } ``` #### 退出登录时清理 ```dart void logout() { HttpUtils.clearPendingRequests(); // 清理待重试的请求 HttpUtils.clearAllInstances(); // 移除所有实例并自动停止心跳 } ``` --- ## 许可证 MIT License