Cuture.AspNetCore.ResponseCaching 2.2.0

dotnet add package Cuture.AspNetCore.ResponseCaching --version 2.2.0                
NuGet\Install-Package Cuture.AspNetCore.ResponseCaching -Version 2.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cuture.AspNetCore.ResponseCaching" Version="2.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Cuture.AspNetCore.ResponseCaching --version 2.2.0                
#r "nuget: Cuture.AspNetCore.ResponseCaching, 2.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Cuture.AspNetCore.ResponseCaching as a Cake Addin
#addin nuget:?package=Cuture.AspNetCore.ResponseCaching&version=2.2.0

// Install Cuture.AspNetCore.ResponseCaching as a Cake Tool
#tool nuget:?package=Cuture.AspNetCore.ResponseCaching&version=2.2.0                

Cuture.AspNetCore.ResponseCaching

1. Intro

The asp.net core server-side caching component implemented based on ResourceFilter and ActionFilter;

基于ResourceFilterActionFilter实现的asp.net core服务端缓存组件

2. 注意项

  • 针对Action的缓存实现,基于Filter,非中间件/AOP实现;
  • 命中缓存时直接将内容写入响应流,省略了序列化、反序列化等操作;
  • 只会缓存响应内容和ContentType,忽略了其它响应Header;
  • 支持基于QueryKeyFormKeyHeaderClaimModel中单个或多个组合的缓存键生成;
  • 已实现基于MemoryRedis(StackExchange.Redis)的缓存,可拓展;
  • 默认缓存Key生成器会包含请求路径为缓存Key;
  • 默认缓存Key是大小写不敏感(强制转换为小写)的;
  • Asp.net Core版本要求 - 6.0以上;
  • Diagnostics支持;
  • 执行流程概览

3. 如何使用

3.1 安装Nuget

Install-Package Cuture.AspNetCore.ResponseCaching

3.2 在Startup中配置选项

public void ConfigureServices(IServiceCollection services)
{
    //....Other Settings

    services.AddCaching(options =>
    {
        options.Enable = true;  //是否启用响应缓存
        options.DefaultCacheStoreLocation = CacheStoreLocation.Memory;      //默认缓存数据存储位置 - Memory
        options.DefaultExecutingLockMode = ExecutingLockMode.None;      //默认执行锁定模式 - 不锁定
        options.DefaultStrictMode = CacheKeyStrictMode.Ignore;      //默认缓存Key的严格模式 - 忽略没有的找到的Key
        options.LockedExecutionLocalResultCache = new MemoryCache(new MemoryCacheOptions());    //锁定执行时,响应的本地缓存
        options.MaxCacheableResponseLength = 1024 * 1024;       //默认最大可缓存的响应内容长度
        options.MaxCacheKeyLength = 1024;       //最大缓存Key长度
        options.OnCannotExecutionThroughLock = (cacheKey, filterContext, next) => Task.CompletedTask;     //无法使用锁执行请求时(Semaphore池用尽)的回调
        options.OnExecutionLockTimeoutFallback = (cacheKey, filterContext, next) => Task.CompletedTask;     //执行锁定超时后的处理委托
    });

    //以下为可选配置

    // 锁定执行的相关配置
    services.PostConfigure<ResponseCachingExecutingLockOptions>(options =>
    {
        options.MinimumSemaphoreRetained = 50;  //信号池的最小保留大小
        options.MaximumSemaphorePooled = 1000;  //信号池的最大大小

        options.MinimumExecutingLockRetained = 50;  //执行锁池的最小保留大小
        options.MaximumExecutingLockPooled = 1000;  //执行锁池的最大大小

        options.SemaphoreRecycleInterval = TimeSpan.FromMinutes(4);     //信号池的回收间隔
        options.ExecutingLockRecycleInterval = TimeSpan.FromMinutes(2);     //执行锁池的回收间隔
    });
}

3.3 为Action方法标记ResponseCachingMetadataAttribute与ResponseCacheableAttribute

