美文网首页
Flutter 进阶:请求脱壳函数封装

Flutter 进阶:请求脱壳函数封装

作者: kadis | 来源:发表于2024-05-06 16:08 被阅读0次

    一、需求来源
    项目中有许多每次请求之后都需要做请求结果判断处理,成功或者失败;
    1、假设接口返回如下 json 结构:
    dart复制代码{
    "code": "OK",
    "errorCode": "200",
    "result": {
    "id": "555079882875867136",
    },
    "application": "yl-gcp-api",
    "traceId": "a37c641910664c08a07481242b7f0e59",
    "message": "OK"
    }

    dart复制代码- 最外层模型设定为 RootModel 层;

    • 次外层模型设定为 resultModel 层;

    当请求用户详情接口时 result 是 Map 类型,
    当请求用户列表接口时 result 是 List 类型,
    当请求用户某个操作接口时 result 是 bool 类型。

    2、实际使用问题
    1)、通过工具生成所有模型;这样通常情况下都没有太大的问题,唯一问题是你会有很多的相似 RootModel 模型;
    2)、因为某些原因,没有生成模型,直接用 Map 通过 key 对应的值判断,成功失败;繁琐且容易出错;(极其不推荐)
    二、解决问题的方法 - 请求结果脱壳
    假设我们有一个 BaseRequestAPI 请求api,可以通过次 api 获取到请求结果(Map类型返回值);
    1、使用 fetch 获取原始的字典返回值,通过 RootModel 解析做判断;
    (此时 result 对应的是 List 类型)
    dart复制代码/// 请求列表
    Future<UserRootModel> fetchUserRootModel() async {
    final api = UserListApi();
    final response = await api.fetch();
    final rootModel = UserRootModel.fromJson(response);
    return rootModel;
    }

    2、使用 fetchResult 获取字典中的 result 字段的值
    (此时 result 对应的是 Map 类型)
    dart复制代码/// 请求详情
    Future<UserDetailModel?> fetchDetail({
    String? userId,
    }) async {
    var api = UserDetailApi(
    userId: userId,
    );
    final tuple = await api.fetchResult();
    if (tuple.result == null) {
    return tuple.result;
    }
    final model = UserDetailModel.fromJson(tuple.result);
    return model;
    }

    3、使用 fetchBool 获取字典中的 result 字段的值
    (此时 result 对应的是 bool 类型)
    dart复制代码/// 请求更新信息
    Future<({bool isSuccess, String message, bool result})> fetchUserInfoUpdate({
    required String? userId,
    required String? code,
    }) async {
    final api = UserInfoUpdateApi(
    userId: userId,
    code: code,
    );
    final tuple = await api.fetchBool();
    return tuple;
    }

    4、使用 fetchModels 获取字典中的 result 字段的对应的模型
    (此时 result 对应的是 List 类型)
    dart复制代码Future<List<UserInfoModel>> requestMemberList({
    required String groupId,
    }) async {
    final api = UserListApi(groupId: groupId);
    var tuple = await api.fetchModels(
    onValue: (respone) => respone["result"],
    onModel: (e) => UserInfoModel.fromJson(e),
    );
    return tuple.result;
    }

    三、源码分享
    dart复制代码/// 请求基类
    class BaseRequestAPI {
    ///url
    String get requestURI {
    // TODO: implement requestURI
    throw UnimplementedError();
    }

    /// get/post...
    HttpMethod get requestType => HttpMethod.GET;

    /// 传参验证
    (bool, String) get validateParamsTuple => (true, "");

    /// 发起请求
    Future<Map<String, dynamic>> fetch() async {
    final response = await ...;
    return response;
    }

    /// 数据请求
    ///
    /// onResult 根据 response 返回和泛型 T 对应的值(默认值取 response["result"])
    /// defaultValue 默认值
    ///
    /// return (请求是否成功, 提示语)
    /// 备注: isSuccess == false 且 message为空一般为断网
    Future<({bool isSuccess, String message, T? result})> fetchResult<T>({
    T Function(Map<String, dynamic> response)? onResult,
    T? defaultValue,
    }) async {
    BaseRequestAPI api = this;
    final response = await api.fetch();
    if (response.isEmpty) {
    return (isSuccess: false, message: "", result: defaultValue); //断网
    }
    bool isSuccess = response["code"] == "OK";
    String message = response["message"] ?? "";
    final result = response["result"] ?? defaultValue;
    final resultNew =
    onResult?.call(response) ?? (result as T?) ?? defaultValue;
    return (isSuccess: isSuccess, message: message, result: resultNew);
    }

    /// 返回布尔值的数据请求
    ///
    /// onTrue 根据字典返回 true 的判断条件(默认判断 response["result"] 布尔值真假)
    /// hasLoading 是否展示 loading
    ///
    /// return (请求是否成功, 提示语)
    Future<({bool isSuccess, String message, bool result})> fetchBool({
    bool Function(Map<String, dynamic> response)? onTrue,
    bool defaultValue = false,
    bool hasLoading = true,
    }) async {
    if (hasLoading) {
    EasyToast.showLoading("请求中");
    }
    final tuple = await fetchResult<bool>(
    onResult: onTrue,
    defaultValue: defaultValue,
    );
    if (hasLoading) {
    EasyToast.hideLoading();
    }
    return (
    isSuccess: tuple.isSuccess,
    message: tuple.message,
    result: tuple.result ?? defaultValue,
    );
    }

    /// 返回列表类型请求接口
    ///
    /// onList 根据字典返回数组;(默认取 response["result"] 对应的数组值)
    ///
    /// return (请求是否成功, 提示语, 数组)
    Future<({bool isSuccess, String message, List<T> result})>
    fetchList<T extends Map<String, dynamic>>({
    List<T> Function(Map<String, dynamic> response)? onList,
    required List<dynamic> Function(Map<String, dynamic> response) onValue,
    List<T> defaultValue = const [],
    }) async {
    final tuple = await fetchResult<List<T>>(
    onResult: onList ??
    (response) {
    final result = onValue(response);
    return List<T>.from(result);
    },
    defaultValue: defaultValue,
    );
    return (
    isSuccess: tuple.isSuccess,
    message: tuple.message,
    result: tuple.result ?? defaultValue,
    );
    }

    /// 返回模型列表类型请求接口
    ///
    /// onList 根据字典返回数组;(默认取 response["result"] 对应的数组值)
    /// defaultValue 默认值空数组
    /// onModel 字典转模型
    ///
    /// return (请求是否成功, 提示语, 模型数组)
    /// 备注: isSuccess == false 且 message为空一般为断网
    Future<({bool isSuccess, String message, List<M> result})> fetchModels<M>({
    required List<dynamic> Function(Map<String, dynamic> response) onValue,
    List<Map<String, dynamic>> Function(Map<String, dynamic> response)? onList,
    List<Map<String, dynamic>> defaultValue = const [],
    required M Function(Map<String, dynamic> json) onModel,
    }) async {
    final tuple = await fetchList<Map<String, dynamic>>(
    onList: onList,
    onValue: onValue,
    defaultValue: defaultValue,
    );
    final list = tuple.result;
    final models = list.map(onModel).toList();
    return (isSuccess: tuple.isSuccess, message: tuple.message, result: models);
    }
    }

    总结
    1、核心思路是通过 Record(Flutter 3.10)类型作为返回值;fetchResult 中将请求成功判断封装(本文示例中 code 为 “OK” 即为请求成功),请求提示语和 result 对应的值进行返回;达到取代 RootModel 的目的;
    dart复制代码Future<({bool isSuccess, String message, T? result})>

    2、fetchResult 为一级封装函数
    fetchBool、fetchList、fetchModels 都衍生于它,如果你有其他类型后边的方法不满足,可使用此方法实现;
    3、请求脱壳方法是近期刚研究出来的,如果使用中发现异常报错,大家留言即可。

    相关文章

      网友评论

          本文标题:Flutter 进阶:请求脱壳函数封装

          本文链接:https://www.haomeiwen.com/subject/mnwlfjtx.html