Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.OutputCaching;

internal sealed class DefaultOutputCachePolicyProvider : IOutputCachePolicyProvider
{
private readonly OutputCacheOptions _options;

/// <summary>
/// Creates a new instance of <see cref="DefaultOutputCachePolicyProvider"/>.
/// </summary>
/// <param name="options">The options configured for the application.</param>
public DefaultOutputCachePolicyProvider(IOptions<OutputCacheOptions> options)
{
ArgumentNullException.ThrowIfNull(options);

_options = options.Value;
}

/// <inheritdoc />
public IReadOnlyList<IOutputCachePolicy> GetBasePolicies()
{
if (_options.BasePolicies is not null && _options.BasePolicies.Count > 0)
{
return _options.BasePolicies;
}
return Array.Empty<IOutputCachePolicy>();
}

/// <inheritdoc />
public ValueTask<IOutputCachePolicy?> GetPolicyAsync(string policyName)
{
ArgumentNullException.ThrowIfNull(policyName);

IOutputCachePolicy? policy = null;

if (_options.NamedPolicies is not null && _options.NamedPolicies.TryGetValue(policyName, out var value))
{
policy = value;
}

return ValueTask.FromResult(policy);
}
}
23 changes: 23 additions & 0 deletions src/Middleware/OutputCaching/src/IOutputCachePolicyProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.AspNetCore.OutputCaching;

/// <summary>
/// A provider to get output cache policies for a given request.
/// </summary>
public interface IOutputCachePolicyProvider
{
/// <summary>
/// Gets the base output cache policies.
/// </summary>
/// <returns>A readonly list of <see cref="IOutputCachePolicy"/> instances.</returns>
IReadOnlyList<IOutputCachePolicy> GetBasePolicies();

/// <summary>
/// Gets a <see cref="IOutputCachePolicy"/> from the given <paramref name="policyName"/>.
/// </summary>
/// <param name="policyName">The policy name to retrieve.</param>
/// <returns>The named <see cref="IOutputCachePolicy"/>.</returns>
ValueTask<IOutputCachePolicy?> GetPolicyAsync(string policyName);
}
14 changes: 11 additions & 3 deletions src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal sealed class OutputCacheMiddleware
private readonly ILogger _logger;
private readonly IOutputCacheStore _store;
private readonly IOutputCacheKeyProvider _keyProvider;
private readonly IOutputCachePolicyProvider _policyProvider;
private readonly WorkDispatcher<string, OutputCacheEntry?> _outputCacheEntryDispatcher;
private readonly WorkDispatcher<string, OutputCacheEntry?> _requestDispatcher;

Expand All @@ -35,18 +36,21 @@ internal sealed class OutputCacheMiddleware
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> used for logging.</param>
/// <param name="outputCache">The <see cref="IOutputCacheStore"/> store.</param>
/// <param name="poolProvider">The <see cref="ObjectPoolProvider"/> used for creating <see cref="ObjectPool"/> instances.</param>
/// <param name="policyProvider">The <see cref="IOutputCachePolicyProvider"/> used to resolve cache policies.</param>
public OutputCacheMiddleware(
RequestDelegate next,
IOptions<OutputCacheOptions> options,
ILoggerFactory loggerFactory,
IOutputCacheStore outputCache,
ObjectPoolProvider poolProvider
ObjectPoolProvider poolProvider,
IOutputCachePolicyProvider policyProvider
)
: this(
next,
options,
loggerFactory,
outputCache,
policyProvider,
new OutputCacheKeyProvider(poolProvider, options))
{ }

