SmartApiResponseCache is a flexible and efficient middleware for .NET APIs that implements HTTP response caching. It stores successful responses (2XX status codes) based on session and request data, improving API performance by avoiding repeated calls to the same endpoints. The cache can be stored in-memory or in a customizable storage solution like Redis.
- Caches successful responses (2XX status codes).
- Customizable cache duration per endpoint.
- Customizable cache case sensitive for query strings for a default or per endpoint.
- Cache invalidation and disabling options per endpoint.
- Supports in-memory caching (with the ability to integrate other storage systems such as Redis).
- Customizable to match your project requirements
-
Install the NuGet package via the package manager:
dotnet add package SmartApiResponseCache -
Or by using the NuGet CLI:
nuget install SmartApiResponseCache
In your Startup.cs (or Program.cs if using .NET 6+), you will need to add the middleware to your service collection and configure the cache options.
You can customize the cache duration and enable/disable the cache via SmartCacheOptions.
In appsettings json using IOptions<SmartCacheOptions> file like:
"SmartCacheOptions": {
"DefaultCacheDurationSeconds": 10,
"IsCacheEnabled": true,
"UseContextSession": false,
"ContentTypes" : [ "application/json", "application/xml", "text/plain" ],
"IsQueryStringCaseSensitive": true,
"ExcludedHeaders": ["x-custom-header"],
"ExcludedMethods": ["DELETE"]
}Or directly like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSmartResponseMemoryCache(options =>
{
// Set the default cache duration (in seconds)
options.DefaultCacheDurationSeconds = 10;
// Enable or disable the cache globally
options.IsCacheEnabled = true;
});
}
//minimal api
builder.Services.AddSmartResponseMemoryCache();
//Or also can do
/*
builder.Services.AddSmartApiResponseCache(
options => builder.Configuration.GetSection(SmartCacheOptions.SectionKey).Bind(options)
);
*/In your Configure method, add the middleware to the pipeline using UseSmartApiResponseCache():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Add other middlewares like routing, authentication, etc.
app.UseSmartApiResponseCache();
}
//minimal api
app.UseSmartApiResponseCache();By default, the middleware uses in-memory caching. However, you can customize the cache storage by implementing the ISmartCacheService interface.
public interface ISmartCacheService
{
Task<string> GenerateCacheKeyAsync(HttpContext context);
bool TryGetCachedResponse(string cacheKey, out CachedResponseEntry cachedEntry);
void CacheResponse(string cacheKey, byte[] response, TimeSpan duration, int statusCode, string contentType, IHeaderDictionary headers);
}If you want to customize, don't use the extension method AddSmartResponseMemoryCache() to add into Services and use your own.
For example, if you want to use Redis as a storage solution, implement the ISmartCacheService interface to interact with Redis:
public class RedisSmartCacheService : ISmartCacheService
{
private readonly IConnectionMultiplexer _redisConnection;
private readonly ICacheKeyGenerator CacheKeyGenerator
public RedisSmartCacheService(IConnectionMultiplexer redisConnection, ICacheKeyGenerator cacheKeyGenerator)
{
_redisConnection = redisConnection;
}
public async Task<string> GenerateCacheKeyAsync(HttpContext context)
{
string keyBuilder = await CacheKeyGenerator.GenerateKey(context);
string myKey;
// Implement your key generation logic here
return myKey;
}
public bool TryGetCachedResponse(string cacheKey, CachedResponseEntry cachedEntry)
{
// Implement your cache retrieval logic here (e.g., using Redis)
}
public void CacheResponse(string cacheKey, byte[] response, TimeSpan duration, int statusCode, string contentType, IHeaderDictionary headers)
{
// Implement your cache storage logic here (e.g., saving to Redis)
}
}You can use the ICacheKeyGenerator or not.
Replace the default service with your custom implementation in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISmartCacheService, RedisSmartCacheService>();
}For example, if you want to change how the key is generated, implement the ICacheKeyGenerator interface to interact with:
public class CustomCacheKeyGeneratorHandler(IHeaderKeyGenerator headerKeyGenerator,
IUserKeyGenerator userKeyGenerator, IOptions<SmartCacheOptions> options) : ICacheKeyGenerator
{
public async Task<string> GenerateKey(HttpContext context)
{
// Implement your key generation logic here
}
}Replace the default service with your custom implementation in the CustomCacheKeyGeneratorHandler method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ICacheKeyGenerator, CustomCacheKeyGeneratorHandler>();
}For example, if you want to change how the user key is generated, implement the IUserKeyGenerator interface to interact with:
public class CustomCreateUserKeyHandler : IUserKeyGenerator
{
public string CreateUserKey(HttpContext context)
{
// Implement your key generation logic here
}
}Replace the default service with your custom implementation in the CustomCreateUserKeyHandler method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUserKeyGenerator, CustomCreateUserKeyHandler>();
}For example, if you want to change how the header key is generated, implement the IHeaderKeyGenerator interface to interact with:
public class CustomHeadersContextHandlerr : IHeaderKeyGenerator
{
public string AddHeaders(HttpContext context)
{
// Implement your key generation logic here
}
}Replace the default service with your custom implementation in the CustomHeadersContextHandlerr method:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHeaderKeyGenerator, CustomHeadersContextHandlerr>();
}If you has default disable caching, then can enabled for specific endpoints, you can use the EnabledSmartCacheAttribute:
[EnabledSmartCacheAttribute]
public IActionResult GetCachedData()
{
// Your action logic here
}
//minimal api
app.Mapget("/weatherforecast", async () =>
{
...
})
.WithSmartCache();You can control the cache duration for individual endpoints by using the SmartCacheAttribute on your controller actions:
[SmartCache(DurationInSeconds = 30)]
public IActionResult GetProducts()
{
// Your action logic here
}
//minimal api
app.Mapget("/weatherforecast", async () =>
{
...
})
.WithSmartCacheSeconds(10);This sets a custom cache duration of 30 seconds for the GetProducts action. If not specified, the global default cache duration will be used.
If you use [SmartCacheAttribute] or .WithoutSmartCache(10) will enabled the cache for the endpoint only with the seconds you request.
If you want to disable caching for specific endpoints, you can use the NoSmartCacheAttribute:
[NoSmartCache]
public IActionResult GetNonCachedData()
{
// Your action logic here
}
//minimal api
app.Mapget("/weatherforecast", async () =>
{
...
})
.WithoutSmartCache();If you has default disable caching, then can enabled for specific endpoints, you can use the EnabledSmartCacheAttribute:
[CaseSensitiveAttribute]
public IActionResult GetCachedData()
{
// Your action logic here
}
//minimal api
app.Mapget("/weatherforecast", async () =>
{
...
})
.SmartCacheIsCaseSensitive();This class contains the options for configuring the cache middleware:
DefaultCacheDurationSeconds: The default duration (in seconds) for cache entries. Default is5.IsCacheEnabled: A flag that indicates whether caching is enabled globally. Default istrue.UseContextSession: A flag that indicates you have enabled sessions in the api and want to use cache per user session. Default isfalse.ContentTypes: Array with cacheable ContentTypes. Don't have default value, but if it's not set should use[ "application/json", "application/xml", "text/plain" ].IsQueryStringCaseSensitive: A flag that indicates whether caching is CASE SENSITIVE for the query strings globally. Default isfalse.ExcludedHeaders: Header should be exclude to generate teh cacheKey. Don't have default value but always will exclude["Date", "Set-Cookie", "Cache-Control", "Expires", "Pragma", "Last-Modified"]plus what you add.
Example:
public class SmartCacheOptions
{
public static string SectionKey = nameof(SmartCacheOptions);
public int DefaultCacheDurationSeconds { get; set; } = 5;
public bool IsCacheEnabled { get; set; } = true;
public bool UseContextSession { get; set; } = false;
public string[] ContentTypes { get; set; }
public bool IsQueryStringCaseSensitive { get; set; } = false;
}Implement this interface to create your custom cache store. This allows you to use various caching systems like Redis, SQL, etc.
GenerateCacheKeyAsync: Generates a unique cache key for the current HTTP request.TryGetCachedResponse: Attempts to retrieve a cached response by key.CacheResponse: Caches a response with a specified duration and status code.
Store the data to be cache.
Body: HTTP response body bytes.StatusCode: HTTP response status.ContentType: HTTP response ContentType.Headers: HTTP response Headers. When cache is hit should addx-smartapiresponsecache: HIT.
- Ensure that the cache service is correctly registered in the DI container (
AddSmartResponseMemoryCache()). - Check that the response body stream is properly handled when caching (read and reset the stream position).
- Verify that cache retrieval logic (e.g., Redis or memory cache) is working as expected.
This project is licensed under the MIT License.