Adds new event so we know when umbraco routes a value, ensure the IUmbracoWebsiteSecurity is initialized for front-end requests, cleans up some of the routing middleware, adds lots of notes

This commit is contained in:
Shannon
2021-03-01 12:51:07 +11:00
parent abb5911b24
commit 6148336d04
23 changed files with 180 additions and 85 deletions

View File

@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Cms.Core.Web;

View File

@@ -0,0 +1,33 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using Umbraco.Cms.Core.Web;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Events
{
/// <summary>
/// Notification raised when Umbraco routes a front-end request.
/// </summary>
public class UmbracoRoutedRequest : INotification
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestBegin"/> class.
/// </summary>
public UmbracoRoutedRequest(IUmbracoContext umbracoContext)
{
if (!umbracoContext.IsFrontEndUmbracoRequest())
{
throw new InvalidOperationException($"{nameof(UmbracoRoutedRequest)} is only valid for Umbraco front-end requests");
}
UmbracoContext = umbracoContext;
}
/// <summary>
/// Gets the <see cref="IUmbracoContext"/>
/// </summary>
public IUmbracoContext UmbracoContext { get; }
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Scoping;
@@ -18,12 +18,15 @@ namespace Umbraco.Cms.Core
{
private readonly IRequestCache _requestCache;
// TODO: Do they need to be static?? These are singleton instances IMO they shouldn't be static
// ReSharper disable StaticMemberInGenericType
private static readonly object Locker = new object();
private static bool _registered;
private static readonly object s_locker = new object();
private static bool s_registered;
// ReSharper restore StaticMemberInGenericType
protected abstract string ItemKey { get; }
private string _itemKey;
protected string ItemKey => _itemKey ?? (_itemKey = GetType().FullName);
// read
// http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html
@@ -43,34 +46,42 @@ namespace Umbraco.Cms.Core
private T NonContextValue
{
get => CallContext<T>.GetData(ItemKey);
set
{
CallContext<T>.SetData(ItemKey, value);
}
set => CallContext<T>.SetData(ItemKey, value);
}
protected HybridAccessorBase(IRequestCache requestCache)
{
_requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
lock (Locker)
lock (s_locker)
{
// register the itemKey once with SafeCallContext
if (_registered) return;
_registered = true;
if (s_registered)
{
return;
}
s_registered = true;
}
// ReSharper disable once VirtualMemberCallInConstructor
var itemKey = ItemKey; // virtual
SafeCallContext.Register(() =>
{
var value = CallContext<T>.GetData(itemKey);
T value = CallContext<T>.GetData(itemKey);
return value;
}, o =>
{
if (o == null) return;
var value = o as T;
if (value == null) throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o));
if (o == null)
{
return;
}
if (!(o is T value))
{
throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o));
}
CallContext<T>.SetData(itemKey, value);
});
}
@@ -93,9 +104,13 @@ namespace Umbraco.Cms.Core
NonContextValue = value;
}
else if (value == null)
{
_requestCache.Remove(ItemKey);
}
else
{
_requestCache.Set(ItemKey, value);
}
}
}
}

View File

