-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add Output Caching middleware #41955
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Should the methods on |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
I think all the async methods should probably have a What's the difference between |
API Review Notes:
options.Policies["NoCache"] = new OutputCachePolicyBuilder().Policy("NoCache").Build();
|
API Review Notes:
|
Updated |
API Review Notes:
I don't think we should ship the Output Cache Store APIs until we have at least one non-in-memory implementation that shows an advantage over Is there any public API added by #41037 that I didn't catch? I caught that Here's the diff from the most recent proposal. +namespace Microsoft.AspNetCore.Builder;
+
-public static class OutputCacheServicesExtensions
+public static class OutputCacheServiceCollectionExtensions
{
- public static IServiceCollection AddOutputCaching(this IServiceCollection services);
- public static IServiceCollection AddOutputCaching(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
+ public static IServiceCollection AddOutputCache(this IServiceCollection services);
+ public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
-public static class OutputCacheExtensions
+public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class OutputCacheConventionBuilderExtensions
+{
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
+}
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
-public class OutputCacheOptions
+public sealed class OutputCacheOptions
{
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
- public PoliciesCollection BasePolicies { get; }
- public IServiceProvider ApplicationServices { get; set; }
+ public IList<IOutputCachePolicy> BasePolicies { get; }
+ public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
- Task OnRequestAsync(OutputCacheContext context);
- Task OnServeFromCacheAsync(OutputCacheContext context);
- Task OnServeResponseAsync(OutputCacheContext context);
+ Task OnRequestAsync(OutputCacheContext context, CancellationToken token);
+ Task OnServeFromCacheAsync(OutputCacheContext context, CancellationToken token);
+ Task OnServeResponseAsync(OutputCacheContext context, CancellationToken token);
}
-public sealed class OutputCachePolicyBuilder : IOutputCachePolicy
+public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
- public OutputCachePolicyBuilder AddPolicy(IOutputCachePolicy policy);
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
+ public OutputCachePolicyBuilder AddPolicy(string policyName);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
- public OutputCachePolicyBuilder With(Func<OutputCacheContext, Task<bool>> predicate);
+ public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, Task<bool>> asyncPredicate);
- public OutputCachePolicyBuilder Policy(string profileName);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
- public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, Task<string>> varyBy);
- public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, (string, string)> varyBy);
- public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, Task<(string, string)>> varyBy);
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, Task<string>> varyByAsync);
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, Task<KeyValuePair<string, string>>> varyByAsnc);
}
public sealed class OutputCacheContext
{
+ public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public HttpContext HttpContext { get; }
public DateTimeOffset? ResponseTime { get; }
public CachedVaryByRules CachedVaryByRules { get; set; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
-public class CachedVaryByRules
+public sealed class CacheVaryByRules
{
- public void SetVaryByCustom(string key, string value);
- public StringValues Headers { get; set; }
- public StringValues QueryKeys { get; set; }
- public StringValues VaryByPrefix { get; set; }
+ public CacheVaryByRules();
+ public IDictionary<string, string> VaryByCustom { get; }
+ public StringValues Headers { get; init; }
+ public StringValues QueryKeys { get; init; }
+ public StringValues VaryByPrefix { get; init; }
}
-
-public class OutputCacheAttribute : Attribute, IOutputCachePolicy
+public sealed class OutputCacheAttribute : Attribute
{
- public int Duration { get; set; }
- public bool NoStore { get; set; }
- public string[]? VaryByQueryKeys { get; set; }
- public string[]? VaryByHeaders { get; set; }
- public string? PolicyName { get; set; }
+ public OutputCacheAttribute();
+ public int Duration { get; init; }
+ public bool NoStore { get; init; }
+ public string[]? VaryByQueryKeys { get; init; }
+ public string[]? VaryByHeaders { get; init; }
+ public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
- PoliciesCollection Policies { get; }
+ IList<IOutputCachePolicy> Policies { get; }
}
-
-public interface IOutputCacheStore
-{
- ValueTask EvictByTagAsync(string tag, CancellationToken token);
- ValueTask<OutputCacheEntry?> GetAsync(string key, CancellationToken token);
- ValueTask SetAsync(string key, OutputCacheEntry entry, TimeSpan validFor, CancellationToken token);
-}
-
-public sealed class OutputCacheEntry
-{
- public DateTimeOffset Created { get; set; }
- public int StatusCode { get; set; }
- public IHeaderDictionary Headers { get; set; }
- public CachedResponseBody Body { get; set; }
- public string[]? Tags { get; set; }
-}
-
-public class CachedResponseBody
-{
- public List<byte[]> Segments { get; }
- public long Length { get; }
- public CachedResponseBody(List<byte[]> segments, long length);
- public async Task CopyToAsync(PipeWriter destination, CancellationToken cancellationToken);
-} @sebastienros @dotnet/aspnet-api-review Does this seem reasonable to all of you? Edit: Updated |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
@sebastienros Can you explain Also, if the default to true, wouldn't we want |
|
Mvc sample: using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
namespace MvcSandbox.Controllers;
public class HomeController : Controller
{
[ModelBinder]
public string Id { get; set; }
[OutputCache(Duration = 10, VaryByQueryKeys = new[] { "culture" })]
public IActionResult Index()
{
return View();
}
} |
During the meetings it was suggested to use
Missed them, thanks.
To make the API lighter. The option is already available with the builder
They already are. Wrongly referenced in the previous issue version maybe?
I followed the naming from UseResponseCaching, UseStaticFiles, UseDefaultFiles. Do you still want to change it? Would we use a common static class for all OutputCaching extension methods? ``OutputCacheBuilderExtensions`?
It's already there.
I followed AddResponseCaching naming. Now I see there is a mix, will change.
Let's change AddOutputCaching to AddOutputCache. OK
OK
Can you explain where would the recursivity be?
Recursivity in this context as in named policies could invoke each others?
I assume you meant "no profile in public api". Shouldn't it be AddPolicy like all the other similar methods instead of just Policy?
Yes, since we decided in meetings to have Can we seal OutputCacheOptions, CachedVaryByRules and OutputCacheAttribute?
Copied from Response Caching.
OK
It was a dictionary but during the meetings the feedback was to have
OK
It was suggested that OutputCachePolicyBuilder should implement it so we can pass a builder as the policy. And it's building it only once, lazily.
It was implementing
OK
Did another pass and found public sealed class OutputCacheOptions
{
+ public bool UseCaseSensitivePaths{ get; set; }
} |
New proposal:
Diff from proposal: public interface IOutputCachePolicy
{
- Task OnRequestAsync(OutputCacheContext context, CancellationToken token);
- Task OnServeFromCacheAsync(OutputCacheContext context, CancellationToken token);
- Task OnServeResponseAsync(OutputCacheContext context, CancellationToken token);
+ Task CacheRequestAsync(OutputCacheContext context, CancellationToken token);
+ Task ServeFromCacheAsync(OutputCacheContext context, CancellationToken token);
+ Task ServeResponseAsync(OutputCacheContext context, CancellationToken token);
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
- IList<IOutputCachePolicy> Policies { get; }
}
+public interface IOutputCacheStore
+{
+ ValueTask EvictByTagAsync(string tag, CancellationToken token);
+ ValueTask<byte[]> GetAsync(string key, CancellationToken token);
+ ValueTask SetAsync(string key, byte[] value, TimeSpan validFor, CancellationToken token);
+} Full proposal: namespace Microsoft.AspNetCore.Builder;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IList<IOutputCachePolicy> BasePolicies { get; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
Task CacheRequestAsync(OutputCacheContext context, CancellationToken token);
Task ServeFromCacheAsync(OutputCacheContext context, CancellationToken token);
Task ServeResponseAsync(OutputCacheContext context, CancellationToken token);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder AddPolicy(string policyName);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, Task<bool>> asyncPredicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, Task<string>> varyByAsync);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, Task<KeyValuePair<string, string>>> varyByAsnc);
}
public sealed class OutputCacheContext
{
public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public HttpContext HttpContext { get; }
public DateTimeOffset? ResponseTime { get; }
public CachedVaryByRules CachedVaryByRules { get; set; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; }
public StringValues Headers { get; init; }
public StringValues QueryKeys { get; init; }
public StringValues VaryByPrefix { get; init; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
public string[]? VaryByQueryKeys { get; init; }
public string[]? VaryByHeaders { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken token);
ValueTask<byte[]> GetAsync(string key, CancellationToken token);
ValueTask SetAsync(string key, byte[] value, TimeSpan validFor, CancellationToken token);
} |
Is there any reason for not using Same goes for I kinda agree with @davidfowl's comments about it not being obvious what the predicates are doing based on the method name "With". You said, if the predicate is true, the policy stays enabled, right? Would I'm fine with shipping the simplified @dotnet/aspnet-api-review I think we should approve what @sebastienros proposed above with all the |
Last piece of feedback:
Lets approve for preview6 and get some feedback and make more breaking changes in preview7 |
Final preview6 API review notes:
namespace Microsoft.AspNetCore.Builder;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IList<IOutputCachePolicy> BasePolicies { get; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken token);
ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken token);
ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken token);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder AddPolicy(string policyName);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, ValueTask<bool>> asyncPredicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyByAsync);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyByAsnc);
}
public sealed class OutputCacheContext
{
public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public HttpContext HttpContext { get; }
public DateTimeOffset? ResponseTime { get; }
public CachedVaryByRules CachedVaryByRules { get; set; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; }
public StringValues Headers { get; init; }
public StringValues QueryKeys { get; init; }
public StringValues VaryByPrefix { get; init; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
public string[]? VaryByQueryKeys { get; init; }
public string[]? VaryByHeaders { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken token);
ValueTask<byte[]> GetAsync(string key, CancellationToken token);
ValueTask SetAsync(string key, byte[] value, TimeSpan validFor, CancellationToken token);
} API Approved! |
@adityamandaleeka, I've moved this to RC1 milestone. Please readjust as necessary. |
API Review Notes:
Approved as follows. Feel free to tell us what we messed up when you get back @sebastienros! 😆 namespace Microsoft.AspNetCore.Builder;
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public OutputCacheOptions();
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
public void AddBasePolicy(IOutputCachePolicy policy);
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, ValueTask<bool>> predicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy);
}
public sealed class OutputCacheContext
{
public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public HttpContext HttpContext { get; }
public DateTimeOffset? ResponseTime { get; }
public CacheVaryByRules CacheVaryByRules { get; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; set }
public StringValues Headers { get; set; }
public StringValues QueryKeys { get; set; }
public StringValues VaryByPrefix { get; set; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
public string[]? VaryByQueryKeys { get; init; }
public string[]? VaryByHeaders { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken);
ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken);
ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken);
} |
We missed VaryByRouteValue |
@sebastienros Can we make sure the currently merged API and the one here are in sync (and reconcile any deltas)? |
OK on all the recommendations, with these comments:
I don't think we can/should have a default constructor. There is an internal one, visible from Tests, which requires a bunch of mandatory arguments ( I want to add a new method to the builder to be able to instantiate a default policy (the one that accepts 200s, anonymous, GET/HEAD). Right now any time a policy is built using the builder, it will use the services.AddOutputCache(options =>
{
// Create a new policy that will contain the DefaultPolicy and enable all endpoints (GET, un-authenticated)
options.AddBasePolicy(build => { });
}); I suggest we add a method on the builder to be explicit about getting a policty that is the default: options.AddBasePolicy(build => build.Enable()); This would be no-op, but it would reflect what it return, a behavior that enables the cache. I could also suggest API after suggestionsnamespace Microsoft.AspNetCore.Builder;
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public OutputCacheOptions();
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
public void AddBasePolicy(IOutputCachePolicy policy);
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, ValueTask<bool>> predicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
+ public OutputCachePolicyBuilder Cache();
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy);
}
public sealed class OutputCacheContext
{
- public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public HttpContext HttpContext { get; }
public DateTimeOffset? ResponseTime { get; }
public CacheVaryByRules CacheVaryByRules { get; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; set }
public StringValues Headers { get; set; }
public StringValues QueryKeys { get; set; }
public StringValues VaryByPrefix { get; set; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
public string[]? VaryByQueryKeys { get; init; }
public string[]? VaryByHeaders { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken);
ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken);
ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken);
} |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
API Review Notes:
API Approved! namespace Microsoft.AspNetCore.Builder;
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public OutputCacheOptions();
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
public void AddBasePolicy(IOutputCachePolicy policy);
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, ValueTask<bool>> predicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
+ public OutputCachePolicyBuilder Cache();
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
+ public OutputCachePolicyBuilder VaryByRouteValue(params string[] routeValueNames);
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
- public OutputCachePolicyBuilder VaryByHeader(params string[] headers);
+ public OutputCachePolicyBuilder VaryByHeader(params string[] headerNames);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy);
}
public sealed class OutputCacheContext
{
public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
- public required HttpContext HttpContext { get; }
+ public required HttpContext HttpContext { get; init; }
- public DateTimeOffset? ResponseTime { get; }
+ public DateTimeOffset? ResponseTime { get; set; }
public CacheVaryByRules CacheVaryByRules { get; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; set }
+ public StringValues RouteValues { get; set; }
public StringValues Headers { get; set; }
public StringValues QueryKeys { get; set; }
public StringValues VaryByPrefix { get; set; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
+ public string[]? VaryByRouteValues { get; init; }
public string[]? VaryByQueryKeys { get; init; }
public string[]? VaryByHeaders { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken);
ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken);
ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken);
} Edit: Let's not do the |
I made two minor adjustments to this API after the meeting.
|
There are other things called |
New update from PR feedback, using namespace Microsoft.AspNetCore.Builder;
public static class OutputCacheApplicationBuilderExtensions
{
public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app);
}
namespace Microsoft.Extensions.DependencyInjection;
public static class OutputCacheServiceCollectionExtensions
{
public static IServiceCollection AddOutputCache(this IServiceCollection services);
public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions);
}
public static class OutputCacheConventionBuilderExtensions
{
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder;
public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder;
}
namespace Microsoft.AspNetCore.OutputCaching;
public sealed class OutputCacheOptions
{
public OutputCacheOptions();
public long SizeLimit { get; set; }
public long MaximumBodySize { get; set; }
public TimeSpan DefaultExpirationTimeSpan { get; set; }
public bool UseCaseSensitivePaths{ get; set; }
public IServiceProvider ApplicationServices { get; }
public void AddPolicy(string name, IOutputCachePolicy policy);
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build);
public void AddBasePolicy(IOutputCachePolicy policy);
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build);
}
public interface IOutputCachePolicy
{
ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken);
ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken);
}
public sealed class OutputCachePolicyBuilder
{
public OutputCachePolicyBuilder();
public OutputCachePolicyBuilder AddPolicy(Type policyType);
public OutputCachePolicyBuilder AddPolicy<T>() where T : IOutputCachePolicy;
public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate);
public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, ValueTask<bool>> predicate);
public OutputCachePolicyBuilder Tag(params string[] tags);
public OutputCachePolicyBuilder Expire(TimeSpan expiration);
public OutputCachePolicyBuilder Cache();
public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true);
public OutputCachePolicyBuilder Clear();
public OutputCachePolicyBuilder NoCache();
public OutputCachePolicyBuilder VaryByRouteValue(params string[] routeValueNames);
public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys);
public OutputCachePolicyBuilder VaryByHeader(params string[] headerNames);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy);
public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy);
}
public sealed class OutputCacheContext
{
public OutputCacheContext();
public bool EnableOutputCaching { get; set; }
public bool AllowCacheLookup { get; set; }
public bool AllowCacheStorage { get; set; }
public bool AllowLocking { get; set; }
public required HttpContext HttpContext { get; init; }
public DateTimeOffset? ResponseTime { get; set; }
public CacheVaryByRules CacheVaryByRules { get; }
public HashSet<string> Tags { get; }
public TimeSpan? ResponseExpirationTimeSpan { get; set; }
}
public sealed class CacheVaryByRules
{
public CacheVaryByRules();
public IDictionary<string, string> VaryByCustom { get; set }
- public StringValues RouteValues { get; set; }
+ public StringValues RouteValueNames { get; set; }
- public StringValues Headers { get; set; }
+ public StringValues HeaderNames { get; set; }
public StringValues QueryKeys { get; set; }
public StringValues VaryByPrefix { get; set; }
}
public sealed class OutputCacheAttribute : Attribute
{
public OutputCacheAttribute();
public int Duration { get; init; }
public bool NoStore { get; init; }
- public string[]? VaryByRouteValues { get; init; }
+ public string[]? VaryByRouteValueNames { get; init; }
public string[]? VaryByQueryKeys { get; init; }
- public string[]? VaryByHeaders { get; init; }
+ public string[]? VaryByHeaderNames { get; init; }
public string? PolicyName { get; init; }
}
public interface IOutputCacheFeature
{
OutputCacheContext Context { get; }
}
public interface IOutputCacheStore
{
ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken);
ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken);
ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken);
} |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
The above renames all look good to me. I like the improved clarity of adding the "Names" suffix to these properties. Does anyone else agree? @dotnet/aspnet-api-review @JamesNK @adityamandaleeka? You can use 👍 on my comment to show approval. |
API review notes
API Approved! |
Uh oh!
There was an error while loading. Please reload this page.
Background and Motivation
Implement Output Caching middleware.
Proposed API
Setup
Configuration
Output Cache Store
Sample
The text was updated successfully, but these errors were encountered: