Skip to content

网络层设计与使用

Flu CLI 提供了一个开箱即用、高度封装的网络层(基于 Dio)。它不仅仅是一个 HTTP 客户端,更是一套包含拦截器、异常处理、Mock 数据降级和响应标准化的完整解决方案。

🏗️ 架构设计

网络层位于 lib/core/network,核心组件如下:

  1. AppHttp (单例): 核心客户端,负责 Dio 实例的创建和配置。
  2. AppResponse: 统一响应体,所有接口返回的数据都会被标准化为结构化对象。
  3. Interceptors:
    • AuthInterceptor: 自动处理 Token 注入。
    • LogInterceptor: 请求和响应日志打印。
    • ErrorInterceptor: 统一错误处理,将 HTTP 错误转化为友好的业务异常。
  4. ResponseAdapter: 适配器模式,用于处理不同后端接口的 JSON 结构差异。

🚀 快速上手

1. 配置 BaseUrl

lib/app.dartMainViewModel 初始化时配置:

dart
// 在应用启动时配置
AppConfig.I.init(
  baseUrl: 'https://api.example.com',
  connectTimeout: 30000,
);

2. 编写 Service

使用 serviceNetwork 代码片段可以快速生成带网络层的 Service:

dart
import '../core/network/app_http.dart';

class ProductService {
  final AppHttp _http;

  ProductService({AppHttp? http}) : _http = http ?? AppHttp();

  /// 获取商品列表
  Future<List<Product>> getProductList(int page) async {
    // 1. 发起请求
    final response = await _http.get(
      '/products',
      queryParameters: {'page': page},
    );

    // 2. 处理响应(isSuccess 会自动判断 code == 200)
    if (response.isSuccess) {
      final list = response.data as List;
      return list.map((e) => Product.fromJson(e)).toList();
    }

    // 3. 错误处理(可选,上层 ViewModel 会捕获异常)
    return [];
  }
}

3. 在 ViewModel 中调用

dart
class ProductListViewModel extends BaseListViewModel<Product> {
  final ProductService _service = ProductService();

  @override
  Future<List<Product>> fetchPage(int page) async {
    // Service 内部已经处理了异常,这里只需关注业务数据
    return await _service.getProductList(page);
  }
}

🧩 核心特性详解

1. 统一响应体 (AppResponse)

后端接口千奇百怪,AppResponse 帮你统一成标准格式:

dart
class AppResponse<T> {
  final bool isSuccess; // 是否成功 (code == 200)
  final int code;       // 业务状态码
  final String message; // 提示信息
  final T? data;        // 业务数据 (泛型)
}

2. Mock 数据降级

在开发阶段后端接口未就绪时,可以开启 Mock 模式:

dart
// 开启 Mock 模式
AppConfig.I.useMockData = true;

在 Service 中支持 Mock 降级:

dart
Future<List<Product>> getProductList() async {
  // 如果开启了 Mock,优先返回 Mock 数据
  if (AppConfig.I.useMockData) {
    return [
        Product(id: '1', name: 'Mock商品 A'),
        Product(id: '2', name: 'Mock商品 B'),
    ];
  }

  // 这行代码在 Mock 模式下不会执行
  final response = await _http.get('/products');
  // ...
}

3. 多环境适配

Flu CLI 模板默认支持多环境配置。你可以创建不同的 Config 类:

dart
// dev_config.dart
const baseUrl = 'https://dev-api.example.com';

// prod_config.dart
const baseUrl = 'https://api.example.com';

🔌 适配不同后端

如果你的后端返回结构不是标准的 { code, msg, data },你可以通过自定义 ResponseAdapter 来适配:

dart
// 默认适配器 (DefaultResponseAdapter)
@override
AppResponse<T> adapt(dynamic data) {
    return AppResponse(
        code: data['code'],
        message: data['msg'],
        data: data['data'],
    );
}

你只需要修改 lib/core/network/response_adapter.dart 中的适配逻辑即可。

❓ 常见问题

Q: 如何处理 Token 过期? A: AuthInterceptor 中预留了 401 错误的处理逻辑,你可以在那里添加跳转登录页或刷新 Token 的代码。

Q: 为什么推荐注入 AppHttp? A: ProductService({AppHttp? http}) 这种写法支持依赖注入,方便在编写单元测试时注入 Mock 的 AppHttp 实例。

Q: 是否支持 WebSocket? A: AppHttp 专注于 RESTful API。如需 WebSocket,建议在 lib/core/network 下单独创建一个 SocketClient 类。

Released under the MIT License.