3.3.1 工作方式概述
  • ResponseCachingMetadata为派生自IResponseCachingMetadataAttribute,用于描述响应缓存的配置等细节;
  • ResponseCacheable为内部实现的Attribute,用于使用Endpoint获取对应ActionResponseCachingMetadata并动态构建Filter

IResponseCachingMetadata接口及内置的ResponseCachingMetadata列表: | 接口 | 描述内容 | 内置实现 | | --- | --- | --- | |IResponseClaimCachePatternMetadata|创建缓存时依据的 Claim 类型|ResponseCachingAttribute| |IResponseFormCachePatternMetadata|创建缓存时依据的 Form 键|ResponseCachingAttribute| |IResponseHeaderCachePatternMetadata|创建缓存时依据的 Header 键|ResponseCachingAttribute| |IResponseModelCachePatternMetadata|创建缓存时依据的 Model 参数名|ResponseCachingAttribute| |IResponseQueryCachePatternMetadata|创建缓存时依据的 Query 键|ResponseCachingAttribute| |ICacheKeyGeneratorMetadata|用于生成缓存Key的ICacheKeyGenerator实现类型|CacheKeyGeneratorAttribute| |ICacheKeyStrictModeMetadata|缓存键严格模式|ResponseCachingAttribute| |ICacheModelKeyParserMetadata|用于生成Model的缓存Key的IModelKeyParser实现类型|CacheModelKeyParserAttribute| |ICacheModeMetadata|缓存模式|ResponseCachingAttribute| |ICacheStoreLocationMetadata|缓存数据存储位置|ResponseCachingAttribute| |IDumpStreamInitialCapacityMetadata|Dump响应的Stream初始容量|ResponseDumpCapacityAttribute| |IExecutingLockMetadata|Action的执行锁定方式|ExecutingLockAttribute| |IHotDataCacheMetadata|热点数据缓存方式|HotDataCacheAttribute| |IMaxCacheableResponseLengthMetadata|最大可缓存响应长度|ResponseCachingAttribute| |IResponseDurationMetadata|缓存时长|ResponseCachingAttribute|

3.3.2 使用内置的特性

使用[ResponseCaching]标记需要缓存响应的Action,或者使用简便标记[CacheByQuery][CacheByForm][CacheByHeader][CacheByClaim][CacheByModel][CacheByPath][CacheByFullUrl] (这些标记都是继承自ResponseCaching并进行了简单的预设置);

[ResponseCaching(
                60,  //缓存时长(秒)
                Mode = CacheMode.Custom,   //设置缓存模式 - 自定义缓存Key生成
                VaryByClaims = new[] { "id" },     //依据Claim中的`id`进行构建缓存Key
                VaryByHeaders = new[] { "version" },   //依据Header中的`version`进行构建缓存Key
                VaryByQueryKeys = new[] { "page", "pageSize" },    //依据Query中的`page`和`pageSize`进行构建缓存Key
                VaryByModels = new[] { "input" },  //依据Model中的`input`进行构建缓存Key
                StoreLocation = CacheStoreLocation.Memory,     //设置缓存数据存储位置 - Memory
                StrictMode = CacheKeyStrictMode.Ignore,    //缓存Key的严格模式 - 忽略没有的找到的Key
                MaxCacheableResponseLength = 1024 * 1024      //最大可缓存的响应内容长度
                )]
[CacheKeyGenerator(typeof(CustomCacheKeyGenerator), FilterType.Resource)]    //使用 CustomCacheKeyGenerator 作为缓存key生成器
[CacheModelKeyParser(typeof(CustomModelKeyParser))]    //使用 CustomModelKeyParser 作为model的key解析器
[ExecutingLock(ExecutingLockMode.CacheKeySingle)]    //执行action时锁定执行过程,锁定粒度为每个缓存Key,不允许并行执行
[HotDataCache(50, HotDataCachePolicy.LRU)]    //将热点数据缓存在内存中,淘汰算法为LRU
[ResponseDumpCapacity(1024 * 1024)]    //指定dump响应的stream初始化容量,减少不必要的扩容
public IEnumerable<DataDto> Foo([FromQuery] int page, [FromQuery] int pageSize, [FromBody] RequestDto input)
{
    //...action logic
}
3.3.3 使用自定义特性
  1. 自定义Attribue,继承继承ResponseCacheableAttribute(或继承IFilterFactory自行实现构建逻辑);
  2. 自定义Attribue,继承需要设置的IResponseCachingMetadata接口;
  3. 使用上述自定义特性标记需要缓存的Action方法;
  • 可以使用多个Attribute分别实现IResponseCachingMetadata,也可以将所有的功能在一个Attribute实现;

