UmbracoRequestBegin and UmbracoRequestEnd part of the core proj and changed to just have a IUmbracoContext reference. Removes IRequestAccessor events, Fixes the DatabaseServerMessengerNotificationHandler weirdness, Removes UmbracoModule and friends and old routing events and statuses
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
|
||||
public class UmbracoApplicationStarting : INotification
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
23
src/Umbraco.Core/Events/UmbracoRequestBegin.cs
Normal file
23
src/Umbraco.Core/Events/UmbracoRequestBegin.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Notification raised on each request begin.
|
||||
/// </summary>
|
||||
public class UmbracoRequestBegin : INotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRequestBegin"/> class.
|
||||
/// </summary>
|
||||
public UmbracoRequestBegin(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IUmbracoContext"/>
|
||||
/// </summary>
|
||||
public IUmbracoContext UmbracoContext { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
@@ -13,11 +13,11 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRequestEnd"/> class.
|
||||
/// </summary>
|
||||
public UmbracoRequestEnd(HttpContext httpContext) => HttpContext = httpContext;
|
||||
public UmbracoRequestEnd(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="HttpContext"/>
|
||||
/// Gets the <see cref="IUmbracoContext"/>
|
||||
/// </summary>
|
||||
public HttpContext HttpContext { get; }
|
||||
public IUmbracoContext UmbracoContext { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the outcome of trying to route an incoming request.
|
||||
/// </summary>
|
||||
public enum EnsureRoutableOutcome
|
||||
{
|
||||
/// <summary>
|
||||
/// Request routes to a document.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Umbraco was ready and configured, and has content.</para>
|
||||
/// <para>The request looks like it can be a route to a document. This does not
|
||||
/// mean that there *is* a matching document, ie the request might end up returning
|
||||
/// 404.</para>
|
||||
/// </remarks>
|
||||
IsRoutable = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Request does not route to a document.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Umbraco was ready and configured, and has content.</para>
|
||||
/// <para>The request does not look like it can be a route to a document. Can be
|
||||
/// anything else eg back-office, surface controller...</para>
|
||||
/// </remarks>
|
||||
NotDocumentRequest = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Umbraco was not ready.
|
||||
/// </summary>
|
||||
NotReady = 11,
|
||||
|
||||
/// <summary>
|
||||
/// There was no content at all.
|
||||
/// </summary>
|
||||
NoContent = 12
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args containing information about why the request was not routable, or if it is routable
|
||||
/// </summary>
|
||||
public class RoutableAttemptEventArgs : UmbracoRequestEventArgs
|
||||
{
|
||||
public EnsureRoutableOutcome Outcome { get; private set; }
|
||||
|
||||
public RoutableAttemptEventArgs(EnsureRoutableOutcome reason, IUmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
Outcome = reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args used for event launched during a request (like in the UmbracoModule)
|
||||
/// </summary>
|
||||
public class UmbracoRequestEventArgs : EventArgs
|
||||
{
|
||||
public IUmbracoContext UmbracoContext { get; private set; }
|
||||
|
||||
public UmbracoRequestEventArgs(IUmbracoContext umbracoContext)
|
||||
{
|
||||
UmbracoContext = umbracoContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,22 @@ namespace Umbraco.Web
|
||||
{
|
||||
public interface IRequestAccessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the request/form/querystring value for the given name
|
||||
/// </summary>
|
||||
string GetRequestValue(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the query string value for the given name
|
||||
/// </summary>
|
||||
string GetQueryStringValue(string name);
|
||||
event EventHandler<UmbracoRequestEventArgs> EndRequest;
|
||||
event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current request uri
|
||||
/// </summary>
|
||||
Uri GetRequestUrl();
|
||||
|
||||
// TODO: Not sure this belongs here but we can leave it for now
|
||||
// TODO: This doesn't belongs here
|
||||
Uri GetApplicationUrl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ namespace Umbraco.Infrastructure.Cache
|
||||
/// <summary>
|
||||
/// Ensures that distributed cache events are setup and the <see cref="IServerMessenger"/> is initialized
|
||||
/// </summary>
|
||||
public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler<UmbracoApplicationStarting>
|
||||
public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler<UmbracoApplicationStarting>, INotificationHandler<UmbracoRequestEnd>
|
||||
{
|
||||
private readonly IServerMessenger _messenger;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly IDistributedCacheBinder _distributedCacheBinder;
|
||||
private readonly ILogger<DatabaseServerMessengerNotificationHandler> _logger;
|
||||
@@ -24,12 +23,10 @@ namespace Umbraco.Infrastructure.Cache
|
||||
/// </summary>
|
||||
public DatabaseServerMessengerNotificationHandler(
|
||||
IServerMessenger serverMessenger,
|
||||
IRequestAccessor requestAccessor,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IDistributedCacheBinder distributedCacheBinder,
|
||||
ILogger<DatabaseServerMessengerNotificationHandler> logger)
|
||||
{
|
||||
_requestAccessor = requestAccessor;
|
||||
_databaseFactory = databaseFactory;
|
||||
_distributedCacheBinder = distributedCacheBinder;
|
||||
_logger = logger;
|
||||
@@ -38,19 +35,6 @@ namespace Umbraco.Infrastructure.Cache
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Handle(UmbracoApplicationStarting notification)
|
||||
{
|
||||
// The scheduled tasks - TouchServerTask and InstructionProcessTask - run as .NET Core hosted services.
|
||||
// The former (as well as other hosted services that run outside of an HTTP request context) depends on the application URL
|
||||
// being available (via IRequestAccessor), which can only be retrieved within an HTTP request (unless it's explicitly configured).
|
||||
// Hence we hook up a one-off task on an HTTP request to ensure this is retrieved, which caches the value and makes it available
|
||||
// for the hosted services to use when the HTTP request is not available.
|
||||
_requestAccessor.RouteAttempt += EnsureApplicationUrlOnce;
|
||||
_requestAccessor.EndRequest += EndRequest;
|
||||
|
||||
Startup();
|
||||
}
|
||||
|
||||
private void Startup()
|
||||
{
|
||||
if (_databaseFactory.CanConnect == false)
|
||||
{
|
||||
@@ -65,28 +49,9 @@ namespace Umbraco.Infrastructure.Cache
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: I don't really know or think that the Application Url plays a role anymore with the DB dist cache,
|
||||
// this might be really old stuff. I 'think' all this is doing is ensuring that the IRequestAccessor.GetApplicationUrl
|
||||
// is definitely called during the first request. If that is still required, that logic doesn't belong here. That logic
|
||||
// should be part of it's own service/middleware. There's also TODO notes within IRequestAccessor.GetApplicationUrl directly
|
||||
// mentioning that the property doesn't belong on that service either. This should be investigated and resolved in a separate task.
|
||||
private void EnsureApplicationUrlOnce(object sender, RoutableAttemptEventArgs e)
|
||||
{
|
||||
if (e.Outcome == EnsureRoutableOutcome.IsRoutable || e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
|
||||
{
|
||||
_requestAccessor.RouteAttempt -= EnsureApplicationUrlOnce;
|
||||
EnsureApplicationUrl();
|
||||
}
|
||||
}
|
||||
|
||||
// By retrieving the application URL within the context of a request (as we are here in responding
|
||||
// to the IRequestAccessor's RouteAttempt event), we'll get it from the HTTP context and save it for
|
||||
// future requests that may not be within an HTTP request (e.g. from hosted services).
|
||||
private void EnsureApplicationUrl() => _requestAccessor.GetApplicationUrl();
|
||||
|
||||
/// <summary>
|
||||
/// Clear the batch on end request
|
||||
/// </summary>
|
||||
private void EndRequest(object sender, UmbracoRequestEventArgs e) => _messenger?.SendMessages();
|
||||
public void Handle(UmbracoRequestEnd notification) => _messenger?.SendMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Umbraco.Infrastructure.DependencyInjection
|
||||
builder.SetDatabaseServerMessengerCallbacks(GetCallbacks);
|
||||
builder.SetServerMessenger<BatchedDatabaseServerMessenger>();
|
||||
builder.AddNotificationHandler<UmbracoApplicationStarting, DatabaseServerMessengerNotificationHandler>();
|
||||
builder.AddNotificationHandler<UmbracoRequestEnd, DatabaseServerMessengerNotificationHandler>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Umbraco.ModelsBuilder.Embedded
|
||||
|
||||
public void Handle(UmbracoRequestEnd notification)
|
||||
{
|
||||
if (IsEnabled && _mainDom.IsMainDom && !notification.HttpContext.Request.IsClientSideRequest())
|
||||
if (IsEnabled && _mainDom.IsMainDom)
|
||||
{
|
||||
GenerateModelsIfRequested();
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.Routing
|
||||
{
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
public class UmbracoModuleTests : BaseWebTest
|
||||
{
|
||||
private UmbracoInjectedModule _module;
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
|
||||
// FIXME: be able to get the UmbracoModule from the container. any reason settings were from testobjects?
|
||||
//create the module
|
||||
var logger = Mock.Of<ILogger>();
|
||||
|
||||
var globalSettings = new GlobalSettings { ReservedPaths = GlobalSettings.StaticReservedPaths + "~/umbraco" };
|
||||
var runtime = Umbraco.Core.RuntimeState.Booting();
|
||||
|
||||
_module = new UmbracoInjectedModule
|
||||
(
|
||||
runtime,
|
||||
logger,
|
||||
Mock.Of<IUmbracoContextFactory>(),
|
||||
globalSettings,
|
||||
HostingEnvironment
|
||||
);
|
||||
|
||||
runtime.Level = RuntimeLevel.Run;
|
||||
|
||||
|
||||
//SettingsForTests.ReservedPaths = "~/umbraco,~/install/";
|
||||
//SettingsForTests.ReservedUrls = "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd";
|
||||
|
||||
}
|
||||
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
|
||||
_module.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
// do not test for /base here as it's handled before EnsureUmbracoRoutablePage is called
|
||||
[TestCase("/umbraco_client/Tree/treeIcons.css", false)]
|
||||
[TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", false)]
|
||||
[TestCase("/umbraco/editContent.aspx", false)]
|
||||
[TestCase("/install/default.aspx", false)]
|
||||
[TestCase("/install/?installStep=license", false)]
|
||||
[TestCase("/install?installStep=license", false)]
|
||||
[TestCase("/install/test.aspx", false)]
|
||||
[TestCase("/config/splashes/noNodes.aspx", false)]
|
||||
[TestCase("/", true)]
|
||||
[TestCase("/home.aspx", true)]
|
||||
[TestCase("/umbraco-test", true)]
|
||||
[TestCase("/install-test", true)]
|
||||
public void Ensure_Request_Routable(string url, bool assert)
|
||||
{
|
||||
var httpContextFactory = new FakeHttpContextFactory(url);
|
||||
var httpContext = httpContextFactory.HttpContext;
|
||||
var umbracoContext = GetUmbracoContext(url);
|
||||
|
||||
var result = _module.EnsureUmbracoRoutablePage(umbracoContext, httpContext);
|
||||
|
||||
Assert.AreEqual(assert, result.Success);
|
||||
}
|
||||
|
||||
//NOTE: This test shows how we can test most of the HttpModule, it however is testing a method that no longer exists and is testing too much,
|
||||
// we need to write unit tests for each of the components: NiceUrlProvider, all of the Lookup classes, etc...
|
||||
// to ensure that each one is individually tested.
|
||||
|
||||
//[TestCase("/", 1046)]
|
||||
//[TestCase("/home.aspx", 1046)]
|
||||
//[TestCase("/home/sub1.aspx", 1173)]
|
||||
//[TestCase("/home.aspx?altTemplate=blah", 1046)]
|
||||
//public void Process_Front_End_Document_Request_Match_Node(string url, int nodeId)
|
||||
//{
|
||||
// var httpContextFactory = new FakeHttpContextFactory(url);
|
||||
// var httpContext = httpContextFactory.HttpContext;
|
||||
// var umbracoContext = new UmbracoContext(httpContext, ApplicationContext.Current, new NullRoutesCache());
|
||||
// var contentStore = new ContentStore(umbracoContext);
|
||||
// var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
|
||||
// umbracoContext.RoutingContext = new RoutingContext(
|
||||
// new IPublishedContentLookup[] {new LookupByNiceUrl()},
|
||||
// new DefaultLastChanceLookup(),
|
||||
// contentStore,
|
||||
// niceUrls);
|
||||
|
||||
// StateHelper.HttpContext = httpContext;
|
||||
|
||||
// //because of so much dependency on the db, we need to create som stuff here, i originally abstracted out stuff but
|
||||
// //was turning out to be quite a deep hole because ultimately we'd have to abstract the old 'Domain' and 'Language' classes
|
||||
// Domain.MakeNew("Test.com", 1000, Language.GetByCultureCode("en-US").id);
|
||||
|
||||
// //need to create a template with id 1045
|
||||
// var template = Template.MakeNew("test", new User(0));
|
||||
|
||||
// SetupUmbracoContextForTest(umbracoContext, template);
|
||||
|
||||
// _module.AssignDocumentRequest(httpContext, umbracoContext, httpContext.Request.Url);
|
||||
|
||||
// Assert.IsNotNull(umbracoContext.PublishedContentRequest);
|
||||
// Assert.IsNotNull(umbracoContext.PublishedContentRequest.XmlNode);
|
||||
// Assert.IsFalse(umbracoContext.PublishedContentRequest.IsRedirect);
|
||||
// Assert.IsFalse(umbracoContext.PublishedContentRequest.Is404);
|
||||
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentCulture);
|
||||
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentUICulture);
|
||||
// Assert.AreEqual(nodeId, umbracoContext.PublishedContentRequest.NodeId);
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,6 @@ namespace Umbraco.Tests.Testing
|
||||
services.AddUnique(ipResolver);
|
||||
services.AddUnique<IPasswordHasher, AspNetPasswordHasher>();
|
||||
services.AddUnique(TestHelper.ShortStringHelper);
|
||||
services.AddUnique<IRequestAccessor, AspNetRequestAccessor>();
|
||||
services.AddUnique<IPublicAccessChecker, PublicAccessChecker>();
|
||||
|
||||
|
||||
|
||||
@@ -243,7 +243,6 @@
|
||||
<Compile Include="TestHelpers\TestHelper.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestHelpers\FakeHttpContextFactory.cs" />
|
||||
<Compile Include="Routing\UmbracoModuleTests.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\DictionaryPublishedContent.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\DomainCache.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\PreviewContent.cs" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -10,42 +11,45 @@ using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Common.AspNetCore
|
||||
{
|
||||
public class AspNetCoreRequestAccessor : IRequestAccessor, INotificationHandler<UmbracoRequestBegin>, INotificationHandler<UmbracoRequestEnd>
|
||||
public class AspNetCoreRequestAccessor : IRequestAccessor, INotificationHandler<UmbracoRequestBegin>
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly WebRoutingSettings _webRoutingSettings;
|
||||
private readonly ISet<string> _applicationUrls = new HashSet<string>();
|
||||
private Uri _currentApplicationUrl;
|
||||
private object _initLocker = new object();
|
||||
private bool _hasAppUrl = false;
|
||||
private bool _isInit = false;
|
||||
|
||||
public AspNetCoreRequestAccessor(IHttpContextAccessor httpContextAccessor,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AspNetCoreRequestAccessor"/> class.
|
||||
/// </summary>
|
||||
public AspNetCoreRequestAccessor(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IOptions<WebRoutingSettings> webRoutingSettings)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_webRoutingSettings = webRoutingSettings.Value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetRequestValue(string name) => GetFormValue(name) ?? GetQueryStringValue(name);
|
||||
|
||||
public string GetFormValue(string name)
|
||||
private string GetFormValue(string name)
|
||||
{
|
||||
var request = _httpContextAccessor.GetRequiredHttpContext().Request;
|
||||
if (!request.HasFormContentType) return null;
|
||||
return request.Form[name];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GetQueryStringValue(string name) => _httpContextAccessor.GetRequiredHttpContext().Request.Query[name];
|
||||
|
||||
public event EventHandler<UmbracoRequestEventArgs> EndRequest;
|
||||
|
||||
public event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Uri GetRequestUrl() => _httpContextAccessor.HttpContext != null ? new Uri(_httpContextAccessor.HttpContext.Request.GetEncodedUrl()) : null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Uri GetApplicationUrl()
|
||||
{
|
||||
// Fixme: This causes problems with site swap on azure because azure pre-warms a site by calling into `localhost` and when it does that
|
||||
@@ -80,17 +84,16 @@ namespace Umbraco.Web.Common.AspNetCore
|
||||
return _currentApplicationUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This just initializes the application URL on first request attempt
|
||||
/// TODO: This doesn't belong here, the GetApplicationUrl doesn't belong to IRequestAccessor
|
||||
/// this should be part of middleware not a lazy init based on an INotification
|
||||
/// </summary>
|
||||
public void Handle(UmbracoRequestBegin notification)
|
||||
{
|
||||
var reason = EnsureRoutableOutcome.IsRoutable; //TODO get the correct value here like in UmbracoInjectedModule
|
||||
RouteAttempt?.Invoke(this, new RoutableAttemptEventArgs(reason, _umbracoContextAccessor.UmbracoContext));
|
||||
}
|
||||
|
||||
public void Handle(UmbracoRequestEnd notification)
|
||||
{
|
||||
EndRequest?.Invoke(this, new UmbracoRequestEventArgs(_umbracoContextAccessor.UmbracoContext));
|
||||
}
|
||||
|
||||
|
||||
=> LazyInitializer.EnsureInitialized(ref _hasAppUrl, ref _isInit, ref _initLocker, () =>
|
||||
{
|
||||
GetApplicationUrl();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,6 @@ namespace Umbraco.Web.Common.DependencyInjection
|
||||
builder.Services.AddUnique<IHttpContextAccessor, HttpContextAccessor>();
|
||||
builder.Services.AddUnique<IRequestAccessor, AspNetCoreRequestAccessor>();
|
||||
builder.AddNotificationHandler<UmbracoRequestBegin, AspNetCoreRequestAccessor>();
|
||||
builder.AddNotificationHandler<UmbracoRequestEnd, AspNetCoreRequestAccessor>();
|
||||
|
||||
// Password hasher
|
||||
builder.Services.AddUnique<IPasswordHasher, AspNetCorePasswordHasher>();
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Notification raised on each request begin.
|
||||
/// </summary>
|
||||
public class UmbracoRequestBegin : INotification
|
||||
{
|
||||
public UmbracoRequestBegin(HttpContext httpContext)
|
||||
{
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
|
||||
public HttpContext HttpContext { get; }
|
||||
};
|
||||
}
|
||||
@@ -95,7 +95,7 @@ namespace Umbraco.Web.Common.Middleware
|
||||
|
||||
try
|
||||
{
|
||||
await _eventAggregator.PublishAsync(new UmbracoRequestBegin(context));
|
||||
await _eventAggregator.PublishAsync(new UmbracoRequestBegin(umbracoContextReference.UmbracoContext));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -111,7 +111,7 @@ namespace Umbraco.Web.Common.Middleware
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _eventAggregator.PublishAsync(new UmbracoRequestEnd(context));
|
||||
await _eventAggregator.PublishAsync(new UmbracoRequestEnd(umbracoContextReference.UmbracoContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace Umbraco.Web.Common.Middleware
|
||||
{
|
||||
if (isFrontEndRequest)
|
||||
{
|
||||
LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId, _requestCache);
|
||||
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
|
||||
_logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.AspNet
|
||||
{
|
||||
public class AspNetRequestAccessor : IRequestAccessor
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly WebRoutingSettings _webRoutingSettings;
|
||||
private readonly ISet<string> _applicationUrls = new HashSet<string>();
|
||||
private Uri _currentApplicationUrl;
|
||||
public AspNetRequestAccessor(IHttpContextAccessor httpContextAccessor, IOptions<WebRoutingSettings> webRoutingSettings)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_webRoutingSettings = webRoutingSettings.Value;
|
||||
|
||||
UmbracoModule.EndRequest += OnEndRequest;
|
||||
UmbracoModule.RouteAttempt += OnRouteAttempt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string GetRequestValue(string name)
|
||||
{
|
||||
return _httpContextAccessor.GetRequiredHttpContext().Request[name];
|
||||
}
|
||||
|
||||
public string GetQueryStringValue(string name)
|
||||
{
|
||||
return _httpContextAccessor.GetRequiredHttpContext().Request.QueryString[name];
|
||||
}
|
||||
|
||||
private void OnEndRequest(object sender, UmbracoRequestEventArgs args)
|
||||
{
|
||||
EndRequest?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
private void OnRouteAttempt(object sender, RoutableAttemptEventArgs args)
|
||||
{
|
||||
RouteAttempt?.Invoke(sender, args);
|
||||
}
|
||||
public event EventHandler<UmbracoRequestEventArgs> EndRequest;
|
||||
public event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
|
||||
public Uri GetRequestUrl() => _httpContextAccessor.HttpContext?.Request.Url;
|
||||
public Uri GetApplicationUrl()
|
||||
{
|
||||
//Fixme: This causes problems with site swap on azure because azure pre-warms a site by calling into `localhost` and when it does that
|
||||
// it changes the URL to `localhost:80` which actually doesn't work for pinging itself, it only works internally in Azure. The ironic part
|
||||
// about this is that this is here specifically for the slot swap scenario https://issues.umbraco.org/issue/U4-10626
|
||||
|
||||
|
||||
// see U4-10626 - in some cases we want to reset the application url
|
||||
// (this is a simplified version of what was in 7.x)
|
||||
// note: should this be optional? is it expensive?
|
||||
|
||||
if (!(_webRoutingSettings.UmbracoApplicationUrl is null))
|
||||
{
|
||||
return new Uri(_webRoutingSettings.UmbracoApplicationUrl);
|
||||
}
|
||||
|
||||
var request = _httpContextAccessor.HttpContext?.Request;
|
||||
|
||||
var url = request?.Url.GetLeftPart(UriPartial.Authority);
|
||||
var change = url != null && !_applicationUrls.Contains(url);
|
||||
if (change)
|
||||
{
|
||||
_applicationUrls.Add(url);
|
||||
|
||||
_currentApplicationUrl ??= new Uri(url);
|
||||
}
|
||||
|
||||
return _currentApplicationUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,6 @@ namespace Umbraco.Web.Runtime
|
||||
{
|
||||
base.Compose(builder);
|
||||
|
||||
builder.Services.AddTransient<UmbracoInjectedModule>();
|
||||
|
||||
|
||||
// register membership stuff
|
||||
builder.Services.AddTransient(factory => MembershipProviderExtensions.GetMembersMembershipProvider());
|
||||
builder.Services.AddTransient(factory => Roles.Enabled ? Roles.Provider : new MembersRoleProvider(factory.GetRequiredService<IMemberService>()));
|
||||
|
||||
@@ -131,7 +131,6 @@
|
||||
</Compile>
|
||||
<Compile Include="AspNet\AspNetHostingEnvironment.cs" />
|
||||
<Compile Include="AspNet\AspNetApplicationShutdownRegistry.cs" />
|
||||
<Compile Include="AspNet\AspNetRequestAccessor.cs" />
|
||||
<Compile Include="AspNet\AspNetSessionManager.cs" />
|
||||
<Compile Include="AspNet\AspNetUserAgentProvider.cs" />
|
||||
<Compile Include="AspNet\FrameworkMarchal.cs" />
|
||||
@@ -177,7 +176,6 @@
|
||||
<Compile Include="HttpContextUmbracoContextAccessor.cs" />
|
||||
<Compile Include="IHttpContextAccessor.cs" />
|
||||
<Compile Include="Composing\ModuleInjector.cs" />
|
||||
<Compile Include="UmbracoModule.cs" />
|
||||
<Compile Include="WebApi\EnableDetailedErrorsAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\FeatureAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\SessionHttpControllerRouteHandler.cs" />
|
||||
@@ -245,7 +243,6 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UmbracoApplication.cs" />
|
||||
<Compile Include="UmbracoInjectedModule.cs" />
|
||||
<Compile Include="UmbracoHttpHandler.cs" />
|
||||
<Compile Include="UmbracoWebService.cs">
|
||||
<SubType>Component</SubType>
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
// notes
|
||||
//
|
||||
// also look at IOHelper.ResolveUrlsFromTextString - nightmarish?!
|
||||
//
|
||||
// context.RewritePath supports ~/ or else must begin with /vdir
|
||||
// Request.RawUrl is still there
|
||||
// response.Redirect does?! always remap to /vdir?!
|
||||
|
||||
/// <summary>
|
||||
/// Represents the main Umbraco module.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Is registered by the <see cref="Umbraco.Web.Runtime.WebRuntime"/>.</para>
|
||||
/// <para>Do *not* try to use that one as a module in web.config.</para>
|
||||
/// </remarks>
|
||||
public class UmbracoInjectedModule : IHttpModule
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
public UmbracoInjectedModule(
|
||||
IRuntimeState runtime,
|
||||
ILogger logger,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
GlobalSettings globalSettings,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_logger = logger;
|
||||
_umbracoContextFactory = umbracoContextFactory;
|
||||
_globalSettings = globalSettings;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins to process a request.
|
||||
/// </summary>
|
||||
private void BeginRequest(HttpContextBase httpContext)
|
||||
{
|
||||
// write the trace output for diagnostics at the end of the request
|
||||
httpContext.Trace.Write("UmbracoModule", "Umbraco request begins");
|
||||
|
||||
// ok, process
|
||||
|
||||
// TODO: should we move this to after we've ensured we are processing a routable page?
|
||||
// ensure there's an UmbracoContext registered for the current request
|
||||
// registers the context reference so its disposed at end of request
|
||||
var umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
|
||||
httpContext.DisposeOnPipelineCompleted(umbracoContextReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the Umbraco Request
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default )
|
||||
/// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because
|
||||
/// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up.
|
||||
/// So we handle it here and explicitly execute the MVC controller.
|
||||
/// </remarks>
|
||||
void ProcessRequest(HttpContextBase httpContext)
|
||||
{
|
||||
|
||||
var umbracoContext = Current.UmbracoContext;
|
||||
|
||||
// do not process if this request is not a front-end routable page
|
||||
var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext);
|
||||
|
||||
// raise event here
|
||||
UmbracoModule.OnRouteAttempt(this, new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext));
|
||||
if (isRoutableAttempt.Success == false) return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
|
||||
/// </summary>
|
||||
internal Attempt<EnsureRoutableOutcome> EnsureUmbracoRoutablePage(IUmbracoContext context, HttpContextBase httpContext)
|
||||
{
|
||||
var uri = context.OriginalRequestUrl;
|
||||
|
||||
var reason = EnsureRoutableOutcome.IsRoutable;
|
||||
|
||||
//// ensure this is a document request
|
||||
//if (!_routableDocumentLookup.IsDocumentRequest(httpContext, context.OriginalRequestUrl))
|
||||
//{
|
||||
// reason = EnsureRoutableOutcome.NotDocumentRequest;
|
||||
//}
|
||||
|
||||
// ensure the runtime is in the proper state
|
||||
// and deal with needed redirects, etc
|
||||
if (!EnsureRuntime(httpContext, uri))
|
||||
{
|
||||
reason = EnsureRoutableOutcome.NotReady;
|
||||
}
|
||||
// ensure Umbraco has documents to serve
|
||||
else if (!EnsureHasContent(context, httpContext))
|
||||
{
|
||||
reason = EnsureRoutableOutcome.NoContent;
|
||||
}
|
||||
|
||||
return Attempt.If(reason == EnsureRoutableOutcome.IsRoutable, reason);
|
||||
}
|
||||
|
||||
// TODO: Where should this execute in netcore? This will have to be a middleware
|
||||
// executing before UseRouting so that it is done before any endpoint routing takes place.
|
||||
private bool EnsureRuntime(HttpContextBase httpContext, Uri uri)
|
||||
{
|
||||
var level = _runtime.Level;
|
||||
switch (level)
|
||||
{
|
||||
// we should never handle Unknown nor Boot: the runtime boots in Application_Start
|
||||
// and as long as it has not booted, no request other than the initial request is
|
||||
// going to be served (see https://stackoverflow.com/a/21402100)
|
||||
// we should never handle BootFailed: if boot failed, the pipeline should not run
|
||||
// at all
|
||||
case RuntimeLevel.Unknown:
|
||||
case RuntimeLevel.Boot:
|
||||
case RuntimeLevel.BootFailed:
|
||||
throw new PanicException($"Unexpected runtime level: {level}.");
|
||||
|
||||
case RuntimeLevel.Run:
|
||||
// ok
|
||||
return true;
|
||||
|
||||
case RuntimeLevel.Install:
|
||||
case RuntimeLevel.Upgrade:
|
||||
|
||||
// NOTE: We have moved the logic that was here to netcore already
|
||||
|
||||
return false; // cannot serve content
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Unexpected runtime level: {level}.");
|
||||
}
|
||||
}
|
||||
|
||||
// ensures Umbraco has at least one published node
|
||||
// if not, rewrites to splash and return false
|
||||
// if yes, return true
|
||||
private bool EnsureHasContent(IUmbracoContext context, HttpContextBase httpContext)
|
||||
{
|
||||
if (context.Content.HasContent())
|
||||
return true;
|
||||
|
||||
_logger.LogWarning("Umbraco has no content");
|
||||
|
||||
if (RouteTable.Routes[Constants.Web.NoContentRouteName] is Route route)
|
||||
{
|
||||
httpContext.RewritePath(route.Url);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrites to the default back office page.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
private void RewriteToBackOfficeHandler(HttpContextBase context)
|
||||
{
|
||||
// GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any)
|
||||
var rewritePath = _globalSettings.GetBackOfficePath(_hostingEnvironment).TrimEnd('/') + "/Default";
|
||||
// rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc)
|
||||
context.RewritePath(rewritePath, "", "", false);
|
||||
|
||||
//if it is MVC we need to do something special, we are not using TransferRequest as this will
|
||||
//require us to rewrite the path with query strings and then re-parse the query strings, this would
|
||||
//also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create
|
||||
//an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does:
|
||||
// * Looks up the route based on the new rewritten URL
|
||||
// * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route
|
||||
//we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal
|
||||
//so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink
|
||||
//a bunch of things!
|
||||
var urlRouting = new UrlRoutingModule();
|
||||
urlRouting.PostResolveRequestCache(context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IHttpModule
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the module, this will trigger for each new application
|
||||
/// and there may be more than 1 application per application domain
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public void Init(HttpApplication app)
|
||||
{
|
||||
app.BeginRequest += (sender, e) =>
|
||||
{
|
||||
var httpContext = ((HttpApplication) sender).Context;
|
||||
|
||||
BeginRequest(new HttpContextWrapper(httpContext));
|
||||
};
|
||||
|
||||
app.PostAuthenticateRequest += (sender, e) =>
|
||||
{
|
||||
var httpContext = ((HttpApplication) sender).Context;
|
||||
//ensure the thread culture is set
|
||||
httpContext.User?.Identity?.EnsureCulture();
|
||||
};
|
||||
|
||||
app.PostResolveRequestCache += (sender, e) =>
|
||||
{
|
||||
var httpContext = ((HttpApplication) sender).Context;
|
||||
ProcessRequest(new HttpContextWrapper(httpContext));
|
||||
};
|
||||
|
||||
app.EndRequest += (sender, args) =>
|
||||
{
|
||||
var httpContext = ((HttpApplication) sender).Context;
|
||||
|
||||
UmbracoModule.OnEndRequest(this, new UmbracoRequestEventArgs(Current.UmbracoContext));
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the main Umbraco module.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Register that one in web.config.</para>
|
||||
/// <para>It will inject <see cref="UmbracoInjectedModule"/> which contains most of the actual code.</para>
|
||||
/// </remarks>
|
||||
public class UmbracoModule : ModuleInjector<UmbracoInjectedModule>
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when...
|
||||
/// </summary>
|
||||
internal static event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when...
|
||||
/// </summary>
|
||||
public static event EventHandler<UmbracoRequestEventArgs> EndRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the RouteAttempt event.
|
||||
/// </summary>
|
||||
internal static void OnRouteAttempt(object sender, RoutableAttemptEventArgs args)
|
||||
{
|
||||
RouteAttempt?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the EndRequest event.
|
||||
/// </summary>
|
||||
internal static void OnEndRequest(object sender, UmbracoRequestEventArgs args)
|
||||
{
|
||||
EndRequest?.Invoke(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user