关于Flutter之Dio的缓存拦截器

#dio 网络请求
 dio: ^5.1.1
#https://github.com/llfbandit/dio_cache_interceptor
#dio 缓存拦截器(如果使用的话:cacheStore = MemCacheStore(maxSize: 10485760, maxEntrySize: 1048576);)
  dio_cache_interceptor: ^3.4.1
#dio 文件缓存拦截器(本项目使用此插件)
  dio_cache_interceptor_file_store: ^1.2.2
#dio 重试拦截器
  dio_smart_retry: ^5.0.0
#dio 日志拦截器
  pretty_dio_logger: ^1.3.1
#缓存需要的路径
  path_provider: ^2.0.14
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
import 'package:dio_smart_retry/dio_smart_retry.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';

import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' show join;

关于 dio 拦截器:https://github.com/llfbandit/dio_cache_interceptor

/// 网路请求 Dio 工具类
class DioUtils {
  late final Dio _dio;
  CacheOptions? _cacheOptions;

  /// 单例
  static DioUtils? _instance;
  factory DioUtils() => _instance ??= DioUtils._internal();
  static DioUtils get shared => _instance ??= DioUtils._internal();
  // 私有命名构造函数
  DioUtils._internal() {
    /// dio 配置
    BaseOptions baseOptions = BaseOptions(
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      sendTimeout: const Duration(seconds: 10),
      baseUrl: 'https://gateway.ngrok.i84.com.cn/',
    );

    /// 初始化 dio 实例
    _dio = Dio(baseOptions);
  }

  /// 初始化 Dio 拦截器配置
  Future dioInterceptorConfig() async {
    /// 一定要注意拦截器的顺序,第一个为缓存,如果缓存执行就不需要走下面的逻辑了,网络拦截器注意放在最后。
    _cacheOptions = await _geFileCacheOptions();

    /// 设置拦截器
    _dio
      // 缓存拦截器
      ..interceptors.add(DioCacheInterceptor(options: _cacheOptions!))
      // 重试拦截器
      ..interceptors.add(RetryInterceptor(dio: _dio))
      // 网络拦截器,发生网络请求/响应后使用
      ..interceptors.add(
        InterceptorsWrapper(
          onRequest: (request, handler) {
            // 可以添加 header 信息
            // request.headers["token"] = "";

            handler.next(request);
          },
          onResponse: (response, handler) {
            if (response.data == null) {
              response.data["data"] = jsonDecode("{}");
            }
            handler.next(response);
          },
          onError: (e, handler) {
            print("错误 error:${e.error} === ${e.message} === ${e.response?.statusCode} === ${e.type}");
          },
        ),
      )
      // 日志拦截器
      ..interceptors.add(PrettyDioLogger(
        request: true,
        requestBody: true,
        responseBody: true,
        compact: true,
        error: true,
      ));
  }
}
/// Dio 缓存拦截器配置 options
extension DioUtilsCacheExtension on DioUtils {
  Future _geFileCacheOptions() async {
    /// 缓存的文件夹名称
    // String cacheDirName = "system_env_info";

    /// 创建缓存路径
    Directory dir = await getTemporaryDirectory();
    // String path = join(dir.path, cacheDirName);
    String path = dir.path;

    print("缓存地址:$path");

    var options = CacheOptions(
      store: FileCacheStore(path),
      // store: MemCacheStore(maxSize: 10485760, maxEntrySize: 1048576),
      policy: CachePolicy.request,
      hitCacheOnErrorExcept: [401, 403],
      maxStale: const Duration(days: 7),
      priority: CachePriority.normal,
      allowPostMethod: true,
      keyBuilder: CacheOptions.defaultCacheKeyBuilder,
    );

    return options;
  }
}
/// 请求结果做一个包装,用于识别请求结果是来自于缓存还是网络
enum HttpResultType {
  success,
  failure,
  catchError,
}

/// 可根据自己的需求,定义最终返回的模型
class HttpsResult {
  final T? data;
  final String? message;
  final int code;
  final int statusCode;
  final String? statusMessage;
  final bool isCache;
  final HttpResultType resultType;
  final dynamic catchError;

  HttpsResult({
    required this.resultType,
    this.data,
    this.code = 0,
    this.message,
    this.statusCode = 0,
    this.statusMessage,
    this.isCache = false,
    this.catchError,
  });
}

以下封装外部调用的POST 请求

/// 请求扩展
extension DioRequestExtension on DioUtils {
  /// @describe POST请求
  ///
  /// @url 请求地址
  ///
  /// @fromJson 模型转换的命名构造函数
  ///
  /// @params 请求参数
  ///
  /// @useCache 是否需要缓存
  ///
  Future> post(
    String url, {
    required T Function(Map) fromJson,
    Map params = const {},
    bool useCache = false,
  }) async {
    /// 封装结果
    HttpsResult onCacheResult(Response response, bool isCache) {
      Map resultData = response.data["data"];
      int code = response.data["code"] ?? 0;
      String? message = response.data["msg"];

      if (response.statusCode == 200) {
        return HttpsResult(
          resultType: HttpResultType.success,
          data: fromJson(resultData),
          code: code,
          message: message,
          statusCode: response.statusCode ?? 0,
          statusMessage: response.statusMessage,
          isCache: isCache,
        );
      } else {
        return HttpsResult(
          resultType: HttpResultType.failure,
          data: fromJson(resultData),
          code: code,
          message: message,
          statusCode: response.statusCode ?? 0,
          statusMessage: response.statusMessage,
          isCache: isCache,
        );
      }
    }

    /// 请求
    try {
      /* 
        每次请求发生两次回调,先回调缓存的结果,再回调实际请求的结果,适用于先展示缓存数据,得到网络请求结果后再刷新页面的场景。
        没有缓存时只回调网络请求的结果,网络请求失败时只回调缓存的结果。
       */

      Options options = _cacheOptions!
          .copyWith(
            policy: useCache ? CachePolicy.refreshForceCache : CachePolicy.noCache,
          )
          .toOptions();

      final response = await _dio.post(
        url,
        data: params,
        options: options,
      );

      // 有缓存
      HttpsResult result;
      if (response.extra["@fromNetwork@"] == false) {
        // 先试用缓存
        result = onCacheResult(response, true);
        // 再请求一次
        final newResponse = await _dio.post(
          url,
          data: params,
          options: _cacheOptions!.copyWith(policy: CachePolicy.noCache).toOptions(),
        );
        if (newResponse.statusCode != 200) {
          return result;
        }
        return onCacheResult(newResponse, false);
      } else {
        // 没有缓存
        return onCacheResult(response, false);
      }
    } catch (error) {
      return HttpsResult(
        resultType: HttpResultType.catchError,
        catchError: error,
      );
    }
  }
}

使用案例:

@override
void initState() {
  /// 初始化 Dio 配置
  DioUtils.shared.dioInterceptorConfig();

  super.initState();
}

/// Dio请求
ElevatedButton(
  onPressed: () async {
    var model = await DioUtils.shared.post(
      'url',
      params: {},
      fromJson: ServerModel.fromMap,
      useCache: true,
    );
    print("结果:${model.data?.content} === ${model.resultType}");
  },
  child: Text("Dio请求 post"),
)
展开阅读全文

页面更新:2024-03-20

标签:缓存   初始化   文件夹   路径   函数   模型   发生   地址   日志   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top