@@ -1,12 +1,10 @@
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Events;
namespace Umbraco.Cms.Core
{
public class HybridEventMessagesAccessor : HybridAccessorBase<EventMessages>, IEventMessagesAccessor
{
protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor";
public HybridEventMessagesAccessor(IRequestCache requestCache)
: base(requestCache)
{ }

View File

@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache;
namespace Umbraco.Cms.Core.Models.PublishedContent
{
@@ -11,9 +11,6 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
: base(requestCache)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridVariationContextAccessor";
/// <summary>
/// Gets or sets the <see cref="VariationContext"/> object.
/// </summary>

View File

@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache;
namespace Umbraco.Cms.Core.Security
{
@@ -11,9 +11,6 @@ namespace Umbraco.Cms.Core.Security
: base(requestCache)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridBackofficeSecurityAccessor";
/// <summary>
/// Gets or sets the <see cref="IBackOfficeSecurity"/> object.
/// </summary>

View File

@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache;
namespace Umbraco.Cms.Core.Security
{
@@ -12,9 +12,6 @@ namespace Umbraco.Cms.Core.Security
: base(requestCache)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridUmbracoWebsiteSecurityAccessor";
/// <summary>
/// Gets or sets the <see cref="IUmbracoWebsiteSecurity"/> object.
/// </summary>

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Cms.Core
namespace Umbraco.Core.Security
{
/// <summary>
/// Creates and manages <see cref="IBackOfficeSecurity"/> instances.

View File

@@ -1,9 +1,10 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using Umbraco.Cms.Core.Models.Security;
namespace Umbraco.Cms.Core.Security
{
// TODO: I think we can kill this whole thing, the logic can just be in the controllers
public interface IUmbracoWebsiteSecurity
{
/// <summary>
@@ -35,18 +36,10 @@ namespace Umbraco.Cms.Core.Security
/// <returns>Result of update profile operation.</returns>
Task<UpdateMemberProfileResult> UpdateMemberProfileAsync(ProfileModel model);
/// <summary>
/// A helper method to perform the validation and logging in of a member.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <returns>Result of login operation.</returns>
// TODO: Kill this, we will just use the MemberManager / MemberSignInManager
Task<bool> LoginAsync(string username, string password);
/// <summary>
/// Check if a member is logged in
/// </summary>
/// <returns>True if logged in, false if not.</returns>
// TODO: Kill this, we will just use the MemberManager
bool IsLoggedIn();
/// <summary>
@@ -55,9 +48,7 @@ namespace Umbraco.Cms.Core.Security
/// <returns>Instance of <see cref="LoginStatusModel"/></returns>
Task<LoginStatusModel> GetCurrentLoginStatusAsync();
/// <summary>
/// Logs out the current member.
/// </summary>
// TODO: Kill this, we will just use the MemberManager
Task LogOutAsync();
/// <summary>

View File

@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Cache;
namespace Umbraco.Cms.Core.Web
{
@@ -14,9 +14,6 @@ namespace Umbraco.Cms.Core.Web
: base(requestCache)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridUmbracoContextAccessor";
/// <summary>
/// Gets or sets the <see cref="UmbracoContext"/> object.
/// </summary>

View File

@@ -12,6 +12,7 @@ using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Web;
using Umbraco.Core.Security;
namespace Umbraco.Cms.Infrastructure.HostedServices
{
@@ -106,7 +107,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
// - batched messenger should not depend on a current HttpContext
// but then what should be its "scope"? could we attach it to scopes?
// - and we should definitively *not* have to flush it here (should be auto)
//
// TODO: This dependency chain is broken and needs to be fixed.
// This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null
// This is a very ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the
// way it was intended.
_backofficeSecurityFactory.EnsureBackOfficeSecurity();
using UmbracoContextReference contextReference = _umbracoContextFactory.EnsureUmbracoContext();
try

View File

@@ -14,7 +14,6 @@ using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
@@ -25,6 +24,7 @@ using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Cms.Web.Website.Controllers;
using Umbraco.Core.Security;
using Umbraco.Extensions;
using Constants = Umbraco.Cms.Core.Constants;
@@ -122,6 +122,10 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest
}
};
// TODO: This dependency chain is broken and needs to be fixed.
// This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null
// This is a very ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the
// way it was intended.
backofficeSecurityFactory.EnsureBackOfficeSecurity();
umbracoContextFactory.EnsureUmbracoContext();

View File

@@ -36,6 +36,7 @@ using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.DependencyInjection;
using Umbraco.Cms.Tests.Integration.Extensions;
using Umbraco.Cms.Tests.Integration.Implementations;
using Umbraco.Core.Security;
using Umbraco.Extensions;
using Constants = Umbraco.Cms.Core.Constants;

View File

@@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
@@ -25,6 +24,7 @@ using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Cms.Web.BackOffice.Filters;
using Umbraco.Cms.Web.BackOffice.ModelBinders;
using Umbraco.Core.Security;
using Umbraco.Extensions;
using DataType = Umbraco.Cms.Core.Models.DataType;

View File

@@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure;
using Umbraco.Cms.Infrastructure.HostedServices;
using Umbraco.Core.Security;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices
{

View File

@@ -53,6 +53,7 @@ using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Cms.Web.Common.Security;
using Umbraco.Cms.Web.Common.Templates;
using Umbraco.Cms.Web.Common.UmbracoContext;
using Umbraco.Core.Security;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Extensions
@@ -263,6 +264,7 @@ namespace Umbraco.Extensions
builder.Services.AddUnique<IUmbracoContextFactory, UmbracoContextFactory>();
builder.Services.AddUnique<IBackOfficeSecurityFactory, BackOfficeSecurityFactory>();
builder.Services.AddUnique<IBackOfficeSecurityAccessor, HybridBackofficeSecurityAccessor>();
builder.AddNotificationHandler<UmbracoRoutedRequest, UmbracoWebsiteSecurityFactory>();
builder.Services.AddUnique<IUmbracoWebsiteSecurityAccessor, HybridUmbracoWebsiteSecurityAccessor>();
var umbracoApiControllerTypes = builder.TypeLoader.GetUmbracoApiControllers().ToList();

View File

@@ -10,9 +10,11 @@ using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.PublishedCache;
using Umbraco.Cms.Web.Common.Profiler;
using Umbraco.Core.Security;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Middleware
@@ -83,27 +85,26 @@ namespace Umbraco.Cms.Web.Common.Middleware
EnsureContentCacheInitialized();
_backofficeSecurityFactory.EnsureBackOfficeSecurity(); // Needs to be before UmbracoContext, TODO: Why?
// TODO: This dependency chain is broken and needs to be fixed.
// This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null
// This is ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the
// way it was intended.
_backofficeSecurityFactory.EnsureBackOfficeSecurity();
UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
Uri currentApplicationUrl = GetApplicationUrlFromCurrentRequest(context.Request);
_hostingEnvironment.EnsureApplicationMainUrl(currentApplicationUrl);
bool isFrontEndRequest = umbracoContextReference.UmbracoContext.IsFrontEndUmbracoRequest();
var pathAndQuery = context.Request.GetEncodedPathAndQuery();
try
{
if (isFrontEndRequest)
{
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery);
}
// Verbose log start of every request
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery);
try
{
{
await _eventAggregator.PublishAsync(new UmbracoRequestBegin(umbracoContextReference.UmbracoContext));
}
catch (Exception ex)
@@ -126,11 +127,10 @@ namespace Umbraco.Cms.Web.Common.Middleware
}
finally
{
if (isFrontEndRequest)
{
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
_logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds);
}
// Verbose log end of every request (in v8 we didn't log the end request of ALL requests, only the front-end which was
// strange since we always logged the beginning, so now we just log start/end of all requests)
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
_logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds);
try
{

View File

@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Core.Security;
namespace Umbraco.Cms.Web.Common.Security
{
// TODO: This is only for the back office, does it need to be in common?
// TODO: This is only for the back office, does it need to be in common? YES currently UmbracoContext has an transitive dependency on this which needs to be fixed/reviewed.
public class BackOfficeSecurityFactory: IBackOfficeSecurityFactory
{
@@ -14,11 +14,11 @@ namespace Umbraco.Cms.Web.Common.Security
private readonly IHttpContextAccessor _httpContextAccessor;
public BackOfficeSecurityFactory(
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IUserService userService,
IHttpContextAccessor httpContextAccessor)
{
_backOfficeSecurityAccessor = backofficeSecurityAccessor;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_userService = userService;
_httpContextAccessor = httpContextAccessor;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -11,9 +11,8 @@ using Umbraco.Cms.Core.Models.Security;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Cms.Web.Website.Security
namespace Umbraco.Cms.Web.Common.Security
{
public class UmbracoWebsiteSecurity : IUmbracoWebsiteSecurity
{
@@ -36,7 +35,7 @@ namespace Umbraco.Cms.Web.Website.Security
/// <inheritdoc/>
public RegisterModel CreateRegistrationModel(string memberTypeAlias = null)
{
var providedOrDefaultMemberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.DefaultAlias;
var providedOrDefaultMemberTypeAlias = memberTypeAlias ?? Core.Constants.Conventions.MemberTypes.DefaultAlias;
var memberType = _memberTypeService.Get(providedOrDefaultMemberTypeAlias);
if (memberType == null)
{
@@ -114,7 +113,7 @@ namespace Umbraco.Cms.Web.Website.Security
public Task<RegisterMemberStatus> RegisterMemberAsync(RegisterModel model, bool logMemberIn = true)
{
throw new System.NotImplementedException();
throw new NotImplementedException();
}
/// <inheritdoc/>

View File

@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
namespace Umbraco.Cms.Web.Common.Security
{
/// <summary>
/// Ensures that the <see cref="IUmbracoWebsiteSecurity"/> is populated on a front-end request
/// </summary>
internal sealed class UmbracoWebsiteSecurityFactory : INotificationHandler<UmbracoRoutedRequest>
{
private readonly IUmbracoWebsiteSecurityAccessor _umbracoWebsiteSecurityAccessor;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IMemberService _memberService;
private readonly IMemberTypeService _memberTypeService;
private readonly IShortStringHelper _shortStringHelper;
public UmbracoWebsiteSecurityFactory(
IUmbracoWebsiteSecurityAccessor umbracoWebsiteSecurityAccessor,
IHttpContextAccessor httpContextAccessor,
IMemberService memberService,
IMemberTypeService memberTypeService,
IShortStringHelper shortStringHelper)
{
_umbracoWebsiteSecurityAccessor = umbracoWebsiteSecurityAccessor;
_httpContextAccessor = httpContextAccessor;
_memberService = memberService;
_memberTypeService = memberTypeService;
_shortStringHelper = shortStringHelper;
}
public void Handle(UmbracoRoutedRequest notification)
{
if (_umbracoWebsiteSecurityAccessor.WebsiteSecurity is null)
{
_umbracoWebsiteSecurityAccessor.WebsiteSecurity = new UmbracoWebsiteSecurity(
_httpContextAccessor,
_memberService,
_memberTypeService,
_shortStringHelper);
}
}
}
}

View File

@@ -18,8 +18,13 @@ namespace Umbraco.Cms.Web.Website.Controllers
{
private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor;
public UmbLoginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory,
ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider,
public UmbLoginController(
IUmbracoContextAccessor umbracoContextAccessor,
IUmbracoDatabaseFactory databaseFactory,
ServiceContext services,
AppCaches appCaches,
IProfilingLogger profilingLogger,
IPublishedUrlProvider publishedUrlProvider,
IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
{
@@ -36,9 +41,6 @@ namespace Umbraco.Cms.Web.Website.Controllers
return CurrentUmbracoPage();
}
// TODO: This is supposed to be for members! not users
//throw new NotImplementedException("Implement this for members");
if (await _websiteSecurityAccessor.WebsiteSecurity.LoginAsync(model.Username, model.Password) == false)
{
// Don't add a field level error, just model level.

View File

@@ -2,8 +2,10 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Infrastructure.DependencyInjection;
using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Cms.Web.Common.Security;
using Umbraco.Cms.Web.Website.Collections;
using Umbraco.Cms.Web.Website.Controllers;
using Umbraco.Cms.Web.Website.Routing;
@@ -40,7 +42,7 @@ namespace Umbraco.Extensions
builder.Services.AddSingleton<IUmbracoRenderingDefaults, UmbracoRenderingDefaults>();
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
builder.Services.AddSingleton<FrontEndRoutes>();
builder.Services.AddSingleton<FrontEndRoutes>();
builder
.AddDistributedCache()

View File

@@ -13,6 +13,7 @@ using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
@@ -48,6 +49,7 @@ namespace Umbraco.Cms.Web.Website.Routing
private readonly IRoutableDocumentFilter _routableDocumentFilter;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly IControllerActionSearcher _controllerActionSearcher;
private readonly IEventAggregator _eventAggregator;
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRouteValueTransformer"/> class.
@@ -62,7 +64,8 @@ namespace Umbraco.Cms.Web.Website.Routing
IUmbracoRouteValuesFactory routeValuesFactory,
IRoutableDocumentFilter routableDocumentFilter,
IDataProtectionProvider dataProtectionProvider,
IControllerActionSearcher controllerActionSearcher)
IControllerActionSearcher controllerActionSearcher,
IEventAggregator eventAggregator)
{
if (globalSettings is null)
{
@@ -79,6 +82,7 @@ namespace Umbraco.Cms.Web.Website.Routing
_routableDocumentFilter = routableDocumentFilter ?? throw new ArgumentNullException(nameof(routableDocumentFilter));
_dataProtectionProvider = dataProtectionProvider;
_controllerActionSearcher = controllerActionSearcher;
_eventAggregator = eventAggregator;
}
/// <inheritdoc/>
@@ -117,6 +121,10 @@ namespace Umbraco.Cms.Web.Website.Routing
// Store the route values as a httpcontext feature
httpContext.Features.Set(umbracoRouteValues);
// publish an event that we've routed a request
// TODO: does this occur on 404 or have we already returned?
await _eventAggregator.PublishAsync(new UmbracoRoutedRequest(_umbracoContextAccessor.UmbracoContext));
// Need to check if there is form data being posted back to an Umbraco URL
PostedDataProxyInfo postedInfo = GetFormInfo(httpContext, values);
if (postedInfo != null)