Giter Club home page Giter Club logo

dio_cache_interceptor's People

Contributors

arbile26 avatar clragon avatar cmengler avatar imrhk avatar live9080 avatar llfbandit avatar murat-ti avatar simonkimi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

dio_cache_interceptor's Issues

onError is throwing an exception when the response is expired, cached and encrypted

We're running into an interesting scenario. We're using a CacheCipher to encrypt/decrypt our json responses. We've hit a scenario where a call will hit the onError function within the dio_cache_interceptor, where we have an expired cached response and returnResponse flag is determined to be true (304). Then, at the end of the call to _getResponse(...), it's trying to deserialize the content from an encrypted cached response without decrypting the body or headers, just after updating the cache store, in updatedCache.toResponse(request, fromNetwork: true). As we're using json, it's trying to convert utf into a string, but as it's still encrypted, it throws a format exception.

Error: FormatException: Missing extension byte (at offset 2) Utf8Codec.decode dart:convert
$.deserializeContent content_serialization.dart:31
CacheResponse.toResponse cache_response.dart:70
DioCacheInterceptor._getResponse dio_cache_interceptor.dart:301
DioCacheInterceptor.onError dio_cache_interceptor.dart:125

We were also using a cache policy of request, hence the expired requirement, but suspect it should also fail for any of the refresh policies / scenarios that has a cached response but can call onError.

Version 3.0.1 / Dio 4.0.0

Otherwise, it's been fairly robust for what we've done so far. Nice work.

export cache_cipher.dart

I can't use CacheChpher class, the file 'model/cache_cipher.dart' isn't exported.
Did I miss anything?

Hive & Moor support should move to a different package

Right now the package includes hive & moor even if you only want to use file based caching.

We should have 2 extra packages that could be imported manually.

dio_cach_interceptor_moor_store
dio_cach_interceptor_hive_store

THIS IS A BREAKING CHANGE

'GeneratedTextColumn' Not found

I am sure why exactly but this is the error I get trying to run this plugin:

  • 'DioCache' is from 'package:dio_cache_interceptor_db_store/src/store/database.dart' ('../../../dev/flutter/.pub-cache/hosted/pub.dartlang.org/dio_cache_interceptor_db_store-3.0.1/lib/src/store/database.dart').
    Try correcting the name to the name of an existing method, or defining a method named 'GeneratedTextColumn'.
    return GeneratedTextColumn('url', $tableName, false,
    ^^^^^^^^^^^^^^^^^^^
    Command PhaseScriptExecution failed with a nonzero exit code

[Suggestion] Use hive for persistent storage.

IMHO i think the sql+moor adds unnecessary complexity to the caching routine. Looking in the source repo i see that you mostly do single key search in the sqllite db to set/retrieve the cached response, for this particular use case hive is superior in all aspects. Also hive works on web+mobile+desktop with no additional dependencies.

Index out of range: no indices are valid: 0

  Future<DioCacheInterceptor> provideDioCacheInterceptor() async {
    final appDocDir = await getApplicationDocumentsDirectory();
    final options = CacheOptions(
      store: FileCacheStore('${appDocDir.path}/cache'),
      policy: CachePolicy.request,
      maxStale: const Duration(days: 90),
      priority: CachePriority.normal,
      keyBuilder: CacheOptions.defaultCacheKeyBuilder,
      allowPostMethod: false,
    );
    return DioCacheInterceptor(options: options);
  }

The exception:

Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: RangeError (index): Index out of range: no indices are valid: 0. Error thrown null.
       at _Int32ArrayView.[](_Int32ArrayView.java)
       at FileCacheStore._deserializeContent(FileCacheStore.java:147)
       at FileCacheStore.get(FileCacheStore.java:61)
       at DioCacheInterceptor._getCacheResponse(DioCacheInterceptor.java:270)
       at DioCacheInterceptor._getResponse(DioCacheInterceptor.java:284)
       at DioCacheInterceptor.onError(DioCacheInterceptor.java:125)
       at DioMixin.fetch._errorInterceptorWrapper.<fn>.<fn>.<fn>(<fn>.java:557)
       at DioMixin.checkIfNeedEnqueue(DioMixin.java:795)
       at DioMixin.fetch._errorInterceptorWrapper.<fn>.<fn>(<fn>.java:555)
       at _CustomZone.bindCallback.<fn>(bindCallback.java)
       at safeRun.<fn>(safeRun.java:7)

I got this error in firebase. I can not reproduce

  • samsung j6
  • huawei P9 lite
  • iPhone 8
  • iPhone 8 plus

(moor): It looks like you've created the database class DioCacheDatabase multiple times.

How to solve the below error since I am not directly adding moor to my app?

flutter: WARNING (moor): It looks like you've created the database class DioCacheDatabase multiple times. When these two databases use the same QueryExecutor, race conditions will occur and might corrupt the database.
Try to follow the advice at https://moor.simonbinder.eu/faq/#using-the-database or, if you know what you're doing, set moorRuntimeOptions.dontWarnAboutMultipleDatabases = true
Here is the stacktrace from when the database was opened a second time:
#0      GeneratedDatabase._handleInstantiated (package:moor/src/runtime/api/db_base.dart:88:30)
#1      new GeneratedDatabase (package:moor/src/runtime/api/db_base.dart:59:12)
#2      new _$DioCacheDatabase (package:dio_cache_interceptor/src/store/db_cache_store/database.g.dart:237:41)
#3      new DioCacheDatabase (package:dio_cache_interceptor/src/store/db_cache_store/database.dart:10:39)
#4      openDb (package:dio_cache_interceptor/src/store/db_cache_store/db_platform/db_os.dart:16:10)

Cache doesn't Worked with HiveStorage

Problem,

I've tried the plugin but I couldn't make it worked. Here is my configuration:

BaseOptions options = new BaseOptions(
      baseUrl: AppConfig.baseUrl,
      connectTimeout: AppConfig.connectTimeout,
      receiveTimeout: AppConfig.receiveTimeout,
    );
    // Global options
    final optionsCache = CacheOptions(
      store: HiveCacheStore(null), // Required. I set null since i've already use hive in my apps
      policy: CachePolicy
          .request, // Default. Checks cache freshness, requests otherwise and caches response.
      // hitCacheOnErrorExcept: [
      //   401,
      //   403,
      //   500,
      //   101
      // ], // Optional. Returns a cached response on error if available but for statuses 401 & 403.
      priority: CachePriority
          .normal, // Optional. Default. Allows 3 cache sets and ease cleanup.
      maxStale: const Duration(
          days:
              7), // Very optional. Overrides any HTTP directive to delete entry past this duration.
    );

    dio = new Dio(options);
    dio.interceptors.add(DioCacheInterceptor(options: optionsCache));
    `
    ``

saveResponse only 200 statusCode

First of all, thank you for providing such a nice package.

There are some inconveniences while using the package. In case of an error condition, it is saved. I think it would be good to save only successful cases.

What do you think about adding this code?

response.statusCode != null &&
        response.statusCode! >= 200 &&
        response.statusCode! < 300) {
Future<void> _saveResponse(
    Response response,
    CacheOptions cacheOptions, {
    int? statusCode,
  }) async {
    final strategy = await CacheStrategyFactory(
      request: response.requestOptions,
      response: response,
      cacheOptions: cacheOptions,
    ).compute();

    final cacheResp = strategy.cacheResponse;
    if (cacheResp != null) {
      // Store response to cache store
      await _getCacheStore(cacheOptions).set(cacheResp);

      // Update extra fields with cache info
      response.extra[CacheResponse.cacheKey] = cacheResp.key;
      response.extra[CacheResponse.fromNetwork] =
          CacheStrategyFactory.allowedStatusCodes.contains(statusCode);
    }
  }

Add option to cache to isar

Add isar cache response handler?

Disclaimer: this is probably pointless - there are already a bunch of caching options, and whatever their speed they're more than fast enough.
But isar just went stable, and it's really cool, and it has to be added to this project.
From the maker of hivedb (@leisim), a super fast nosql (unlike hive, doesn't have to load everything to memory), built on a safe rust wrapper for a fast C libmdbx

This is something which doesn't seem difficult to implement, and I have plenty of experience with hivedb and drift, so maybe something I would open a PR for, maybe after I've used isar at least a little bit in my app.

Issue with post calls with different bodies

When I do dio.post with the same url but with different body, the interceptor caches the first call and returns the same cached response for all other post call although each one of them has a different body.

[Bug] Stale cache gets never purged

Hey
so I was experimenting with the CacheOptions and forceCache and it turns out that a recent change broke the purging of stale entries.
cca456e?diff=split#diff-64d31cdb5c921267241ca3b129a926e65686a33f36c8e393776643f70dd38867L169

It seems like the problem is that once you set the global CacheOptions to have a maxStale value, the if condition mentioned above is always false and stale entries do not get purged. This still works with version 3.1.0 though.

I think the easiest way to reproduce this is add this 2 lines here:
cca456e?diff=split#diff-25cfd2bce1cd1995fee8c59ec60b98c2ba1936d3e00b046e841238c543108b83R66

    fromNetwork = resp.extra[CacheResponse.fromNetwork];
    expect(fromNetwork, isTrue);

This should ensure that the response is not coming from the expired cache.

Hope this helps

How to use normal options and cached options together?

In dio-http-cache we have something like this:

Options cacheOptions = buildCacheOptions(Duration(days: 7), options: Options(
  headers: {
    'Accept-Language': 'en',
  },
));

Response response = await Dio().get("http://www.google.com", options: cacheOptions);

I try something like this:

CacheOptions cacheOptions = CacheOptions(
  maxStale: Duration(days: 7),
  store: DbCacheStore(databasePath: 'dio_cached'),
);

DioCacheInterceptor dioCacheManager = DioCacheInterceptor(options: cacheOptions);

Options options = cacheOptions.toOptions();
option.headers = {
  'Accept-Language': 'en',
};

dio.interceptors.add(dioCacheManager);


Response response = await dio.get(
	url
	options: options,
  );

Checking if cache is being hit.

Is there a way to verify (e.g. console logs) when the cache is being hit? I have implemented the Cache interceptor with the Hive Store and all loads seem to be taking the same amount of time. Would be nice to know if im going mad or if the cache is just simply not being used.

Thanks in advance.

Flutter/ Analysis issue: The function 'FileCacheStore' isn't defined

I keep getting analysis issue: The function 'FileCacheStore' isn't defined in Android Studio. It can compile but this error is really annoying/ shown all the time in IDE.

Also when running "dart migrate" i get this error and migration fails.

Analyzing project...
[---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/]
1 analysis issue found:
error • The function 'FileCacheStore' isn't defined at lib/services/service_locator.dart:27:48 • (undefined_function)

Cannot open file.

My Implementation

  Future<DioCacheInterceptor> provideDioCacheInterceptor() async {
    final appDocDir = await getApplicationDocumentsDirectory();
    final options = CacheOptions(
      store: FileCacheStore('${appDocDir.path}/cache'),
      policy: CachePolicy.request,
      maxStale: const Duration(days: 90),
      priority: CachePriority.normal,
      keyBuilder: CacheOptions.defaultCacheKeyBuilder,
      allowPostMethod: false,
    );
    return DioCacheInterceptor(options: options);
  }

The exception

Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: FileSystemException: Cannot open file, path = '/data/user/0/package-name/app_flutter/cache/normal/f9c8f07d-eeba-5bfa-aacd-eaa76ba03fa8' (OS Error: No such file or directory, errno = 2). Error thrown null.
       at _File.readAsBytesSync(_File.java)
       at FileCacheStore._deserializeContent(FileCacheStore.java:136)
       at FileCacheStore.get(FileCacheStore.java:61)
       at DioCacheInterceptor._getCacheResponse(DioCacheInterceptor.java:270)
       at DioCacheInterceptor._getResponse(DioCacheInterceptor.java:284)
       at DioCacheInterceptor.onError(DioCacheInterceptor.java:125)
       at DioMixin.fetch._errorInterceptorWrapper.<fn>.<fn>.<fn>(<fn>.java:557)
       at DioMixin.checkIfNeedEnqueue(DioMixin.java:795)
       at DioMixin.fetch._errorInterceptorWrapper.<fn>.<fn>(<fn>.java:555)
       at _CustomZone.bindCallback.<fn>(bindCallback.java)
       at safeRun.<fn>(safeRun.java:7)

I get this error in firebase. But I can not reproduce it.
Device:

  • Oppo A9 2020

CacheControl is null and CacheResponse is always expired

Hello, I'm trying to use the library but is not working for me this is my case:

Versions:
`
dio: 4.0.0

dio_cache_interceptor: 2.0.0

sqlite3_flutter_libs: 0.4.1
`

DB setup:


static Future<DbCacheStore> _initDatabaseCacheStore() async {
    final directory = await getApplicationDocumentsDirectory();
    return DbCacheStore(
      databasePath: directory.path,
      databaseName: 'http_request_cache_db',
      logStatements: true,
    );
  }

Default cache option:
_cacheOptions = CacheOptions( store: cacheStore, policy: CachePolicy.noCache, );

Dio Interceptor
if (_cacheOptions != null) { dio..interceptors.add(DioCacheInterceptor(options: _cacheOptions)); }

Get implementation

 response = await dio.get<dynamic>(
            url,
            options: cacheTime == null
                ? null
                : _cacheOptions
                    .copyWith(
                      maxStale: cacheTime,
                      policy: CachePolicy.request,
                    )
                    .toOptions(),
          );

DB logs:


flutter: Moor: Sent SELECT * FROM DioCache WHERE cacheKey = ? LIMIT 1; with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65]
flutter: Moor: Sent SELECT * FROM DioCache WHERE cacheKey = ? LIMIT 1; with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65]
flutter: Moor: Sent INSERT OR REPLACE INTO DioCache (cacheKey, date, content, eTag, headers, maxStale, priority, responseDate, url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65, 1620377375, [91, 123, 34, 98, 114, 97, 110, 100, 104, 97, 110, 100, 108, 101, 34, 58, 34, 117, 108, 116, 97, 98, 101, 97, 117, 116, 121, 34, 44, 34, 99, 111, 109, 112, 97, 116, 105, 98, 105, 108, 105, 116, 121, 34, 58, 123, 34, 115, 99, 111, 114, 101, 34, 58, 48, 46, 53, 50, 44, 34, 98, 114, 101, 97, 107, 100, 111, 119, 110, 34, 58, 123, 34, 101, 110, 103, 97, 103, 101, 109, 101, 110, 116, 34, 58, 49, 44, 34, 102, 111, 108, 108, 111, 119, 101, 114, 34, 58, 48, 46, 53, 54, 51, 55, 50, 55, 57, 53, 55, 56, 54, 54, 48, 49, 56, 52, 44, 34, 99, 111, 110, 116, 101, 110, 116, 34, 58, 48, 125, 125, 125, 93], W/"84-HktiapgBEXHG4P4ZwdmBVVCe01Q", [123, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 91, 34, 107, 101, 101, 112, 45, 97, 108, 105, 118, 101, 34, 93, 44, 34, 120, 45, 112<…>
flutter: Moor: Sent SELECT * FROM DioCache WHERE cacheKey = ? LIMIT 1; with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65]
flutter: Moor: Sent SELECT * FROM DioCache WHERE cacheKey = ? LIMIT 1; with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65]
flutter: Moor: Sent INSERT OR REPLACE INTO DioCache (cacheKey, date, content, eTag, headers, maxStale, priority, responseDate, url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) with args [7aefd21a-827f-59b7-a3f3-554e3fc9ae65, 1620377389, [91, 123, 34, 98, 114, 97, 110, 100, 104, 97, 110, 100, 108, 101, 34, 58, 34, 117, 108, 116, 97, 98, 101, 97, 117, 116, 121, 34, 44, 34, 99, 111, 109, 112, 97, 116, 105, 98, 105, 108, 105, 116, 121, 34, 58, 123, 34, 115, 99, 111, 114, 101, 34, 58, 48, 46, 53, 50, 44, 34, 98, 114, 101, 97, 107, 100, 111, 119, 110, 34, 58, 123, 34, 101, 110, 103, 97, 103, 101, 109, 101, 110, 116, 34, 58, 49, 44, 34, 102, 111, 108, 108, 111, 119, 101, 114, 34, 58, 48, 46, 53, 54, 51, 55, 50, 55, 57, 53, 55, 56, 54, 54, 48, 49, 56, 52, 44, 34, 99, 111, 110, 116, 101, 110, 116, 34, 58, 48, 125, 125, 125, 93], W/"84-HktiapgBEXHG4P4ZwdmBVVCe01Q", [123, 34, 99, 111, 110, 110, 101, 99, 116, 105, 111, 110, 34, 58, 91, 34, 107, 101, 101, 112, 45, 97, 108, 105, 118, 101, 34, 93, 44, 34, 120, 45, 112<…>

I debugged the code and when the library check if the CacheResponse is expired using CacheResponse.isExpired() the cacheControl is always null

/// Check if cache-control fields invalidates cache entry
 /// with [date] header or [responseDate] if missing.
 ///
 /// Checking in order against:
 /// - no-cache,
 /// - max-age,
 /// - and expires header values.
 bool isExpired() {
   final cControl = cacheControl; // -> This is null
   final checkedDate = date ?? responseDate;

   if (cControl != null) {
     if (cControl.noCache ?? false) {
       return true;
     }

     final checkedMaxAge = cControl.maxAge;
     if (checkedMaxAge != null && checkedMaxAge > 0) {
       final maxDate = checkedDate.add(Duration(seconds: checkedMaxAge));
       return maxDate.isBefore(DateTime.now());
     }
   }

   final checkedExpires = expires;
   if (checkedExpires != null) {
     return checkedExpires.difference(checkedDate).isNegative;
   }

   return true;
 }
}

Caching POST requests and processing them later

Hey, there 👋

This library is great for automagically handling caching of get requests, I'm already using it for that an I'm super satisfied. But where I'm having trouble is figuring out how to cache post requests and what does this library offer for caching post requests ❓🤷‍♂️

Here's what my app needs:
When my apps users are offline they still need to be able to fill up certain forms and save them so that once the device is back online the cached POST requests should be processed and sent trough DIO.

What I already know
I'm aware that this library has allowPostMethod that can be set to true and that you have to over-ride the keyBuilder for making custom keys on POST requests.

My question
Do you have to write your own mechanism or is this library itself able to provide an automatical queue mechanism for processing cached post requests? If so, are there any examples of that?

Thanks in advance for your time and care :)

Cannot update forceCache value when there is no http directive

first, special thanks for providing this awesome package.

i'm using the following line in my app because i want to toggle between cache value or update it.

policy: forceRefresh ? CachePolicy.refresh : CachePolicy.forceCache,

The problem is that the cache value which is stored by forceCache in DBStore is never getting updated even if i set the cache policy to refresh (there is no cache directive in header).

do you have a solution?

caching not working.

I am using dio_cache_interceptor: ^2.3.1 with dio: ^4.0.0.

I download the ReadMe example and Run it on my system.

After trying multiple times my response is not cached.

Here is ss.

Simulator Screen Shot - iPhone 11 Pro - 2021-05-11 at 19 29 22

And here without the internet.

Simulator Screen Shot - iPhone 11 Pro - 2021-05-11 at 19 29 34

Here it supposes to return my cache response.

Let me know If I am missing something.

No file in cache folder

I am trying to use dio_cache_interceptor_file_store with FileCacheStore to store data into cache for each GET request.
I am using DIO with retrofit, so I imagine that both are compatible with dio_cache_interceptor.

I dont want to depends on http directives, so I am using CachePolicy.forceCache.

@RestApi(baseUrl: ApiRoutes.baseUrl)
abstract class ApiClient {

  factory ApiClient(Dio dio) {
    dio.options = BaseOptions(
      sendTimeout: 20000,
      connectTimeout: 20000,
      receiveTimeout: 20000,
      contentType: 'application/json',
    );

    pp.getTemporaryDirectory().then((dir) {
      CacheOptions _cacheOptions = CacheOptions(
        store: FileCacheStore(dir.path),
        policy: CachePolicy.forceCache,
        maxStale: const Duration(minutes: 50),
      );

      dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
    });

    return _ApiClient(dio);
  }

  @GET(ApiRoutes.foo + '/{id}/bar')
  Future<List<FooBar>> getFooBar(
    @Path() String foo, {
    @Header('Range') String? bar = '',
  });
}

And then calling getFooBar from my repository

class FooBarRepository {
  late Dio _dio;
  late ApiClient _userClient;

  FooBarRepository() {
    _dio = Dio();
    _userClient = ApiClient(_dio);
  }

  Future<List<FooBar>?> getFooBar({
    String? id = '00000000-0000-0000-0000-000000000000',
  }) async {
    List<FooBar> response;
    try {
      response = await _userClient.getFooBar(id!);
    } on DioError catch (error) {
      return Future.value(null);
    }

    return response;
  }
}

My device is rooted so when I receive the data from the server I am able to see if the cache folder is empty or not, and unfortunately it is always empty.
Status code 200 -> I receive the data without problem
Status code 403 -> Nothing is loaded from the cache

But this is not working like I expected, so I dont know if I am missing something from the documentation.

Thank you for working on this

I typically don't comment much, but just want to say, thank you for building this. This was much needed, as the other http_cache package for dio was becoming very slow to upgrade. I am glad you guys are using multiple different cache options, making it available for any platform. Really amazed. Can't wait to see if I can contribute to this in any way. Thank you

Connect timeout will return expire cache data

Hi,

I encounter the issue that it will return the expire cache data when the http request connect timeout, it intercepted the connect timeout exception handling logic. Can you consider adding the connect timeout case to the options.hitCacheOnErrorExcept? Let us decide whether we want to return data in connect timeout case. many thanks.

image

Documentation needs to be a bit more clear about http directives.

For example, what does the package do if there is an etag directive to a call? Does it check with the API first when the cache is enabled, for the etag, or gives data from the cache directly, when the CachePolicy is set to CachePolicy.request, or only for CachePolicy.refresh?

[Question] Server requests conditions for cache activation ?

I'm using the cache with "default" configuration and the server side does not have any configuration to manage caching (no etag by example)

final options = CacheOptions(
      store: MemCacheStore(),
      policy: CachePolicy.request,
      hitCacheOnErrorExcept: [401, 403],
      maxStale: const Duration(minutes: 5),
      priority: CachePriority.normal,
      cipher: null,
      keyBuilder: CacheOptions.defaultCacheKeyBuilder,
      allowPostMethod: false,
    );

With this, caching is not working and multiple requests are fired without any cache (with a web application).

Is this "normal" ?
What needs to be done server side to make it working ?

Max-age header is ignored

In my API I store almost immutable data and I want them to be cached. However, I am not easily able to add ETag header or last-modified header. Therefore I use following in my responses:

cache-control: max-age=1800,public

According to MDN in terms of caching, it should behave as static assets and should be cached.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#caching_static_assets

But the dio_cache_interceptor (when using CachePolicy.request policy) considers cacheable only those requests with ETag or last-modified headers present.
https://github.com/llfbandit/dio_cache_interceptor/blob/master/lib/src/dio_cache_interceptor.dart#L145

I would find it very helpful if the requests with max-age would be cached as well.

[Future Request] Add the ability to set Hive box name

Hi,
I depend heavily on cache, by the passage of the time the user may have hundreds and hundreds of cached API calls. But each route has different API calls. So, for better performance, it would be better to have separate Hive box for each route rather than one box for all routes.

add update to CacheStore class

maybe this code be usefull

better to add copyWith() for CacheResponse

Future<void> update(key, content) async {
    final res= await get(key);
    if (res != null) {
      await delete(res.key);
      final CacheResponse response = CacheResponse(
          cacheControl: res.cacheControl,
          content: utf8.encode(jsonEncode(content)),
          date: DateTime.now(),
          eTag: res.eTag,
          expires: DateTime.now().add(const Duration(seconds: 45)),
          headers: res.headers,
          lastModified: DateTime.now(),
          key: res.key,
          maxStale: res.maxStale,
          priority: res.priority,
          requestDate: res.requestDate,
          responseDate: res.requestDate,
          url: res.url);
      await set(response);
    }
  }

response.data is different types depending on URL requested

I'm not sure if this is expected or not. It makes it hard to use this cache with the stream option since callers have to check the type.

PS C:\Users\micro\Documents\GitHub\flutter_eng_cycle_times> dart run .\bin\bug.dart
Instance of 'ResponseBody'
Instance of 'ResponseBody'
{userId: 1, id: 1, title: delectus aut autem, completed: false}
Instance of '_GeneratedStreamImpl<List<int>>'
Instance of '_GeneratedStreamImpl<List<int>>'
I'm using the dart command from my flutter install:

[√] Flutter (Channel master, 2.3.0-2.0.pre.90, on Microsoft Windows [Version 10.0.19041.928], locale en-US)
    • Flutter version 2.3.0-2.0.pre.90 at C:\Users\micro\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision f82dedd5b9 (2 days ago), 2021-05-10 15:22:50 -0700
    • Engine revision f5b97d0b23
    • Dart version 2.14.0 (build 2.14.0-74.0.dev)
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor/src/store/file_cache_store.dart';

void main() async {
  final cacheOptions = CacheOptions(
    store: FileCacheStore('cache'),
    policy: CachePolicy.request,
    hitCacheOnErrorExcept: [401, 403],
    maxStale: const Duration(days: 7),
    priority: CachePriority.normal,
    cipher: null,
    keyBuilder: CacheOptions.defaultCacheKeyBuilder,
    // Default. Allows to cache POST requests.
    // Overriding [keyBuilder] is strongly recommended.
    allowPostMethod: false,
  );

  final nonCachingDio = Dio();

  final cachingDio = Dio()
    ..interceptors.add(DioCacheInterceptor(options: cacheOptions));

  var requestOptions = Options(
      // method: request.method,
      // headers: request.headers,
      responseType: ResponseType.stream);

  var url = 'https://jsonplaceholder.typicode.com/todos/1';
  var response = await nonCachingDio.request(url, options: requestOptions);
  print(response.data);
  response = await nonCachingDio.request(url, options: requestOptions);
  print(response.data);

  // Force refresh
  response = await cachingDio.request(url,
      options: cacheOptions.copyWith(policy: CachePolicy.refresh).toOptions());
  response = await cachingDio.request(url, options: requestOptions);
  print(response.data);
  response = await cachingDio.request(url, options: requestOptions);
  print(response.data);
}

response.data is sometimes ResponseBody, breaking caching?

This is probably just a symptom from #21. However this is how it's actually manifesting for me in my program (here reduced to a smaller test case). Notice how the example url from #21 caches fine, however when I use a GitHub API URL caching blows up somewhere inside dio_cache_interceptor. Maybe I've configured something wrong?

PS C:\Users\micro\Documents\GitHub\flutter_eng_cycle_times> rmdir .\cache\

Confirm
The item at C:\Users\micro\Documents\GitHub\flutter_eng_cycle_times\cache\ has children and the Recurse parameter was not specified. If you continue, all children will be 
 removed with the item. Are you sure you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): A
PS C:\Users\micro\Documents\GitHub\flutter_eng_cycle_times> dart run .\bin\bug2.dart
Unhandled exception:
type 'ResponseBody' is not a subtype of type 'Stream<List<int>>' in type cast
#0      serializeContent (package:dio_cache_interceptor/src/util/content_serialization.dart:10:30)
#1      DioCacheInterceptor._buildCacheResponse (package:dio_cache_interceptor/src/dio_cache_interceptor.dart:213:13)
#2      DioCacheInterceptor.onResponse (package:dio_cache_interceptor/src/dio_cache_interceptor.dart:78:31)
#3      DioMixin.fetch._responseInterceptorWrapper.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:dio/src/dio_mixin.dart:526:28)
#4      DioMixin.checkIfNeedEnqueue (package:dio/src/dio_mixin.dart:795:22)
#5      DioMixin.fetch._responseInterceptorWrapper.<anonymous closure>.<anonymous closure> (package:dio/src/dio_mixin.dart:524:22)
#6      new Future.<anonymous closure> (dart:async/future.dart:174:37)
#7      Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#8      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#9      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#10     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor/src/store/file_cache_store.dart';

void main() async {
  final cacheOptions = CacheOptions(
    store: FileCacheStore('cache'),
    policy: CachePolicy.request,
    hitCacheOnErrorExcept: [401, 403],
    maxStale: const Duration(days: 7),
    priority: CachePriority.normal,
    cipher: null,
    keyBuilder: CacheOptions.defaultCacheKeyBuilder,
    // Default. Allows to cache POST requests.
    // Overriding [keyBuilder] is strongly recommended.
    allowPostMethod: false,
  );

  final cachingDio = Dio()
    ..interceptors.add(DioCacheInterceptor(options: cacheOptions));

  var cachableUrl = 'https://jsonplaceholder.typicode.com/todos/1';

  var options = Options(responseType: ResponseType.stream);

  var response = await cachingDio.request(cachableUrl, options: options);
  print(response.data.runtimeType);

  var errorUrl =
      'https://api.github.com/repos/flutter/flutter/compare/06e2fd63574bad2edafbe4653104ed76871ee0b1...d97f41caed971d4668ffe56699367ec3978db8f6';

  response = await cachingDio.request(errorUrl, options: options);
  print(response.data.runtimeType);
}

file_cache_store.dart is not exported

file_cache_store.dart is not exported so we have to manually import the file_cache_store file.

import 'package:dio_cache_interceptor/src/store/file_cache_store.dart'

the file_cache_store.dart should be exported as well

MemCacheStore response size computation

Hi,

I had an issue with MemCacheStore and I had a look to the source code to understand what was wrong:

My response has a content size of 95,88KB but it was skipped from being put into the cache because it's too heavy. When I look to the _LruMap the content size of the response if multiply by 8! Is it for bytes conversion or a magic number?
(https://github.com/llfbandit/dio_cache_interceptor/blob/master/dio_cache_interceptor/lib/src/store/mem_cache_store.dart#L164) which exceeds the default maxEntrySize (and that's why the response is skipped from being put into the cache). I'm wondering why the content size is multiply by 8? Is it a bug or did I miss something as I would personally just use the content size + the header size which is already a bytes length, to compute the response overall size?

I know that I can set a new maxEntrySize (and I'll change it as a workaround) but the default value seems enough for my requirements.

Suggestion: It's not easy to understand that the response has oversized the maximum entry size and it takes times. Perhaps a log could be a good option to help developers to understand that the response could not have be stored in the cache due to an oversized content size?

dio_cache_interceptor version 3.2.2

Suggestion for potential addition for better storage management.

Idea 1:
Maybe an option to specify the max amount of items (or size) the cache stores at a time, in the interceptor, Currently it seems like the package caches everything and never deletes them (even in the case mentioned below), unless a new response overwrites it, which could lead to uncomfortable cache sizes for some apps.

Idea 2:
Delete the data from cache if it comes up as expired when checked.

Whoops sorry, wrote this while half asleep, confused maxAge and maxStale.

import Error while using dio_cache_interceptor_db_store

It seems you removed db_cache_store from dio_cache_interceptor and not from dio_cache_interceptor_db_store package

Error Log:

Error: Error when reading '../../../development/flutter/.pub-cache/hosted/pub.dartlang.org/dio_cache_interceptor-3.0.0/lib/src/store/db_cache_store/database.dart': No such file or directory

import 'package:dio_cache_interceptor/src/store/db_cache_store/database.dart';
^

../../../development/flutter/.pub-cache/hosted/pub.dartlang.org/dio_cache_interceptor_db_store-3.0.0/lib/src/store/dio_cache_interceptor_db_store.dart:35:14: Error: Method not found: 'openDb'.
final DioCacheDatabase _db;
^^^^^^^^^^^^^^^^
../../../development/flutter/.pub-cache/hosted/pub.dartlang.org/dio_cache_interceptor_db_store-3.0.0/lib/src/store/dio_cache_interceptor_db_store.dart:29:9: Error: 'DioCacheDatabase' isn't a type.
final DioCacheDatabase _db;
^^^^^^^^^^^^^^^^
../../../development/flutter/.pub-cache/hosted/pub.dartlang.org/dio_cache_interceptor_db_store-3.0.0/lib/src/store/dio_cache_interceptor_db_store.dart:29:9: Error: Type 'DioCacheDatabase' not found.

   ^

Is it possible to move dbcache to a single pakcages

Is it possible to move dbcache to a single pakcages
I hope that dio_cache_interceptor does not rely on the database. After all, database dependency is a huge feature, and a large amount of code will be introduced to the native end, which makes it unfriendly for us who use flutter module instead of flutter application

Cache for Paginated API

My app has a list which supports pagination with data loaded from API call and I also cached the data.
In that list I also implemented pull to refresh function to force refresh the data from server.
When I trigger the pull to refresh, only the first page is loaded from server, and next page is returned from cache.
Is there any way for subsequent API call which only differs in page & size to not load from cache? Can I delete cache which has same URL but different query params?

I implemented a custom Dio client for force refresh trigger. When I trigger the pull to refresh I set the forceRefresh to true.

class CustomDio extends DioForNative {
  final CacheOptions _cacheOptions;
  CustomDio([
    BaseOptions options,
    this._cacheOptions,
  ]) : super(options);

  /// Set Caching Options
  ///
  /// [forceRefresh] will determine the response is coming from cache if available or network request
  ///
  /// [maxAge] will determine cache will be deleted
  void setCacheOptions({
    bool forceRefresh = true,

    /// Duration until cache deleted
    Duration maxAge = const Duration(minutes: 30),
  }) {
    final cache = _cacheOptions
        .copyWith(
          policy: forceRefresh ? CachePolicy.noCache : CachePolicy.forceCache,
          maxStale: maxAge,
        )
        .toOptions();
    options.extra.addAll(cache.extra);
  }
}

I hope this is clear enough to understand. Thank you.

Add support for "soft" stale

Could an option be added for maxStaleSoft which would only try to get new data after duration has been reached, and even then, it would return stale data if it isn't able to get new data?
I guess maxStaleSoft would be either a Duration (which would be overridden by maxStale), or a boolean (which would modify maxStale)
Or, a more general option alwaysAllowCacheFallback which would cause that whatever the cause, if cache is expired but we don't have new data, return cached data.

💡 if cache is expired but we can't now get new data, return cached data Is that already how the interceptor works? Is that a reasonable default?

My use case:

I have HTTP endpoints (for audio classes relevant to today etc) whose content

  1. Might be outdated in 3 hours*
  2. Is always relevant - yesterdays class doesn't become wrong just because a day has passed

I would like the cached response to always be returned if it has been cached for less then 3 hours.
After 3 hours - try to update cache, returned cached data if the http request fails (most likely, because of no data connection)

*) A more or less arbitrary number. The assumption is that if user has yesterdays class for 3 extra hours it's fine.

If you think you might get to this soon, great!
If this isn't a fit for this project, also great! I'll figure something out.
If you think it would be a nice feature, but don't know if you'll ever get around to it - I'll take a look and consider if it would take less time to open a PR or use a different caching method.

Issues with `Expires` header

Hi,

Maybe I'm missing something obvious, but in the isExpired check, if the Expires header is set, you compare checkedDate with checkedExpires, both of which are constant, unchanging values I believe. This would imply that that this check is always false (assuming the Expires value is in the future at the time of the request).

Shouldn't it be checkedExpires.difference(DateTime.now()).isNegative; on L120? (see

return checkedExpires.difference(checkedDate).isNegative;
)

bool isExpired() {
    final cControl = cacheControl;
    final checkedDate = date ?? responseDate;

    if (cControl != null) {
      if (cControl.noCache ?? false) {
        return true;
      }

      final checkedMaxAge = cControl.maxAge;
      if (checkedMaxAge != null && checkedMaxAge > 0) {
        final maxDate = checkedDate.add(Duration(seconds: checkedMaxAge));
        return maxDate.isBefore(DateTime.now());
      }
    }

    final checkedExpires = expires;
    if (checkedExpires != null) {
      return checkedExpires.difference(DateTime.now()).isNegative;
    }

    return true;
  }
}

Second question (separate but related):

In the _shouldStoreResponse method, shouldn't there be a check if the exiresHeader != null, i.e. result |= response.headers[expiresHeader] != null; around L158? (

result |= response.headers[lastModifiedHeader] != null;
)

  bool _shouldStoreResponse(Response response, {CachePolicy? policy}) {
    if (policy == CachePolicy.forceCache ||
        policy == CachePolicy.refreshForceCache) {
      return true;
    }

    var result = response.headers[etagHeader] != null;
    result |= response.headers[lastModifiedHeader] != null;
    result |= response.headers[expiresHeader] != null

    final cacheControl = CacheControl.fromHeader(
      response.headers[cacheControlHeader],
    );

    if (cacheControl != null) {
      final checkedMaxAge = cacheControl.maxAge;
      result |= checkedMaxAge != null && checkedMaxAge > 0;
      result &= !(cacheControl.noStore ?? false);
    }

    return result;
  }

I made these changes for testing purposes on a fork after I had issues with the Expires header and it seems to be working correctly with the two changes above. But again, maybe I've just missed something obvious somewhere.

Thank you for your time and the great package!

Ben

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.