logo

Flutter网络请求进阶:基于Dio的封装实践指南

作者:问题终结者2026.02.09 14:54浏览量:0

简介:本文详细讲解如何在Flutter项目中通过Dio库封装高性能网络工具,包含基础请求实现、自定义配置管理、拦截器机制等核心模块。通过标准化封装提升代码复用性,降低维护成本,特别适合需要统一管理网络请求的中大型项目开发场景。

一、Dio基础能力解析

Dio作为Flutter生态中最流行的HTTP客户端库,其核心优势在于支持多种请求类型、拦截器机制和全局配置管理。在开始封装前,我们需要掌握其基础用法:

1.1 环境配置与依赖管理

pubspec.yaml中添加依赖时,建议锁定次要版本号以确保稳定性:

  1. dependencies:
  2. dio: ^5.3.2 # 推荐使用稳定版本

1.2 基础请求实现

GET请求示例

  1. Future<void> fetchData() async {
  2. try {
  3. final dio = Dio();
  4. final response = await dio.get(
  5. 'https://api.example.com/posts/1',
  6. options: Options(
  7. receiveTimeout: Duration(seconds: 5),
  8. ),
  9. );
  10. print('Response: ${response.data}');
  11. } catch (e) {
  12. if (e is DioException) {
  13. print('Error type: ${e.type}');
  14. print('Error message: ${e.message}');
  15. }
  16. }
  17. }

POST请求示例

  1. Future<void> postData() async {
  2. try {
  3. final dio = Dio();
  4. final data = {
  5. 'title': 'Flutter Guide',
  6. 'body': 'Dio封装实践',
  7. 'userId': 1001
  8. };
  9. final response = await dio.post(
  10. 'https://api.example.com/posts',
  11. data: data,
  12. options: Options(
  13. contentType: Headers.formUrlEncodedContentType,
  14. ),
  15. );
  16. print('Created ID: ${response.data['id']}');
  17. } catch (e) {
  18. // 错误处理逻辑
  19. }
  20. }

二、核心封装策略

2.1 请求配置标准化

通过自定义RequestOptions实现配置集中管理:

  1. enum RequestMethod { get, post, put, delete }
  2. class NetworkConfig {
  3. static const String baseUrl = 'https://api.example.com';
  4. static const Duration connectTimeout = Duration(seconds: 10);
  5. static const Duration receiveTimeout = Duration(seconds: 8);
  6. static Map<String, String> defaultHeaders = {
  7. 'Accept': 'application/json',
  8. 'Content-Type': 'application/json',
  9. };
  10. }
  11. class CustomRequestOptions {
  12. final RequestMethod method;
  13. final String endpoint;
  14. final Map<String, dynamic>? queryParams;
  15. final Map<String, dynamic>? bodyParams;
  16. final Map<String, String>? customHeaders;
  17. CustomRequestOptions({
  18. this.method = RequestMethod.get,
  19. required this.endpoint,
  20. this.queryParams,
  21. this.bodyParams,
  22. this.customHeaders,
  23. });
  24. String get fullUrl => '${NetworkConfig.baseUrl}$endpoint';
  25. }

2.2 Dio实例封装

创建可复用的Dio实例工厂:

  1. class DioClient {
  2. final Dio _dio;
  3. DioClient._internal() : _dio = _createDioInstance();
  4. static final DioClient _instance = DioClient._internal();
  5. factory DioClient() => _instance;
  6. static Dio _createDioInstance() {
  7. final dio = Dio(BaseOptions(
  8. baseUrl: NetworkConfig.baseUrl,
  9. connectTimeout: NetworkConfig.connectTimeout,
  10. receiveTimeout: NetworkConfig.receiveTimeout,
  11. headers: NetworkConfig.defaultHeaders,
  12. ));
  13. // 添加拦截器
  14. dio.interceptors.addAll([
  15. LogInterceptor(responseBody: true), // 开发环境日志
  16. ErrorInterceptor(), // 统一错误处理
  17. RetryInterceptor(), // 自动重试机制
  18. ]);
  19. return dio;
  20. }
  21. Future<Response<T>> request<T>(CustomRequestOptions options) async {
  22. try {
  23. final requestOptions = _buildRequestOptions(options);
  24. Response response;
  25. switch (options.method) {
  26. case RequestMethod.get:
  27. response = await _dio.get(options.endpoint, queryParameters: options.queryParams);
  28. break;
  29. case RequestMethod.post:
  30. response = await _dio.post(options.endpoint, data: options.bodyParams);
  31. break;
  32. // 其他方法实现...
  33. default:
  34. throw UnsupportedError('Unsupported request method');
  35. }
  36. return _handleResponse(response);
  37. } catch (e) {
  38. _handleError(e);
  39. rethrow;
  40. }
  41. }
  42. // 其他辅助方法...
  43. }

2.3 拦截器实现

