AspNetCore.CacheOutput
3.1.0
dotnet add package AspNetCore.CacheOutput --version 3.1.0
NuGet\Install-Package AspNetCore.CacheOutput -Version 3.1.0
<PackageReference Include="AspNetCore.CacheOutput" Version="3.1.0" />
paket add AspNetCore.CacheOutput --version 3.1.0
#r "nuget: AspNetCore.CacheOutput, 3.1.0"
// Install AspNetCore.CacheOutput as a Cake Addin #addin nuget:?package=AspNetCore.CacheOutput&version=3.1.0 // Install AspNetCore.CacheOutput as a Cake Tool #tool nuget:?package=AspNetCore.CacheOutput&version=3.1.0
AspNetCore.CacheOutput
Strathweb.CacheOutput library (https://github.com/filipw/Strathweb.CacheOutput) redone to work with ASP.NET Core
CacheOutput will take care of server side caching and set the appropriate client side (response) headers for you.
You can specify the following properties:
- ClientTimeSpan (corresponds to CacheControl MaxAge HTTP header)
- MustRevalidate (corresponds to MustRevalidate HTTP header - indicates whether the origin server requires revalidation of a cache entry on any subsequent use when the cache entry becomes stale)
- ExcludeQueryStringFromCacheKey (do not vary cache by querystring values)
- ServerTimeSpan (time how long the response should be cached on the server side)
- AnonymousOnly (cache enabled only for requests when Thread.CurrentPrincipal is not set)
Additionally, the library is setting ETags for you, and keeping them unchanged for the duration of the caching period. Caching by default can only be applied to GET actions.
Installation
You can build from the source here, or you can install the Nuget version:
Install core package: Install-Package AspNetCore.CacheOutput
Depending on which cache provider you decided to use install any of the following packages:
Install-Package AspNetCore.CacheOutput.InMemory
Install-Package AspNetCore.CacheOutput.Redis
In "Startup" class "ConfigureServices" method depending on previosly installed cache provider register additional services:
For AspNetCore.CacheOutput.InMemory cache provider:
services.AddInMemoryCacheOutput();
For AspNetCore.CacheOutput.Redis cache provider:
services.AddRedisCacheOutput(Configuration.GetConnectionString("<redis connection string name>"));
Usage
// Cache for 100 seconds on the server, inform the client that response is valid for 100 seconds
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100)]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// Cache for 100 seconds on the server, inform the client that response is valid for 100 seconds. Cache for anonymous users only.
[CacheOutput(ClientTimeSpan = 100, ServerTimeSpan = 100, AnonymousOnly = true)]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// Inform the client that response is valid for 50 seconds. Force client to revalidate.
[CacheOutput(ClientTimeSpan = 50, MustRevalidate = true)]
public string Get(int id)
{
return "value";
}
// Cache for 50 seconds on the server. Ignore querystring parameters when serving cached content.
[CacheOutput(ServerTimeSpan = 50, ExcludeQueryStringFromCacheKey = true)]
public string Get(int id)
{
return "value";
}
Caching convention
In order to determine the expected content type of the response, CacheOutput will run Web APIs internal content negotiation process, based on the incoming request & the return type of the action on which caching is applied.
Each individual content type response is cached separately (so out of the box, you can expect the action to be cached as JSON and XML, if you introduce more formatters, those will be cached as well).
Important: We use action name as part of the key. Therefore it is necessary that action names are unique inside the controller - that's the only way we can provide consistency.
So you either should use unique method names inside a single controller, or (if you really want to keep them the same names when overloading) you need to use ActionName attribute to provide uniqeness for caching. Example:
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Team> Get()
{
return Teams;
}
[ActionName("GetById")]
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Team> Get(int id)
{
return Teams;
}
If you want to bypass the content negotiation process, you can do so by using the MediaType
property:
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50, MediaType = "image/jpeg")]
public HttpResponseMessage Get(int id)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = GetImage(id); // e.g. StreamContent, ByteArrayContent,...
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return response;
}
This will always return a response with image/jpeg
as value for the Content-Type
header.
Ignoring caching
You can set up caching globally (add global caching filter) or on controller level (decorate the controller with the caching attribute). This means that caching settings will cascade down to all the actions in your entire application (in the first case) or in the controller (in the second case).
You can still instruct a specific action to opt out from caching by using [IgnoreCacheOutput]
attribute.
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public class IgnoreController : Controller
{
[HttpGet("cached")]
public string GetCached()
{
return DateTime.Now.ToString();
}
[IgnoreCacheOutput]
[HttpGet("uncached")]
public string GetUnCached()
{
return DateTime.Now.ToString();
}
}
Server-side caching
By default you can use AspNetCore.CacheOutput.InMemory cache provider to cache on the server side. However, you are free to swap this with anything else (static Dictionary, Memcached, Redis, whatever..) as long as you implement the following IApiCacheOutput interface (part of the distributed assembly).
public interface IApiCacheOutput
{
Task RemoveStartsWithAsync(string key);
Task<T> GetAsync<T>(string key) where T : class;
Task RemoveAsync(string key);
Task<bool> ContainsAsync(string key);
Task AddAsync(string key, object value, DateTimeOffset expiration, string dependsOnKey = null);
}
Suppose you have a custom implementation:
public class MyCache : IApiCacheOutput
{
// omitted for brevity
}
You can register your implementation in "Startup" class "ConfigureServices" method:
services.AddSingleton<IApiCacheOutput, MyCache>();
Cache invalidation
Invalidation on action level - done through attributes. For example:
public class TeamsController : Controller
{
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Team> Get()
{
// return something
}
[CacheOutput(ClientTimeSpan = 50, ServerTimeSpan = 50)]
public IEnumerable<Player> GetTeamPlayers(int id)
{
// return something
}
[InvalidateCacheOutput(nameof(Get))]
public void Post(Team value)
{
// this invalidates Get action cache
}
}
Obviously, multiple attributes are supported. You can also invalidate methods from separate controller:
[InvalidateCacheOutput(typeof(OtherController), nameof(Get))] // this will invalidate Get in a different controller
[InvalidateCacheOutput(nameof(Get))] // this will invalidate Get in this controller
public void Post(Team value)
{
// do stuff
}
If an error occurs that would prevent the resource from getting updated, return a non-Success status code to stop the invalidation:
[InvalidateCacheOutput(nameof(Get)] // this will invalidate Get upon a successful request
public void Put(Team value)
{
try{
// do stuff that causes an error
}
catch (...specific error)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest; // Prevents clearing the cache for the controller(s)
}
}
This works with ActionResult
s as well:
[InvalidateCacheOutput(nameof(Get)] // this will invalidate Get upon a successful request
public ActionResult Put(Team value)
{
try{
// do stuff that causes an error
}
catch (...specific error)
{
return BadRequest(); // Prevents clearing the cache for the controller(s)
}
return NoContent();
}
Please note that the Forbiden()
ActionResult
does not have a StatusCode causing it to fall back to the Response.StatusCode usage. Since ActionResult
values aren't
merged into the Response
until much later, the Response.StatusCode
will be defaulted to 200 and cause the cache to be invalidated. Setting the Response.StatusCode
to 403 will cause it to behave as expected.
Customizing the cache keys
You can provide your own cache key generator. To do this, you need to implement the ICacheKeyGenerator
interface. The default implementation should suffice in most situations.
When implementing, it is easiest to inherit your custom generator from the DefaultCacheKeyGenerator
class.
To set your custom implementation as the default register your implementation in "Startup" class "ConfigureServices" method:
services.AddSingleton<ICacheKeyGenerator, MyCustomCacheKeyGenerator>();
You can set a specific cache key generator for an action, using the CacheKeyGenerator
property:
[CacheOutput(CacheKeyGenerator=typeof(SuperNiceCacheKeyGenerator))]
Important: register it with your DI as itself:
services.AddSingleton<SuperNiceCacheKeyGenerator, SuperNiceCacheKeyGenerator>();
Finding a matching cache key generator is done in this order:
- Checks if CacheKeyGenerator property set in current action CacheOutputAttribute.
- Default globally registered CacheKeyGenerator.
DefaultCacheKeyGenerator
JSONP
We automatically exclude callback parameter from cache key to allow for smooth JSONP support.
So:
/api/something?abc=1&callback=jQuery1213
is cached as:
/api/something?abc=1
Position of the callback parameter does not matter.
Etags
For client side caching, in addition to MaxAge, we will issue Etags. You can use the Etag value to make a request with If-None-Match header. If the resource is still valid, server will then response with a 304 status code.
For example:
GET /api/myresource
Accept: application/json
Status Code: 200
Cache-Control: max-age=100
Content-Length: 24
Content-Type: application/json; charset=utf-8
Date: Fri, 25 Jan 2013 03:37:11 GMT
ETag: "5c479911-97b9-4b78-ae3e-d09db420d5ba"
Server: Microsoft-HTTPAPI/2.0
On the next request:
GET /api/myresource
Accept: application/json
If-None-Match: "5c479911-97b9-4b78-ae3e-d09db420d5ba"
Status Code: 304
Cache-Control: max-age=100
Content-Length: 0
Date: Fri, 25 Jan 2013 03:37:13 GMT
Server: Microsoft-HTTPAPI/2.0
License
Licensed under MIT. License included.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. 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. |
.NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
- No dependencies.
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (4)
Showing the top 4 NuGet packages that depend on AspNetCore.CacheOutput:
Package | Downloads |
---|---|
AspNetCore.CacheOutput.InMemory
InMemoryOutputCacheProvider for AspNetCore.CacheOutput package |
|
AspNetCore.CacheOutput.Redis
StackExchangeRedisOutputCacheProvider for AspNetCore.CacheOutput package |
|
AspNetCore.CacheOutput.LiteDB
Provider for caching using ASPNet OutputCache using LiteDatabase. For using with ASP.NET Core port of Strathweb.CacheOutput library developed by Alexander Shabunevich (https://github.com/Iamcerba/AspNetCore.CacheOutput) |
|
NetcodeHub.Packages.Wrappers.OutputCache
NetcodeHubOutputCacheService is a robust .NET library designed to simplify caching operations in your applications. This package provides a straightforward interface to interact with caching mechanisms, ensuring efficient and thread-safe caching of data. It integrates seamlessly with various caching stores through the IOutputCacheStore interface, supporting asynchronous operations to set, retrieve, and remove cached data. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.1.0 | 87,692 | 2/26/2024 |
3.0.0 | 208,779 | 4/4/2023 |
3.0.0-pre3 | 210 | 4/4/2023 |
3.0.0-pre2 | 147 | 4/3/2023 |
3.0.0-pre1 | 1,771 | 2/5/2023 |
2.1.0 | 293,147 | 11/25/2021 |
2.0.3.1 | 6,789 | 10/18/2021 |
2.0.2 | 18,605 | 9/8/2021 |
2.0.0-pre2 | 878 | 7/7/2021 |
2.0.0-pre1 | 475 | 5/5/2021 |
1.0.11.2 | 155,841 | 10/29/2020 |
1.0.11.1 | 35,457 | 7/21/2020 |
1.0.11 | 20,196 | 2/8/2020 |
1.0.10 | 16,160 | 3/4/2019 |
1.0.9 | 15,712 | 12/18/2018 |
1.0.5 | 803 | 11/12/2018 |
1.0.4 | 4,933 | 2/21/2018 |
1.0.3 | 2,506 | 2/13/2018 |