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:
Shannon
2021-02-02 18:42:39 +11:00
parent 9e43ff8662
commit 86fd473018
22 changed files with 76 additions and 663 deletions

View File

@@ -1,5 +1,9 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Core.Events
{
public class UmbracoApplicationStarting : INotification
{
/// <summary>

View 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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -38,6 +38,7 @@ namespace Umbraco.Infrastructure.DependencyInjection
builder.SetDatabaseServerMessengerCallbacks(GetCallbacks);
builder.SetServerMessenger<BatchedDatabaseServerMessenger>();
builder.AddNotificationHandler<UmbracoApplicationStarting, DatabaseServerMessengerNotificationHandler>();
builder.AddNotificationHandler<UmbracoRequestEnd, DatabaseServerMessengerNotificationHandler>();
return builder;
}

View File

@@ -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();
}

View File

@@ -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);
//}
}
}

View File

@@ -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>();

View File

@@ -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" />

View File

@@ -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;
});
}
}

View File

@@ -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>();

View File

@@ -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; }
};
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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>()));

View File

@@ -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>

View File

@@ -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
}
}

View File

@@ -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);
}
}
}