Add output caching to the Delivery API (#15216)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.OutputCaching;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Caching;
|
||||
|
||||
internal sealed class DeliveryApiOutputCachePolicy : IOutputCachePolicy
|
||||
{
|
||||
private readonly TimeSpan _duration;
|
||||
|
||||
public DeliveryApiOutputCachePolicy(TimeSpan duration)
|
||||
=> _duration = duration;
|
||||
|
||||
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
IRequestPreviewService requestPreviewService = context
|
||||
.HttpContext
|
||||
.RequestServices
|
||||
.GetRequiredService<IRequestPreviewService>();
|
||||
|
||||
context.EnableOutputCaching = requestPreviewService.IsPreview() is false;
|
||||
context.ResponseExpirationTimeSpan = _duration;
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
|
||||
=> ValueTask.CompletedTask;
|
||||
|
||||
ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
|
||||
=> ValueTask.CompletedTask;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Caching;
|
||||
|
||||
internal sealed class OutputCachePipelineFilter : UmbracoPipelineFilter
|
||||
{
|
||||
public OutputCachePipelineFilter(string name)
|
||||
: base(name)
|
||||
=> PostPipeline = PostPipelineAction;
|
||||
|
||||
private void PostPipelineAction(IApplicationBuilder applicationBuilder)
|
||||
=> applicationBuilder.UseOutputCache();
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.OutputCaching;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
using Umbraco.Cms.Api.Delivery.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
|
||||
@@ -13,6 +15,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
|
||||
[ApiExplorerSettings(GroupName = "Content")]
|
||||
[LocalizeFromAcceptLanguageHeader]
|
||||
[ValidateStartItem]
|
||||
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.ContentCachePolicy)]
|
||||
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
|
||||
{
|
||||
protected IApiPublishedContentCache ApiPublishedContentCache { get; }
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.OutputCaching;
|
||||
using Umbraco.Cms.Api.Common.Builders;
|
||||
using Umbraco.Cms.Api.Delivery.Filters;
|
||||
using Umbraco.Cms.Api.Delivery.Routing;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
@@ -14,6 +16,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Media;
|
||||
[DeliveryApiMediaAccess]
|
||||
[VersionedDeliveryApiRoute("media")]
|
||||
[ApiExplorerSettings(GroupName = "Media")]
|
||||
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.MediaCachePolicy)]
|
||||
public abstract class MediaApiControllerBase : DeliveryApiControllerBase
|
||||
{
|
||||
private readonly IApiMediaWithCropsResponseBuilder _apiMediaWithCropsResponseBuilder;
|
||||
|
||||
@@ -3,9 +3,11 @@ using System.Text.Json.Serialization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Api.Delivery.Accessors;
|
||||
using Umbraco.Cms.Api.Delivery.Caching;
|
||||
using Umbraco.Cms.Api.Delivery.Configuration;
|
||||
using Umbraco.Cms.Api.Delivery.Handlers;
|
||||
using Umbraco.Cms.Api.Delivery.Json;
|
||||
@@ -14,10 +16,12 @@ using Umbraco.Cms.Api.Delivery.Routing;
|
||||
using Umbraco.Cms.Api.Delivery.Security;
|
||||
using Umbraco.Cms.Api.Delivery.Services;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Infrastructure.Security;
|
||||
using Umbraco.Cms.Web.Common.ApplicationBuilder;
|
||||
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
@@ -79,7 +83,38 @@ public static class UmbracoBuilderExtensions
|
||||
|
||||
// FIXME: remove this when Delivery API V1 is removed
|
||||
builder.Services.AddSingleton<MatcherPolicy, DeliveryApiItemsEndpointsMatcherPolicy>();
|
||||
|
||||
builder.AddOutputCache();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IUmbracoBuilder AddOutputCache(this IUmbracoBuilder builder)
|
||||
{
|
||||
DeliveryApiSettings.OutputCacheSettings outputCacheSettings =
|
||||
builder.Config.GetSection(Constants.Configuration.ConfigDeliveryApi).Get<DeliveryApiSettings>()?.OutputCache
|
||||
?? new DeliveryApiSettings.OutputCacheSettings();
|
||||
|
||||
if (outputCacheSettings.Enabled is false || outputCacheSettings is { ContentDuration.TotalSeconds: <= 0, MediaDuration.TotalSeconds: <= 0 })
|
||||
{
|
||||
return builder;
|
||||
}
|
||||
|
||||
builder.Services.AddOutputCache(options =>
|
||||
{
|
||||
options.AddBasePolicy(_ => { });
|
||||
|
||||
if (outputCacheSettings.ContentDuration.TotalSeconds > 0)
|
||||
{
|
||||
options.AddPolicy(Constants.DeliveryApi.OutputCache.ContentCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.ContentDuration));
|
||||
}
|
||||
|
||||
if (outputCacheSettings.MediaDuration.TotalSeconds > 0)
|
||||
{
|
||||
options.AddPolicy(Constants.DeliveryApi.OutputCache.MediaCachePolicy, new DeliveryApiOutputCachePolicy(outputCacheSettings.MediaDuration));
|
||||
}
|
||||
});
|
||||
|
||||
builder.Services.Configure<UmbracoPipelineOptions>(options => options.AddFilter(new OutputCachePipelineFilter("UmbracoDeliveryApiOutputCache")));
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ public class DeliveryApiSettings
|
||||
/// </summary>
|
||||
public MemberAuthorizationSettings? MemberAuthorization { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the settings for the Delivery API output cache.
|
||||
/// </summary>
|
||||
public OutputCacheSettings OutputCache { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if any member authorization type is enabled for the Delivery API.
|
||||
/// </summary>
|
||||
@@ -138,4 +143,42 @@ public class DeliveryApiSettings
|
||||
/// <remarks>These are only required if logout is to be used.</remarks>
|
||||
public Uri[] LogoutRedirectUrls { get; set; } = Array.Empty<Uri>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed configuration options for output caching of the Delivery API.
|
||||
/// </summary>
|
||||
public class OutputCacheSettings
|
||||
{
|
||||
private const string StaticDuration = "00:01:00"; // one minute
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Delivery API output should be cached.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the Delivery API output should be cached; otherwise, <c>false</c>.</value>
|
||||
/// <remarks>
|
||||
/// The default value is <c>false</c>.
|
||||
/// </remarks>
|
||||
[DefaultValue(StaticEnabled)]
|
||||
public bool Enabled { get; set; } = StaticEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how long the Content Delivery API output should be cached.
|
||||
/// </summary>
|
||||
/// <value>Cache lifetime.</value>
|
||||
/// <remarks>
|
||||
/// The default cache duration is one minute, if this configuration value is not provided.
|
||||
/// </remarks>
|
||||
[DefaultValue(StaticDuration)]
|
||||
public TimeSpan ContentDuration { get; set; } = TimeSpan.Parse(StaticDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how long the Media Delivery API output should be cached.
|
||||
/// </summary>
|
||||
/// <value>Cache lifetime.</value>
|
||||
/// <remarks>
|
||||
/// The default cache duration is one minute, if this configuration value is not provided.
|
||||
/// </remarks>
|
||||
[DefaultValue(StaticDuration)]
|
||||
public TimeSpan MediaDuration { get; set; } = TimeSpan.Parse(StaticDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,5 +17,21 @@ public static partial class Constants
|
||||
/// </summary>
|
||||
public const string PreviewContentPathPrefix = "preview-";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constants for Delivery API output cache.
|
||||
/// </summary>
|
||||
public static class OutputCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Output cache policy name for content
|
||||
/// </summary>
|
||||
public const string ContentCachePolicy = "DeliveryApiContent";
|
||||
|
||||
/// <summary>
|
||||
/// Output cache policy name for media
|
||||
/// </summary>
|
||||
public const string MediaCachePolicy = "DeliveryApiMedia";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user