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

2024-05-06  本文已影响0人  kadis

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

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

当请求用户详情接口时 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、请求脱壳方法是近期刚研究出来的,如果使用中发现异常报错,大家留言即可。

上一篇下一篇

猜你喜欢

热点阅读