diff --git a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureMvcOptions.cs b/src/Umbraco.Cms.Api.Common/Configuration/ConfigureMvcOptions.cs deleted file mode 100644 index 0eefc6710c..0000000000 --- a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureMvcOptions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Api.Common.Routing; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; - -namespace Umbraco.Cms.Api.Common.Configuration; - -public class ConfigureMvcOptions : IConfigureOptions -{ - private readonly IOptions _globalSettings; - - public ConfigureMvcOptions(IOptions globalSettings) => _globalSettings = globalSettings; - - public void Configure(MvcOptions options) - { - // these MVC options may be applied more than once; let's make sure we only execute once. - if (options.Conventions.Any(convention => convention is UmbracoBackofficeToken)) - { - return; - } - - // Replace the BackOfficeToken in routes. - - var backofficePath = _globalSettings.Value.UmbracoPath.TrimStart(Constants.CharArrays.TildeForwardSlash); - options.Conventions.Add(new UmbracoBackofficeToken(Constants.Web.AttributeRouting.BackOfficeToken, backofficePath)); - } -} diff --git a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderApiExtensions.cs b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderApiExtensions.cs index 8b940a8b1d..535c9b90fd 100644 --- a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderApiExtensions.cs +++ b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderApiExtensions.cs @@ -22,10 +22,6 @@ public static class UmbracoBuilderApiExtensions { public static IUmbracoBuilder AddUmbracoApiOpenApiUI(this IUmbracoBuilder builder) { - builder.Services.ConfigureOptions(); - builder.Services.ConfigureOptions(); - builder.Services.AddApiVersioning().AddApiExplorer(); - builder.Services.AddSwaggerGen(); builder.Services.ConfigureOptions(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj b/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj index c6569f0262..fd5e7d4b83 100644 --- a/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj +++ b/src/Umbraco.Cms.Api.Common/Umbraco.Cms.Api.Common.csproj @@ -8,9 +8,7 @@ Umbraco.Cms.Api.Common - - - + diff --git a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs index b6e52ada7c..6370fc24f9 100644 --- a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs @@ -34,7 +34,6 @@ public static class UmbracoBuilderExtensions builder .Services - .ConfigureOptions() .AddControllers() .AddJsonOptions(Constants.JsonOptionsNames.DeliveryApi, options => { diff --git a/src/Umbraco.Cms.Api.Delivery/Filters/ValidateStartItemAttribute.cs b/src/Umbraco.Cms.Api.Delivery/Filters/ValidateStartItemAttribute.cs index 8e5d1bbb38..54f2b06b14 100644 --- a/src/Umbraco.Cms.Api.Delivery/Filters/ValidateStartItemAttribute.cs +++ b/src/Umbraco.Cms.Api.Delivery/Filters/ValidateStartItemAttribute.cs @@ -14,19 +14,20 @@ internal sealed class ValidateStartItemAttribute : TypeFilterAttribute private class ValidateStartItemFilter : IActionFilter { - private readonly IRequestStartItemProvider _requestStartItemProvider; + private readonly IRequestStartItemProviderAccessor _requestStartItemProviderAccessor; - public ValidateStartItemFilter(IRequestStartItemProvider requestStartItemProvider) - => _requestStartItemProvider = requestStartItemProvider; + public ValidateStartItemFilter(IRequestStartItemProviderAccessor requestStartItemProviderAccessor) + => _requestStartItemProviderAccessor = requestStartItemProviderAccessor; public void OnActionExecuting(ActionExecutingContext context) { - if (_requestStartItemProvider.RequestedStartItem() is null) + if (_requestStartItemProviderAccessor.TryGetValue(out IRequestStartItemProvider? requestStartItemProvider) is false + || requestStartItemProvider.RequestedStartItem() is null) { return; } - IPublishedContent? startItem = _requestStartItemProvider.GetStartItem(); + IPublishedContent? startItem = requestStartItemProvider.GetStartItem(); if (startItem is null) { diff --git a/src/Umbraco.Cms.Api.Delivery/Routing/VersionedDeliveryApiRouteAttribute.cs b/src/Umbraco.Cms.Api.Delivery/Routing/VersionedDeliveryApiRouteAttribute.cs index 468970cfe1..4853046318 100644 --- a/src/Umbraco.Cms.Api.Delivery/Routing/VersionedDeliveryApiRouteAttribute.cs +++ b/src/Umbraco.Cms.Api.Delivery/Routing/VersionedDeliveryApiRouteAttribute.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Common.Routing; +using Umbraco.Cms.Web.Common.Routing; namespace Umbraco.Cms.Api.Delivery.Routing; diff --git a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj index 5efada8f00..33a3105b73 100644 --- a/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj +++ b/src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiExplorerOptions.cs b/src/Umbraco.Web.Common/Configuration/ConfigureApiExplorerOptions.cs similarity index 94% rename from src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiExplorerOptions.cs rename to src/Umbraco.Web.Common/Configuration/ConfigureApiExplorerOptions.cs index 50b1c2a93d..ed914f8088 100644 --- a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiExplorerOptions.cs +++ b/src/Umbraco.Web.Common/Configuration/ConfigureApiExplorerOptions.cs @@ -2,7 +2,7 @@ using Asp.Versioning; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.Options; -namespace Umbraco.Cms.Api.Common.Configuration; +namespace Umbraco.Cms.Web.Common.Configuration; public sealed class ConfigureApiExplorerOptions : IConfigureOptions { diff --git a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiVersioningOptions.cs b/src/Umbraco.Web.Common/Configuration/ConfigureApiVersioningOptions.cs similarity index 92% rename from src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiVersioningOptions.cs rename to src/Umbraco.Web.Common/Configuration/ConfigureApiVersioningOptions.cs index b00d575ab6..a15019b165 100644 --- a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureApiVersioningOptions.cs +++ b/src/Umbraco.Web.Common/Configuration/ConfigureApiVersioningOptions.cs @@ -1,8 +1,7 @@ - using Asp.Versioning; using Microsoft.Extensions.Options; -namespace Umbraco.Cms.Api.Common.Configuration; +namespace Umbraco.Cms.Web.Common.Configuration; public sealed class ConfigureApiVersioningOptions : IConfigureOptions { diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index dc94fa3dc8..0fa1d7fc4b 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -46,6 +46,7 @@ using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Web.Common; using Umbraco.Cms.Web.Common.ApplicationModels; using Umbraco.Cms.Web.Common.AspNetCore; +using Umbraco.Cms.Web.Common.Configuration; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.FileProviders; @@ -291,6 +292,9 @@ public static partial class UmbracoBuilderExtensions options.Cookie.HttpOnly = true; }); + builder.Services.ConfigureOptions(); + builder.Services.ConfigureOptions(); + builder.Services.AddApiVersioning().AddApiExplorer(); builder.Services.ConfigureOptions(); builder.Services.ConfigureOptions(); builder.Services.TryAddEnumerable(ServiceDescriptor diff --git a/src/Umbraco.Web.Common/Mvc/UmbracoMvcConfigureOptions.cs b/src/Umbraco.Web.Common/Mvc/UmbracoMvcConfigureOptions.cs index eb4991fec3..83a2e28834 100644 --- a/src/Umbraco.Web.Common/Mvc/UmbracoMvcConfigureOptions.cs +++ b/src/Umbraco.Web.Common/Mvc/UmbracoMvcConfigureOptions.cs @@ -1,7 +1,11 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Web.Common.Filters; using Umbraco.Cms.Web.Common.ModelBinders; +using Umbraco.Cms.Web.Common.Routing; using Umbraco.Cms.Web.Common.Validators; namespace Umbraco.Cms.Web.Common.Mvc; @@ -15,6 +19,17 @@ namespace Umbraco.Cms.Web.Common.Mvc; /// public class UmbracoMvcConfigureOptions : IConfigureOptions { + private readonly GlobalSettings _globalSettings; + + [Obsolete("Use the constructor that accepts GlobalSettings options. Will be removed in V14.")] + public UmbracoMvcConfigureOptions() + : this(StaticServiceProvider.Instance.GetRequiredService>()) + { + } + + public UmbracoMvcConfigureOptions(IOptions globalSettings) + => _globalSettings = globalSettings.Value; + /// public void Configure(MvcOptions options) { @@ -22,5 +37,13 @@ public class UmbracoMvcConfigureOptions : IConfigureOptions options.ModelValidatorProviders.Insert(0, new BypassRenderingModelValidatorProvider()); options.ModelMetadataDetailsProviders.Add(new BypassRenderingModelValidationMetadataProvider()); options.Filters.Insert(0, new EnsurePartialViewMacroViewContextFilterAttribute()); + + // these MVC options may be applied more than once; let's make sure we only add these conventions once. + if (options.Conventions.Any(convention => convention is UmbracoBackofficeToken) is false) + { + // Replace the BackOfficeToken in routes. + var backofficePath = _globalSettings.UmbracoPath.TrimStart(Core.Constants.CharArrays.TildeForwardSlash); + options.Conventions.Add(new UmbracoBackofficeToken(Core.Constants.Web.AttributeRouting.BackOfficeToken, backofficePath)); + } } } diff --git a/src/Umbraco.Cms.Api.Common/Routing/BackOfficeRouteAttribute.cs b/src/Umbraco.Web.Common/Routing/BackOfficeRouteAttribute.cs similarity index 72% rename from src/Umbraco.Cms.Api.Common/Routing/BackOfficeRouteAttribute.cs rename to src/Umbraco.Web.Common/Routing/BackOfficeRouteAttribute.cs index 733e856aaf..f93a4f3a89 100644 --- a/src/Umbraco.Cms.Api.Common/Routing/BackOfficeRouteAttribute.cs +++ b/src/Umbraco.Web.Common/Routing/BackOfficeRouteAttribute.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core; -namespace Umbraco.Cms.Api.Common.Routing; +namespace Umbraco.Cms.Web.Common.Routing; /// /// Routes a controller within the backoffice area, I.E /umbraco @@ -11,7 +10,7 @@ public class BackOfficeRouteAttribute : RouteAttribute // All this does is append [umbracoBackoffice]/ to the route, // this is then replaced with whatever is configures as UmbracoPath by the UmbracoBackofficeToken convention public BackOfficeRouteAttribute(string template) - : base($"[{Constants.Web.AttributeRouting.BackOfficeToken}]/" + template.TrimStart('/')) + : base($"[{Core.Constants.Web.AttributeRouting.BackOfficeToken}]/" + template.TrimStart('/')) { } } diff --git a/src/Umbraco.Cms.Api.Common/Routing/UmbracoBackofficeToken.cs b/src/Umbraco.Web.Common/Routing/UmbracoBackofficeToken.cs similarity index 97% rename from src/Umbraco.Cms.Api.Common/Routing/UmbracoBackofficeToken.cs rename to src/Umbraco.Web.Common/Routing/UmbracoBackofficeToken.cs index 1b87d5e82b..33388398bc 100644 --- a/src/Umbraco.Cms.Api.Common/Routing/UmbracoBackofficeToken.cs +++ b/src/Umbraco.Web.Common/Routing/UmbracoBackofficeToken.cs @@ -1,7 +1,7 @@ using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc.ApplicationModels; -namespace Umbraco.Cms.Api.Common.Routing; +namespace Umbraco.Cms.Web.Common.Routing; /// /// Adds a custom template token for specifying backoffice route with attribute routing diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 8b92241b28..739ad33aa6 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -11,6 +11,8 @@ + +