日志拦截器

  1. class LogInterceptor extends Interceptor {
  2. final bool responseBody;
  3. LogInterceptor({this.responseBody = false});
  4. @override
  5. void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
  6. print('--> ${options.method} ${options.uri}');
  7. print('Headers: ${options.headers}');
  8. print('Body: ${options.data}');
  9. super.onRequest(options, handler);
  10. }
  11. @override
  12. void onResponse(Response response, ResponseInterceptorHandler handler) {
  13. print('<-- ${response.statusCode} ${response.requestOptions?.uri}');
  14. if (responseBody) {
  15. print('Body: ${response.data}');
  16. }
  17. super.onResponse(response, handler);
  18. }
  19. }

错误处理拦截器

  1. class ErrorInterceptor extends Interceptor {
  2. @override
  3. void onError(DioException err, ErrorInterceptorHandler handler) {
  4. final errorMessage = _parseError(err);
  5. print('Network Error: $errorMessage');
  6. // 根据错误类型返回特定状态码
  7. switch (err.type) {
  8. case DioExceptionType.connectionTimeout:
  9. case DioExceptionType.receiveTimeout:
  10. // 处理超时错误
  11. break;
  12. case DioExceptionType.badResponse:
  13. // 处理服务器错误
  14. break;
  15. default:
  16. // 处理其他错误
  17. break;
  18. }
  19. super.onError(err, handler);
  20. }
  21. String _parseError(DioException err) {
  22. if (err.response != null) {
  23. return '${err.response?.statusCode}: ${err.response?.data}';
  24. }
  25. return err.message ?? 'Unknown error';
  26. }
  27. }

三、高级功能扩展

3.1 自动重试机制

  1. class RetryInterceptor extends Interceptor {
  2. static const int maxRetries = 3;
  3. static const Duration retryDelay = Duration(milliseconds: 500);
  4. @override
  5. void onError(DioException err, ErrorInterceptorHandler handler) async {
  6. if (shouldRetry(err)) {
  7. int retryCount = 0;
  8. while (retryCount < maxRetries) {
  9. try {
  10. await Future.delayed(retryDelay * (retryCount + 1));
  11. final response = await err.requestOptions?.followUp(err.response);
  12. if (response != null) {
  13. return handler.resolve(response);
  14. }
  15. } catch (e) {
  16. retryCount++;
  17. }
  18. }
  19. }
  20. handler.next(err);
  21. }
  22. bool shouldRetry(DioException err) {
  23. return err.type == DioExceptionType.connectionTimeout ||
  24. err.type == DioExceptionType.receiveTimeout ||
  25. (err.response?.statusCode == 502 ||
  26. err.response?.statusCode == 503 ||
  27. err.response?.statusCode == 504);
  28. }
  29. }

3.2 请求缓存策略

  1. class CacheInterceptor extends Interceptor {
  2. final Map<String, Response> _cache = {};
  3. final Duration maxCacheAge;
  4. CacheInterceptor({this.maxCacheAge = const Duration(minutes: 5)});
  5. @override
  6. void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
  7. final cacheKey = options.uri.toString();
  8. final cachedResponse = _cache[cacheKey];
  9. if (cachedResponse != null) {
  10. final isExpired = DateTime.now()
  11. .difference(cachedResponse.extra?['cachedAt'] ?? DateTime(0))
  12. .compareTo(maxCacheAge) > 0;
  13. if (!isExpired) {
  14. handler.resolve(cachedResponse);
  15. return;
  16. }
  17. }
  18. super.onRequest(options, handler);
  19. }
  20. @override
  21. void onResponse(Response response, ResponseInterceptorHandler handler) {
  22. final cacheKey = response.requestOptions?.uri.toString();
  23. if (response.requestOptions?.extra?['cache'] == true) {
  24. _cache[cacheKey] = response.copyWith(
  25. extra: {
  26. ...response.extra,
  27. 'cachedAt': DateTime.now(),
  28. },
  29. );
  30. }
  31. super.onResponse(response, handler);
  32. }
  33. }

四、最佳实践建议

  1. 环境区分配置:通过BuildConfig区分开发/测试/生产环境配置
  2. 统一错误处理:建立全局错误码映射表,规范错误提示
  3. 性能监控:集成网络请求耗时统计和成功率监控
  4. Mock数据支持:开发阶段通过拦截器返回本地JSON文件
  5. 取消请求管理:使用CancelToken管理页面卸载时的请求取消

完整封装后的网络工具应具备以下特性:

  • 类型安全的请求方法定义
  • 统一的错误处理机制
  • 灵活的配置覆盖能力
  • 可扩展的拦截器体系
  • 完善的日志记录系统

这种封装方式在多个中大型项目中验证有效,可显著提升网络请求模块的可维护性,特别适合需要统一管理API调用的业务场景。实际开发中可根据项目需求进一步扩展功能模块,如添加JWT认证、请求频率限制等特性。

相关文章推荐

发表评论

活动