Expand All @@ -56,18 +60,21 @@ internal OutputCacheMiddleware(
IOptions<OutputCacheOptions> options,
ILoggerFactory loggerFactory,
IOutputCacheStore cache,
IOutputCachePolicyProvider policyProvider,
IOutputCacheKeyProvider keyProvider)
{
ArgumentNullException.ThrowIfNull(next);
ArgumentNullException.ThrowIfNull(options);
ArgumentNullException.ThrowIfNull(loggerFactory);
ArgumentNullException.ThrowIfNull(cache);
ArgumentNullException.ThrowIfNull(policyProvider);
ArgumentNullException.ThrowIfNull(keyProvider);

_next = next;
_options = options.Value;
_logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
_store = cache;
_policyProvider = policyProvider;
_keyProvider = keyProvider;
_outputCacheEntryDispatcher = new();
_requestDispatcher = new();
Expand Down Expand Up @@ -217,10 +224,11 @@ internal bool TryGetRequestPolicies(HttpContext httpContext, out IReadOnlyList<I
policies = Array.Empty<IOutputCachePolicy>();
List<IOutputCachePolicy>? result = null;

if (_options.BasePolicies != null)
var basePolicies = _policyProvider.GetBasePolicies();
if (basePolicies.Count > 0)
{
result = new();
result.AddRange(_options.BasePolicies);
result.AddRange(basePolicies);
}

var metadata = httpContext.GetEndpoint()?.Metadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static IServiceCollection AddOutputCache(this IServiceCollection services
services.AddTransient<IConfigureOptions<OutputCacheOptions>, OutputCacheOptionsSetup>();

services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.TryAddSingleton<IOutputCachePolicyProvider, DefaultOutputCachePolicyProvider>();

services.TryAddSingleton<IOutputCacheStore>(sp =>
{
Expand Down
44 changes: 17 additions & 27 deletions src/Middleware/OutputCaching/src/Policies/NamedPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.OutputCaching;

Expand All @@ -12,9 +11,6 @@ namespace Microsoft.AspNetCore.OutputCaching;
internal sealed class NamedPolicy : IOutputCachePolicy
{
private readonly string _policyName;
private IOptions<OutputCacheOptions>? _options;
private readonly object _synLock = new();

/// <summary>
/// Create a new <see cref="NamedPolicy"/> instance.
/// </summary>
Expand All @@ -25,58 +21,52 @@ public NamedPolicy(string policyName)
}

/// <inheritdoc />
ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
async ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var policy = GetProfilePolicy(context);
var policy = await GetProfilePolicy(context);

if (policy == null)
{
return ValueTask.CompletedTask;
return;
}

return policy.ServeResponseAsync(context, cancellationToken);
await policy.ServeResponseAsync(context, cancellationToken);
}

/// <inheritdoc />
ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
async ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var policy = GetProfilePolicy(context);
var policy = await GetProfilePolicy(context);

if (policy == null)
{
return ValueTask.CompletedTask;
return;
}

return policy.ServeFromCacheAsync(context, cancellationToken);
await policy.ServeFromCacheAsync(context, cancellationToken);
}

/// <inheritdoc />
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
async ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var policy = GetProfilePolicy(context);
var policy = await GetProfilePolicy(context);

if (policy == null)
{
return ValueTask.CompletedTask;
return;
}

return policy.CacheRequestAsync(context, cancellationToken);
await policy.CacheRequestAsync(context, cancellationToken);
}

internal IOutputCachePolicy? GetProfilePolicy(OutputCacheContext context)
internal ValueTask<IOutputCachePolicy?> GetProfilePolicy(OutputCacheContext context)
{
if (_options == null)
var provider = context.HttpContext.RequestServices.GetRequiredService<IOutputCachePolicyProvider>();
if (provider == null)
{
lock (_synLock)
{
_options ??= context.HttpContext.RequestServices.GetRequiredService<IOptions<OutputCacheOptions>>();
}
return ValueTask.FromResult<IOutputCachePolicy?>(null);
}

var policies = _options!.Value.NamedPolicies;

return policies != null && policies.TryGetValue(_policyName, out var cacheProfile)
? cacheProfile
: null;
return provider.GetPolicyAsync(_policyName);
}
}
3 changes: 3 additions & 0 deletions src/Middleware/OutputCaching/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
#nullable enable
Microsoft.AspNetCore.OutputCaching.IOutputCachePolicyProvider
Microsoft.AspNetCore.OutputCaching.IOutputCachePolicyProvider.GetBasePolicies() -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy!>!
Microsoft.AspNetCore.OutputCaching.IOutputCachePolicyProvider.GetPolicyAsync(string! policyName) -> System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy?>
Loading
Loading