3.4 使用Redis进行缓存

不配置时将默认使用MemoryCache进行缓存

3.4.1 安装Nuget
Install-Package Cuture.AspNetCore.ResponseCaching.StackExchange.Redis
3.4.2 配置Redis缓存
public void ConfigureServices(IServiceCollection services)
{
    //....Other Settings

    services.AddCaching()
            .UseRedisResponseCache("redis:6379",    //redis配置字符串
                                   "ResponseCache_"     //缓存前缀
                                  );
}

3.5 拦截器

  • 当前只有两个拦截器
    • 缓存存储拦截器:ICacheStoringInterceptor
    • 缓存写入拦截器:IResponseWritingInterceptor
  • 拦截器可以有多个;
  • 拦截器执行顺序为 全局Service拦截器全局Instance拦截器Attribute拦截器
3.5.1 自定义拦截器
  • 继承需要拦截的流程接口即可;
public class IgnoreHelloCacheStoringInterceptor
    : Attribute     //继承Attribute,以进行单个Action的设置
    , ICacheStoringInterceptor      //响应存储拦截器
{
    public async Task<ResponseCacheEntry?> OnCacheStoringAsync(ActionContext actionContext, string key, ResponseCacheEntry entry, OnCacheStoringDelegate<ActionContext> next)
    {
        if (key == "hello")
        {
            return null; //key为hello时,不进行存储,且不进行后续的处理
        }

        return await next(actionContext, key, entry);   //执行后续处理
    }
}
3.5.2 设置全局拦截器
  • 全局生效的拦截器
public void ConfigureServices(IServiceCollection services)
{
    //....Other Settings

    services.AddCaching().ConfigureInterceptor(options =>
    {
        //Instance拦截器
        options.AddInterceptor(new CacheHitStampInterceptor("key", "value"));    //添加拦截器 CacheHitStampInterceptor 的实例作为全局拦截器
        
        //Service拦截器
        options.AddServiceInterceptor<CustomInterceptor>();    //从DI中获取 CustomInterceptor 作为全局拦截器
    });
}
3.5.2 设置Action拦截器
  • 将拦截器Attribute设置到对应的Action方法即可,此时拦截器只针对当前Action生效
3.5.3 内置的CacheHitStamp缓存处理拦截器
  • 此拦截器将会在命中缓存时向响应的HttpHeader中添加指定内容
  • 此设置可能因为前置拦截器短路而不执行

配置方法:

public void ConfigureServices(IServiceCollection services)
{
    //....Other Settings

    services.AddCaching().UseCacheHitStampHeader("cached", "1");    //当命中缓存时,响应的Header中将附加`cached: 1`
}

4. 示例

4.1 基于Query缓存

使用query中的page和pageSize缓存

[HttpGet]
[CacheByQuery(60, "page", "pageSize")]
public ResultDto Foo(int page, int pageSize)
{
    //...action logic
}

4.2 基于Form缓存

使用form中的page和pageSize缓存

[HttpGet]
[CacheByForm(60, "page", "pageSize")]
public ResultDto Foo()
{
    int page = int.Parse(Request.Form["page"]);
    int pageSize = int.Parse(Request.Form["pageSize"]);
    //...action logic
}

4.3 基于Model缓存

使用action的参数进行缓存

[HttpPost]
[CacheByModel(60)]
public ResultDto Foo(RequestDto input)
{
    //...action logic
}

public class RequestDto : ICacheKeyable
{
    public int Page { get; set; }

    public int PageSize { get; set; }

    public string AsCacheKey() => $"{Page}_{PageSize}";
}

