1. 背景
在移动应用开发中,与后端服务器进行接口通信是常见的需求。客户端需要向服务器发送请求并获取响应数据,以便展示、更新或同步数据。然而,处理接口请求时可能会面临一些挑战和痛点。
痛点:
- 请求发起繁琐:传统的接口请求代码通常涉及到创建请求对象、设置请求参数、处理请求回调等繁琐的步骤,使得请求发起变得冗长和复杂。
- 参数管理困难:在进行接口请求时,常常需要传递一系列参数,包括URL、请求参数、查询参数等。对于大量的参数管理和传递,缺乏一种简洁和优雅的方式。
- 返回值转换繁琐:接口请求返回的是原始的JSON数据,通常需要手动将其转换为对应的数据模型,这一过程可能比较繁琐和重复。
- 异常处理不一致:在网络请求过程中可能会遇到各种异常情况,如网络连接问题、超时、服务器错误等。对于这些异常情况的处理可能存在不一致性,导致错误消息不明确或处理不及时。
针对以上痛点,我们可以通过优化客户端接口请求的最佳实践来改善开发体验和代码质量。通过简化请求发起、优化参数管理、自动转换返回值、统一异常处理等手段,可以增加开发效率、提升代码可读性,并提供更好的用户体验。
2. 简化请求发起
下面是示例代码,其中 Http 类是基于 Dio 的一个封装,包括设置默认请求头、拦截器、超时时间等配置。
import '../net/http.dart';
class HttpRequestJson {
static Future<HttpResponse<T>> fetch<T>(
String url, {
Map<String, dynamic>? params,
FromJsonMethod<T>? fromJsonMethod,
}) async {
try {
final response = await Http().get(url, queryParams: params);
final resJson = response.data as Map<String, dynamic>?;
if (resJson == null) {
return HttpResponse.error(code: 400, message: 'Failed');
}
return HttpResponse.fromJson(
resJson: resJson,
fromJsonMethod: fromJsonMethod,
);
} catch (e) {
return errorResponse(e);
}
}
static Future<HttpResponse<T>> post<T>(
String url, {
Map<String, dynamic>? params,
Map<String, dynamic>? queryParameters,
FromJsonMethod<T>? fromJsonMethod,
CancelToken? cancelToken,
}) async {
try {
final response = await Http().post(
url,
data: params,
params: queryParameters,
cancelToken: cancelToken,
);
final resJson = response.data as Map<String, dynamic>?;
if (resJson == null) {
return HttpResponse.error(code: 400, message: 'Failed');
}
return HttpResponse.fromJson(
resJson: resJson,
fromJsonMethod: fromJsonMethod,
);
} catch (e) {
return errorResponse<T>(e);
}
}
}
HttpResponse
是一个用于表示网络请求响应的类。它用于封装从服务器返回的数据,包括响应的状态码、消息和数据。
属性:
code
:表示响应的状态码,用于表示请求的执行结果。常见的状态码包括 200 表示成功,400 表示请求错误,500 表示服务器错误等。message
:表示响应的消息,用于提供对响应结果的简要描述。它可以包含一些有关响应的额外信息或错误信息。data
:表示响应的数据,它可以是任何类型的数据,如字符串、数字、对象等。具体的数据类型取决于服务器返回的内容。
方法:
fromJson
:用于从返回的 JSON 数据中解析并创建一个HttpResponse
对象。它接受一个包含响应数据的 JSON 对象以及一个可选的自定义数据解析方法。error
:用于创建一个表示错误响应的HttpResponse
对象。它接受一个错误码、错误消息和可选的错误数据。
HttpResponse<T>
是一个具有描述性泛型 T
的类,用于表示网络请求响应的数据模型。通过使用泛型,我们可以指定响应数据的具体类型,从而提供更具体和类型安全的数据访问。
在 HttpResponse<T>
中,T
表示响应数据的类型。它可以是任何有效的 Dart 类型,包括内置类型(如 int
、String
)和自定义类型(如自定义类或模型)。通过将 T
作为泛型参数传递给 HttpResponse
,我们可以在实例化时确定响应数据的类型。
例如,如果我们希望将响应数据解析为一个名为 User
的自定义类对象,我们可以使用 HttpResponse<User>
。这样,我们就可以在 HttpResponse<User>
对象中访问和操作 User
类型的数据。
使用描述性泛型 T
可以提高代码的可读性和可维护性。它使我们能够在编译时捕获类型错误,并提供更具体和精确的数据类型操作。通过使用泛型,我们可以编写更通用和灵活的代码,以适应不同类型的响应数据,并避免进行手动的类型转换和验证。
总而言之,HttpResponse<T>
中的描述性泛型 T
允许我们指定网络请求响应数据的具体类型,提供类型安全和精确的数据访问和操作,从而提高代码质量和开发效率。
3. 示例
这是使用 HttpRequestJson
的示例代码,以 User
接口为例:
class UserApis {
static Future<HttpResponse<User>> getUser(int userId) async {
final params = {'userId': userId};
final res = await HttpRequestJson.get<User>(
'api/user',
params: params,
fromJsonMethod: User.fromJson,
);
return res;
}
static Future<HttpResponse<User>> createUser(User user) async {
final res = await HttpRequestJson.post<User>(
'api/user',
params: user.toJson(),
fromJsonMethod: User.fromJson,
);
return res;
}
static Future<HttpResponse<User>> updateUser(User user) async {
final res = await HttpRequestJson.put<User>(
'api/user/${user.id}',
params: user.toJson(),
fromJsonMethod: User.fromJson,
);
return res;
}
static Future<HttpResponse<void>> deleteUser(int userId) async {
final res = await HttpRequestJson.delete<void>(
'api/user/$userId',
);
return res;
}
}
4. 接口文档转 Dart 模型类
要将接口文档转换为 Dart 模型类,您可以使用 quicktype
工具。quicktype
提供了在线使用和命令行工具两种方式。
主流的接口文档管理工具(例如 Apifox、yapi 等)都支持从接口文档导出 json-schema
数据。利用 quicktype
,我们可以将 json-schema
转换为 Dart 类,并且能够保留接口注释。
以下是转换后的 Dart 模型类示例,它包含了 fromJson()
和 toJson()
方法,我们强烈建议将复杂的请求参数也以对象的方式暴露给业务方。
您可以实现自动化地将接口文档转换为各端的模型类。为此,您需要使用 quicktype
命令行工具,并且能够从接口文档管理工具中获取指定接口的 json-schema
数据。
通过这种方式,您可以更加高效地生成和更新模型类,减少手动编写和维护的工作量,提高开发效率。
5. 结论
通过上述优化,我们成功简化了客户端接口请求的发起过程,并提供了更好的参数管理、返回值转换和异常处理机制。这些优化可以显著提高开发效率、提升代码质量,并提供更好的用户体验。
我们建议在实际开发中采用这些最佳实践,根据具体的业务需求和开发框架,适配和调整相关代码。这样可以确保代码的可读性、可维护性和可扩展性,同时提升开发效率和用户满意度。
希望本文对您有所帮助,如果您还有任何问题,请随时提问。