Flutter for OpenHarmony 电子合同签署App实战 - API集成实现
API集成系统设计与实现 本文详细介绍了如何构建一个功能完整的API集成系统。系统采用Dio作为HTTP客户端,通过单例模式确保全局唯一实例,并配置了基础URL、超时时间等参数。核心设计包括: 安全通信:通过拦截器自动添加认证令牌 错误处理:统一处理网络错误和服务器响应 性能优化:实现请求缓存和日志记录 服务封装:创建API服务类分离业务逻辑 系统实现了请求拦截、响应处理、日志记录等功能,提供可靠
API集成是应用与后端服务通信的关键。这个功能需要提供安全的API调用机制、错误处理、请求拦截和缓存管理。在这篇文章中,我们将详细讲解如何实现一个功能完整、高效可靠的API集成系统。
API集成的设计目标
API集成功能需要实现以下设计目标:
- 安全的通信:使用HTTPS加密通信,添加认证信息,确保数据传输的安全性
- 错误处理:完善的错误处理机制,包括网络错误、服务器错误、超时等各种情况
- 请求拦截:使用拦截器添加认证、日志记录等功能,统一管理所有请求
- 缓存机制:缓存API响应,减少不必要的网络请求,提高应用性能
- 超时设置:设置合理的超时时间,防止请求无限期等待
- 重试机制:失败请求自动重试,提高可靠性
- 请求队列:管理并发请求,避免过多的同时请求
HTTP客户端的选择与配置
在实际应用中,我们使用dio包来进行HTTP请求。dio包提供了更多的功能,比如拦截器、超时设置、请求取消等。相比原生的http包,dio提供了更强大的功能和更灵活的配置选项。首先,我们需要在pubspec.yaml中添加依赖。这样可以确保我们能够使用dio包提供的所有功能。
首先在pubspec.yaml中添加依赖。这一步是API集成的基础,我们需要引入必要的第三方库来支持HTTP通信、屏幕适配和状态管理。dio是一个功能强大的HTTP客户端库,提供了拦截器、超时设置、请求取消等高级功能。flutter_screenutil用于处理不同屏幕尺寸的适配,确保应用在各种设备上都能正常显示。get是一个轻量级的状态管理和路由框架,可以简化应用的状态管理逻辑。通过这些包的组合,我们可以构建一个完整、高效的API集成系统。
dependencies:
dio: ^5.0.0
flutter_screenutil: ^5.9.0
get: ^4.6.0
这些依赖包括了HTTP客户端、屏幕适配和状态管理框架。通过这些包,我们可以构建一个完整的API集成系统。
API客户端的单例模式实现
为了确保全局只有一个Dio实例,我们使用单例模式来创建API客户端。单例模式是一种经典的设计模式,确保一个类在整个应用生命周期中只有一个实例。这样做的好处是可以共享连接池,提高HTTP请求的性能,同时也便于管理全局的拦截器和配置。通过单例模式,我们避免了创建多个HTTP客户端实例导致的资源浪费。在API集成中,使用单例模式可以确保所有的HTTP请求都使用同一个连接池,从而提高网络通信的效率和稳定性。
class ApiClient {
static final ApiClient _instance = ApiClient._internal();
late Dio _dio;
factory ApiClient() {
return _instance;
}
ApiClient._internal() {
_initDio();
}
}
这个单例模式确保了全局只有一个Dio实例。通过工厂构造函数,我们可以在任何地方获取同一个实例。这样可以确保所有的HTTP请求都使用同一个连接池,提高性能。
Dio的初始化配置
Dio的初始化是API集成的第一步,也是最关键的一步。我们需要配置基础URL、超时时间、内容类型等参数。这些配置对所有请求都适用,避免了在每个请求中重复配置。通过BaseOptions,我们可以设置所有请求的默认选项,包括连接超时、接收超时和发送超时。合理的超时设置可以防止请求无限期地等待,提高应用的响应性。这样可以确保所有请求都有一致的配置,并且能够在网络不稳定的情况下快速失败并重试。
void _initDio() {
_dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
sendTimeout: const Duration(seconds: 30),
contentType: 'application/json',
responseType: ResponseType.json,
),
);
}
这个配置设置了基础URL、超时时间和内容类型。通过这些配置,我们可以确保所有请求都有一致的行为。超时时间的设置可以防止请求无限期地等待。
请求拦截器的实现
请求拦截器是API集成中非常重要的一部分,它允许我们在每个请求发送前进行统一的处理。通过拦截器,我们可以自动添加认证令牌、请求ID、自定义请求头等信息,确保所有请求都包含必要的元数据。这样可以避免在每个API调用中重复添加这些信息,提高代码的可维护性。请求拦截器还可以用于请求日志记录、请求参数验证等功能。通过拦截器,我们可以在一个地方管理所有请求的共同逻辑,实现关注点分离的设计原则。
_dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
final token = _getAuthToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
options.headers['X-Request-ID'] = _generateRequestId();
return handler.next(options);
},
),
);
在请求拦截器中,我们首先获取存储的认证令牌,然后将其添加到请求头中。这样每个请求都会自动包含认证信息,无需在每个API调用中重复添加。同时,我们生成一个唯一的请求ID,用于在日志中追踪请求。
响应拦截器和错误处理
响应拦截器用于处理API响应,是API集成中的关键环节。我们可以在这里检查响应状态码,处理错误响应,或者对响应数据进行统一的处理。响应拦截器是处理API响应的关键部分,它允许我们在一个地方处理所有响应,避免在每个API调用中重复处理。通过拦截器,我们可以实现统一的错误处理策略、响应数据转换、日志记录等功能。这样可以提高代码的可维护性和一致性,同时也便于调试和监控应用的API调用。
_dio.interceptors.add(
InterceptorsWrapper(
onResponse: (response, handler) {
if (kDebugMode) {
print('Response: ${response.statusCode}');
}
return handler.next(response);
},
onError: (error, handler) {
if (kDebugMode) {
print('Error: ${error.message}');
}
return handler.next(error);
},
),
);
在响应拦截器中,我们记录响应状态码和错误信息。这对于调试和监控应用的API调用非常有帮助。通过日志,我们可以快速定位问题所在。
日志拦截器的添加
为了更好地调试和监控API调用,我们添加一个日志拦截器。这个拦截器会记录所有请求和响应的详细信息,包括请求头、请求体、响应体等。日志拦截器对于调试API问题非常有用,通过详细的日志信息,我们可以快速定位问题所在。在开发阶段,我们可以启用详细的日志记录,在生产环境中,我们可以根据需要调整日志级别,以避免过多的日志输出影响应用性能。通过日志,我们可以看到请求的详细信息、响应数据和错误信息,这对于性能分析和问题诊断非常有帮助。
_dio.interceptors.add(
LogInterceptor(
requestBody: true,
responseBody: true,
error: true,
logPrint: (object) {
if (kDebugMode) {
print(object);
}
},
),
);
日志拦截器会记录请求体、响应体和错误信息。这对于调试API问题非常有用。在生产环境中,我们可以根据需要调整日志级别。
API服务类的设计
现在让我们创建一个API服务类来处理具体的API调用。这个类会封装所有与合同相关的API操作,提供一个清晰的接口供应用的其他部分使用。API服务类是应用与后端API之间的桥梁,通过API服务类,我们可以将API调用逻辑与UI逻辑分离。这样可以使代码更加清晰和易于维护,同时也便于单元测试和代码复用。API服务类还可以处理数据转换、错误处理等逻辑,提供一个统一的数据访问接口。
class ContractApiService {
final ApiClient _apiClient = ApiClient();
Future<List<ContractModel>> getContracts({
int page = 1,
int pageSize = 20,
String? status,
}) async {
try {
final response = await _apiClient.dio.get(
'/contracts',
queryParameters: {
'page': page,
'pageSize': pageSize,
if (status != null) 'status': status,
},
);
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
final contracts = (data['data'] as List)
.map((item) => ContractModel.fromJson(item))
.toList();
return contracts;
} else {
throw ApiException(
message: 'Failed to fetch contracts',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
}
这个方法展示了如何获取合同列表。我们使用查询参数来支持分页和过滤。如果响应状态码是200,我们解析响应数据并返回合同列表。否则,我们抛出一个自定义异常。
获取单个合同详情
获取单个合同的详情是一个常见的操作,通常用于显示合同的详细页面。我们需要根据合同ID来获取详细信息,包括合同的完整内容、签署状态、参与者信息等。通过合同ID,我们可以从服务器获取该合同的所有信息,这样可以确保用户看到的是最新的合同信息。这个操作使用RESTful风格的URL设计,将ID作为路径参数,这样的设计更加清晰和易于理解。通过单个合同详情的获取,我们可以为用户提供完整的合同信息展示。
Future<ContractModel> getContractDetail(String contractId) async {
try {
final response = await _apiClient.dio.get(
'/contracts/$contractId'
);
if (response.statusCode == 200) {
return ContractModel.fromJson(response.data);
} else {
throw ApiException(
message: 'Failed to fetch contract detail',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
这个方法通过合同ID来获取详细信息。我们使用RESTful风格的URL,将ID作为路径参数。这样的设计更加清晰和易于理解。
创建合同的API调用
创建合同是应用中的重要操作,需要发送POST请求来完成。创建合同时,我们需要发送合同的基本信息到服务器,包括合同标题、内容、参与者等信息。服务器会验证这些信息,然后创建新的合同。如果创建成功,服务器会返回201状态码和新创建的合同信息。这个操作涉及到数据验证、错误处理等重要逻辑。通过创建合同的API,我们可以为用户提供完整的合同创建功能,支持用户快速生成新的合同。
Future<ContractModel> createContract(
ContractCreateRequest request
) async {
try {
final response = await _apiClient.dio.post(
'/contracts',
data: request.toJson(),
);
if (response.statusCode == 201) {
return ContractModel.fromJson(response.data);
} else {
throw ApiException(
message: 'Failed to create contract',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
创建合同时,我们发送POST请求,并将请求数据作为JSON体发送。如果创建成功,服务器会返回201状态码和新创建的合同信息。
更新合同的API调用
更新合同是修改现有合同信息的操作,需要发送PUT请求来完成。更新合同时,我们需要发送修改后的合同信息到服务器,包括更新的标题、内容、参与者等信息。服务器会验证这些信息,然后更新合同。如果更新成功,服务器会返回200状态码和更新后的合同信息。这个操作需要确保只有有权限的用户才能更新合同,同时需要处理并发更新的情况。通过更新合同的API,我们可以为用户提供修改合同信息的功能。
Future<ContractModel> updateContract(
String contractId,
ContractUpdateRequest request,
) async {
try {
final response = await _apiClient.dio.put(
'/contracts/$contractId',
data: request.toJson(),
);
if (response.statusCode == 200) {
return ContractModel.fromJson(response.data);
} else {
throw ApiException(
message: 'Failed to update contract',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
更新合同时,我们发送PUT请求,并将修改后的数据作为JSON体发送。如果更新成功,服务器会返回200状态码和更新后的合同信息。
删除合同的API调用
删除合同是一个敏感的操作,需要谨慎处理。我们使用DELETE请求来删除合同。删除操作通常返回204(No Content)或200(OK)状态码。我们检查响应状态码,如果不是这两个之一,就抛出异常。这样可以确保删除操作的安全性。
Future<void> deleteContract(String contractId) async {
try {
final response = await _apiClient.dio.delete(
'/contracts/$contractId'
);
if (response.statusCode != 204 && response.statusCode != 200) {
throw ApiException(
message: 'Failed to delete contract',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
删除操作通常返回204(No Content)或200(OK)状态码。我们检查响应状态码,如果不是这两个之一,就抛出异常。
签署合同的API调用
签署合同是应用的核心功能。我们需要发送签名数据到服务器。签署合同时,我们发送签名数据到服务器。服务器会验证签名并更新合同状态。这是一个关键的操作,需要确保数据的完整性和安全性。
Future<void> signContract(
String contractId,
SignatureData signature
) async {
try {
final response = await _apiClient.dio.post(
'/contracts/$contractId/sign',
data: signature.toJson(),
);
if (response.statusCode != 200) {
throw ApiException(
message: 'Failed to sign contract',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw _handleDioException(e);
}
}
签署合同时,我们发送签名数据到服务器。服务器会验证签名并更新合同状态。
下载合同文件
下载合同文件是一个常见的需求。我们需要处理文件下载和进度跟踪。下载文件时,我们使用download方法而不是get方法。这样可以更有效地处理大文件。我们还提供了进度回调,允许UI显示下载进度。
Future<void> downloadContract(
String contractId,
String savePath
) async {
try {
await _apiClient.dio.download(
'/contracts/$contractId/download',
savePath,
onReceiveProgress: (received, total) {
if (total != -1) {
final progress = (received / total * 100);
print('Download progress: ${progress.toStringAsFixed(0)}%');
}
},
);
} on DioException catch (e) {
throw _handleDioException(e);
}
}
下载文件时,我们使用download方法而不是get方法。这样可以更有效地处理大文件。我们还提供了进度回调,允许UI显示下载进度。
错误处理机制
完善的错误处理是API集成的重要部分。我们需要将不同类型的错误转换为有意义的异常。错误处理机制可以帮助我们快速定位问题。通过将不同类型的错误转换为自定义异常,我们可以在应用的不同地方统一处理错误。
ApiException _handleDioException(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return ApiException(message: 'Connection timeout');
case DioExceptionType.sendTimeout:
return ApiException(message: 'Send timeout');
case DioExceptionType.receiveTimeout:
return ApiException(message: 'Receive timeout');
case DioExceptionType.badResponse:
return ApiException(
message: e.response?.data['message'] ?? 'Server error',
statusCode: e.response?.statusCode,
);
case DioExceptionType.cancel:
return ApiException(message: 'Request cancelled');
default:
return ApiException(message: 'Unknown error');
}
}
这个方法将Dio异常转换为自定义的ApiException。这样做的好处是可以统一处理所有类型的错误,提供一致的错误信息给用户。
自定义异常类
我们定义一个自定义异常类来表示API错误。这样可以更清晰地区分API错误和其他类型的错误。自定义异常类包含错误消息和HTTP状态码。这样可以提供更详细的错误信息,帮助调试和用户理解问题。
class ApiException implements Exception {
final String message;
final int? statusCode;
ApiException({
required this.message,
this.statusCode,
});
String toString() => 'ApiException: $message (Status: $statusCode)';
}
自定义异常类包含错误消息和HTTP状态码。这样可以提供更详细的错误信息。
数据模型的定义
为了确保类型安全,我们定义了数据模型来表示API响应。数据模型提供了类型安全的方式来处理API响应。通过fromJson工厂方法,我们可以轻松地将JSON数据转换为Dart对象。这样可以避免类型错误和数据不一致的问题。
class ContractModel {
final String id;
final String title;
final String content;
final String status;
final DateTime createdDate;
final DateTime expiryDate;
ContractModel({
required this.id,
required this.title,
required this.content,
required this.status,
required this.createdDate,
required this.expiryDate,
});
factory ContractModel.fromJson(Map<String, dynamic> json) {
return ContractModel(
id: json['id'] as String,
title: json['title'] as String,
content: json['content'] as String,
status: json['status'] as String,
createdDate: DateTime.parse(json['createdDate'] as String),
expiryDate: DateTime.parse(json['expiryDate'] as String),
);
}
}
数据模型提供了类型安全的方式来处理API响应。通过fromJson工厂方法,我们可以轻松地将JSON数据转换为Dart对象。
在页面中使用API
现在让我们看看如何在页面中使用这个API服务。在页面中,我们创建API服务实例,然后调用相应的方法来获取数据。通过这样的设计,我们可以将API调用逻辑与UI逻辑分离。
class ContractListPage extends StatefulWidget {
const ContractListPage({Key? key}) : super(key: key);
State<ContractListPage> createState() => _ContractListPageState();
}
class _ContractListPageState extends State<ContractListPage> {
final ContractApiService _apiService = ContractApiService();
List<ContractModel> _contracts = [];
bool _isLoading = false;
String? _errorMessage;
void initState() {
super.initState();
_loadContracts();
}
Future<void> _loadContracts() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final contracts = await _apiService.getContracts();
setState(() {
_contracts = contracts;
_isLoading = false;
});
} on ApiException catch (e) {
setState(() {
_errorMessage = e.message;
_isLoading = false;
});
}
}
}
在页面中,我们创建API服务实例,然后在initState中调用_loadContracts方法。这个方法会获取合同列表并更新UI。
缓存机制的实现
为了提高应用的性能,我们可以实现一个简单的缓存机制。这样可以减少不必要的网络请求。缓存管理器使用单例模式,存储缓存数据和过期时间。当获取缓存时,我们检查是否已过期,如果过期则删除缓存。
class CacheManager {
static final CacheManager _instance = CacheManager._internal();
final Map<String, CacheEntry> _cache = {};
factory CacheManager() {
return _instance;
}
CacheManager._internal();
void set(String key, dynamic value, {Duration? expiration}) {
_cache[key] = CacheEntry(
value: value,
expiresAt: expiration != null
? DateTime.now().add(expiration)
: DateTime.now().add(const Duration(hours: 1)),
);
}
dynamic get(String key) {
final entry = _cache[key];
if (entry == null) return null;
if (DateTime.now().isAfter(entry.expiresAt)) {
_cache.remove(key);
return null;
}
return entry.value;
}
}
class CacheEntry {
final dynamic value;
final DateTime expiresAt;
CacheEntry({
required this.value,
required this.expiresAt,
});
}
缓存管理器提供了简单的缓存功能。通过缓存,我们可以减少不必要的网络请求,提高应用的性能。
关键功能说明
这个API集成系统包含了以下核心功能:
- 单例模式:确保全局只有一个Dio实例,节省资源
- 请求拦截:自动添加认证信息和请求ID
- 错误处理:详细的错误分类和处理
- 数据模型:使用类型安全的数据模型
- 缓存管理:减少不必要的网络请求
- 下载支持:支持文件下载和进度跟踪
设计考虑
API集成的设计需要考虑以下几个方面:
- 安全性:使用HTTPS、添加认证信息、验证SSL证书
- 可靠性:实现重试机制、超时设置、错误恢复
- 性能:使用缓存、连接池、请求队列
- 可维护性:清晰的代码结构、完善的错误处理
- 可扩展性:易于添加新的API端点和功能
总结
这个API集成系统为应用提供了一个安全、可靠、高效的后端通信机制。通过使用Dio框架、拦截器、缓存管理等技术,我们能够构建一个企业级的API集成解决方案。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)