OutputCache - SetVaryByRouteValue doesn't work
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Describe the bug
I fetch the page by id and use slug like this:
[https://example.com/123/slug-a](https://example.com/123/slug-a)
[https://example.com/123/slug-b](https://example.com/123/slug-b )
[https://example.com/123/slug-c](https://example.com/123/slug-c )
All these routes should get the same page from output cache regardless of the slug value.
I use the `SetVaryByRouteValue("id")` but it doesn't work.
```
services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.AddPolicy().Expire(TimeSpan.FromSeconds(10000)), true);
options.AddPolicy("VaryById", builder => builder.SetVaryByRouteValue("id"));
});
```
The method:
```
[Route("text/{id:int}/{textSlug?}")]
[OutputCache(PolicyName = "VaryById")]
public async Task TextPage(int id, string textSlug)
```
When I use `SetVaryByQuery("id")` I get the cached version of the page as long I don't change the id, but then I need to change my url to look like this:
[https://example.com/123?slug=slug-a](https://example.com/123?slug=slug-a)
[https://example.com/123?slug=slug-b](https://example.com/123?slug=slug-b)
[https://example.com/123?slug=slug-c](https://example.com/123?slug=slug-c)
### Expected Behavior
I expect to get the cached version as long that I don't change the id route value.
So for:
[https://example.com/123/slug-a](https://example.com/123/slug-a)
[https://example.com/123/slug-b](https://example.com/123/slug-b )
[https://example.com/123/slug-c](https://example.com/123/slug-c )
I should get the same page from Output Cache.
### Steps To Reproduce
These are my settings:
```
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Primitives;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Authorization;
using System.Reflection;
using System.Linq;
namespace Cache;
public sealed class MyCustomPolicy : IOutputCachePolicy
{
public static readonly MyCustomPolicy Instance = new();
public MyCustomPolicy()
{
}
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var attemptOutputCaching = AttemptOutputCaching(context);
context.EnableOutputCaching = true;
context.AllowCacheLookup = attemptOutputCaching;
context.AllowCacheStorage = attemptOutputCaching;
context.AllowLocking = true;
// Vary by any query by default
context.CacheVaryByRules.QueryKeys = "*";
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}
ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var response = context.HttpContext.Response;
// Verify existence of cookie headers
if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
{
context.AllowCacheStorage = false;
}
// Check response code
if (response.StatusCode != StatusCodes.Status200OK)
{
context.AllowCacheStorage = false;
}
return ValueTask.CompletedTask;
}
private static bool AttemptOutputCaching(OutputCacheContext context)
{
// Check if the current request fulfills the requirements to be cached
var request = context.HttpContext.Request;
// Verify the method
if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
{
return false;
}
// Verify existence of authorization attribute
var controllerActionDescriptor = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata()?.MethodInfo;
var hasAuthorizeAttribute = controllerActionDescriptor?.GetCustomAttributes()?.OfType().Any() ?? false;
if (hasAuthorizeAttribute)
{
return false;
}
return true;
}
}
```
Add the service:
```
services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.AddPolicy().Expire(TimeSpan.FromSeconds(10000)), true);
options.AddPolicy("VaryById", builder => builder.SetVaryByRouteValue("id"));
//options.AddPolicy("Query", builder => builder.SetVaryByQuery("id"));
});
```
The method:
```
[Route("text/{id:int}/{textSlug?}")]
[OutputCache(PolicyName = "VaryById")]
public async Task TextPage(int id, string textSlug)
```
### Exceptions (if any)
_No response_
### .NET Version
7.0.203
### Anything else?
I tried to add to the base policy the following and it didn't work:
`context.CacheVaryByRules.RouteValueNames = "id";`
Contributor guide
Assessment
This issue has not been assessed yet.