使用Model缓存会使用以下方式的其中一种生成Key(优先级从上往下)

  • 指定ModelKeyParserType
  • Model类实现ICacheKeyable接口
  • Model类的ToString方法
此时FilterResourceFilter转变为ActionFilter

4.4 基于用户缓存

使用用户凭据中的id和query中的pagepageSize组合构建缓存Key

[ResponseCaching(60,
                 Mode = CacheMode.Custom,
                 VaryByClaims = new[] { "id" },
                 VaryByQueryKeys = new[] { "page", "pageSize" })]
public ResultDto Foo(int page, int pageSize)
{
    //...action logic
}

5. 自定义缓存实现

实现IMemoryResponseCacheIDistributedResponseCache接口;并将实现注入asp.net core的DI,替换掉默认实现;


Diagnostics支持

  • 部分功能已使用DiagnosticDiagnosticNameCuture.AspNetCore.ResponseCaching

事件列表如下:

事件名称 事件
Cuture.AspNetCore.ResponseCaching.StartProcessingCache 开始处理缓存
Cuture.AspNetCore.ResponseCaching.EndProcessingCache 处理缓存结束
Cuture.AspNetCore.ResponseCaching.CacheKeyGenerated 缓存key已生成
Cuture.AspNetCore.ResponseCaching.ResponseFromCache 从缓存响应请求
Cuture.AspNetCore.ResponseCaching.ResponseFromActionResult 使用IActionResult响应请求事件
Cuture.AspNetCore.ResponseCaching.CacheKeyTooLong 缓存键过长
Cuture.AspNetCore.ResponseCaching.NoCachingFounded 没有找到缓存
Cuture.AspNetCore.ResponseCaching.CacheBodyTooLong 缓存内容过大

使用ILogger打印事件信息

  • 已实现简单的Diagnostic订阅并打印,直接启用即可输出日志;
  • 使用Diagnostic会对性能有那么一点影响;
1. 配置服务时添加Logger
services.AddCaching()
        .AddDiagnosticDebugLogger() //在Debug模式下使用Logger输出Diagnostic事件信息
        .AddDiagnosticReleaseLogger();  //在Release模式下使用Logger输出Diagnostic事件信息
2. 构建应用时启用Logger
app.EnableResponseCachingDiagnosticLogger();

如上配置后,内部将订阅相关Diagnostic,并将事件信息使用ILogger输出。


其它

1. 启用 CacheKeyAccessor 在代码中访问当前请求的 cache key

services.AddCaching()
        .EnableCacheKeyAccessor(); //启用 CacheKeyAccessor ,从DI中获取 ICacheKeyAccessor 以访问当前请求的 `cache key`
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.
  • net7.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Cuture.AspNetCore.ResponseCaching:

Package Downloads
Cuture.AspNetCore.ResponseCaching.StackExchange.Redis

The `asp.net core` server-side caching component implemented based on `ResourceFilter` and `ActionFilter`; 基于`ResourceFilter`和`ActionFilter`实现的`asp.net core`服务端缓存组件。此包为使用 StackExchange.Redis 的 ResponseCache 实现。

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.0 19,254 3/21/2024
2.1.0 8,861 11/15/2023
2.0.2 17,684 5/13/2023
2.0.2-beta-05 1,069 5/3/2023
2.0.2-beta-03 230 5/2/2023
2.0.2-beta-02 457 4/27/2023
2.0.1 2,560 3/26/2023
2.0.0 31,987 11/13/2021
2.0.0-preview-01 3,416 9/29/2021
1.4.1 7,587 7/1/2021
1.4.0 8,079 4/13/2021
1.3.0 2,723 3/19/2021
1.2.0 5,452 1/25/2021
1.1.0 3,446 12/26/2020
1.0.4 641 11/21/2020
1.0.3 535 11/17/2020
1.0.2 533 11/12/2020
1.0.1 598 9/18/2020
1.0.0 632 9/7/2020
0.0.1-alpha0005 369 8/27/2020
0.0.1-alpha0004 383 8/24/2020
0.0.1-alpha0003 405 8/20/2020
0.0.1-alpha0002 351 8/18/2020
0.0.1-alpha0001 370 8/17/2020