Flutter中Dio Cache Interceptor无法缓存请求的问题求助
我正尝试为Flutter应用添加dio_cache_interceptor,使用的版本如下:
dio: ^5.6.0 dio_cache_interceptor: ^3.5.0
但发起请求时,请求始终发送至Web服务器,缓存似乎从未生效。
已尝试或排查的内容:
- 响应状态码为200
- 调整过CacheOptions.keyBuilder,生成的请求缓存键始终一致
- 响应头信息如下:
- etag:
- content-type: application/json;charset=utf-8
- cache-control: private, max-age=86400, stale-if-error=21600
- date: Fri, 16 Aug 2024, 09:42:46 GMT
- content-length: 2332
- x-aspnet-version: 4.0.30319
(该API由我维护,可添加自定义响应头)
- etag:
- 调整过拦截器顺序(缓存拦截器已放在首位)
- 已安装http: ^1.2.1包,但未在当前文件中使用
- 查阅过Dio Cache Interceptor官方文档,但未找到解决方法
已花费一整天排查仍无头绪,希望能得到方向指引。
精简后的代码:
import 'dart:async'; import 'package:dio/dio.dart'; import 'package:dio_cache_interceptor/dio_cache_interceptor.dart'; class WebAPIProvider { late final Dio dio; late final CacheOptions cacheOptions; /// WebAPIProvider Constructor WebAPIProvider() { // Set cache options cacheOptions = CacheOptions( store: MemCacheStore(), policy: CachePolicy.request, hitCacheOnErrorExcept: [403], maxStale: const Duration(days: 7), ); // Set base options for every request and add interceptors dio = Dio( BaseOptions( baseUrl: 'https://my.domain.com/webapi/v1', connectTimeout: const Duration(seconds: 7), receiveTimeout: const Duration(seconds: 4), responseType: ResponseType.json, ), )..interceptors.addAll([ DioCacheInterceptor( options: cacheOptions, ), InterceptorsWrapper( onRequest: onDioRequest, onResponse: onDioResponse, onError: onDioError, ), ]); } /// Fired before every Dio request /// /// @Stackoverflow: This adds the accessToken (and other stuff) to the header FutureOr<void> onDioRequest( RequestOptions options, RequestInterceptorHandler handler, ) async { // Set contentType options.headers["contentType"] = "application/json"; // Set accessToken options.headers["accessToken"] = "access-token-here"; // Continue return handler.next(options); } /// Fired at every response Dio receives /// /// @Stackoverflow: Using this for logging the reponse FutureOr<void> onDioResponse( Response response, ResponseInterceptorHandler handler, ) async { // Some logging here... // Continue return handler.next(response); } /// Fired at any error during the request /// /// @Stackoverflow: Using this for logging the error and resolve with an "empty" dataset FutureOr<void> onDioError( DioException exception, handler, ) async { // Continue with empty response return handler.resolve(Response( extra: {"exceptionType": exception.type}, requestOptions: exception.requestOptions, )); } /// 🌐 Invokes the API [endpoint] with optional [requestHeaders] and [queryParameters]. /// /// @Stackoverflow: This is the method I'm invoking, i.e. data = api.get('/my/endpoint') Future<Response> get( String endpoint, [ Map<String, String>? requestHeaders, Map<String, dynamic>? queryParameters, ]) async { // Send request Response<dynamic> response = await dio.get( endpoint, queryParameters: queryParameters, options: Options( headers: requestHeaders, ), ); // Here I would convert it into a specific type, matching my API return response; } }
补充说明:尝试将onDioRequest()改为同步方法(原本因从安全存储读取accessToken设为异步),但响应始终包含extra: {@cache_key@: always-same-key, @fromNetwork@: true}的内容。
修复请求选项覆盖缓存配置的问题
当前全局设置的CacheOptions会被get方法中传入的空Options对象覆盖,导致缓存规则未生效。修改get方法的请求代码,把全局缓存配置合并到请求选项中:Response<dynamic> response = await dio.get( endpoint, queryParameters: queryParameters, options: cacheOptions.copyWith(headers: requestHeaders).toOptions(), );强制忽略响应头缓存指令
响应头中的private指令可能干扰缓存行为,可在全局CacheOptions中添加参数,强制使用自定义的缓存规则:cacheOptions = CacheOptions( store: MemCacheStore(), policy: CachePolicy.request, hitCacheOnErrorExcept: [403], maxStale: const Duration(days: 7), overrideCacheHeaders: true, // 强制忽略响应头的缓存指令 );排查自定义拦截器的影响
暂时移除自定义的InterceptorsWrapper,测试缓存是否生效。如果生效,再逐步恢复onRequest/onResponse/onError方法,排查是否有代码修改了请求/响应参数导致缓存无法正常存储或读取。验证缓存存储状态
在请求后手动调用缓存存储的获取方法,确认缓存是否被写入:final cacheKey = cacheOptions.keyBuilder(RequestOptions(path: endpoint, baseUrl: dio.options.baseUrl)); final cachedResponse = await cacheOptions.store.get(cacheKey); print(cachedResponse); // 查看是否有缓存数据测试持久化缓存存储
MemCacheStore是内存缓存,应用重启后会丢失,可临时换成HiveCacheStore(需添加依赖dio_cache_interceptor_hive_store)测试,确认是否是存储类型的问题。
内容的提问来源于stack exchange,提问作者Boris

