v10: Fix build warnings in Web.Common (#12349)

* Run code cleanup

* Run dotnet format

* Start manual cleanup in Web.Common

* Finish up manual cleanup

* Fix tests

* Fix up InMemoryModelFactory.cs

* Inject proper macroRenderer

* Update src/Umbraco.Web.Common/Filters/JsonDateTimeFormatAttribute.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* Update src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* Fix based on review

Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
Nikolaj Geisle
2022-05-09 09:39:46 +02:00
committed by GitHub
parent 02cd139770
commit c576bbea03
199 changed files with 12812 additions and 12443 deletions

View File

@@ -1,66 +1,78 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Exceptions;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Cms.Web.Common.Middleware
namespace Umbraco.Cms.Web.Common.Middleware;
/// <summary>
/// Executes when Umbraco booting fails in order to show the problem
/// </summary>
public class BootFailedMiddleware : IMiddleware
{
/// <summary>
/// Executes when Umbraco booting fails in order to show the problem
/// </summary>
public class BootFailedMiddleware : IMiddleware
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly IRuntimeState _runtimeState;
public BootFailedMiddleware(IRuntimeState runtimeState, IHostingEnvironment hostingEnvironment)
: this(runtimeState, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService<IWebHostEnvironment>())
{
private readonly IRuntimeState _runtimeState;
private readonly IHostingEnvironment _hostingEnvironment;
_runtimeState = runtimeState;
_hostingEnvironment = hostingEnvironment;
}
public BootFailedMiddleware(IRuntimeState runtimeState, IHostingEnvironment hostingEnvironment)
public BootFailedMiddleware(IRuntimeState runtimeState, IHostingEnvironment hostingEnvironment, IWebHostEnvironment webHostEnvironment)
{
_runtimeState = runtimeState;
_hostingEnvironment = hostingEnvironment;
_webHostEnvironment = webHostEnvironment;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// TODO: It would be possible to redirect to the installer here in debug mode while
// still showing the error. This would be a lot more friendly than just the YSOD.
// We could also then have a different installer view for when package migrations fails
// and to retry each one individually. Perhaps this can happen in the future.
if (_runtimeState.Level == RuntimeLevel.BootFailed)
{
_runtimeState = runtimeState;
_hostingEnvironment = hostingEnvironment;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// TODO: It would be possible to redirect to the installer here in debug mode while
// still showing the error. This would be a lot more friendly than just the YSOD.
// We could also then have a different installer view for when package migrations fails
// and to retry each one individually. Perhaps this can happen in the future.
if (_runtimeState.Level == RuntimeLevel.BootFailed)
// short circuit
if (_hostingEnvironment.IsDebugMode)
{
// short circuit
if (_hostingEnvironment.IsDebugMode)
{
BootFailedException.Rethrow(_runtimeState.BootFailedException);
}
else // Print a nice error page
{
context.Response.Clear();
context.Response.StatusCode = 500;
var file = GetBootErrorFileName();
var viewContent = await File.ReadAllTextAsync(file);
await context.Response.WriteAsync(viewContent, Encoding.UTF8);
}
BootFailedException.Rethrow(_runtimeState.BootFailedException);
}
else
{
await next(context);
// Print a nice error page
context.Response.Clear();
context.Response.StatusCode = 500;
var file = GetBootErrorFileName();
var viewContent = await File.ReadAllTextAsync(file);
await context.Response.WriteAsync(viewContent, Encoding.UTF8);
}
}
private string GetBootErrorFileName()
else
{
var fileName = _hostingEnvironment.MapPathWebRoot("~/config/errors/BootFailed.html");
if (File.Exists(fileName)) return fileName;
return _hostingEnvironment.MapPathWebRoot("~/umbraco/views/errors/BootFailed.html");
await next(context);
}
}
private string GetBootErrorFileName()
{
var fileName = _webHostEnvironment.MapPathWebRoot("~/config/errors/BootFailed.html");
if (File.Exists(fileName))
{
return fileName;
}
return _webHostEnvironment.MapPathWebRoot("~/umbraco/views/errors/BootFailed.html");
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@@ -7,72 +7,73 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Middleware
namespace Umbraco.Cms.Web.Common.Middleware;
/// <summary>
/// Ensures that preview pages (front-end routed) are authenticated with the back office identity appended to the
/// principal alongside any default authentication that takes place
/// </summary>
public class PreviewAuthenticationMiddleware : IMiddleware
{
/// <summary>
/// Ensures that preview pages (front-end routed) are authenticated with the back office identity appended to the principal alongside any default authentication that takes place
/// </summary>
public class PreviewAuthenticationMiddleware : IMiddleware
private readonly ILogger<PreviewAuthenticationMiddleware> _logger;
public PreviewAuthenticationMiddleware(ILogger<PreviewAuthenticationMiddleware> logger) => _logger = logger;
/// <inheritdoc />
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
private readonly ILogger<PreviewAuthenticationMiddleware> _logger;
HttpRequest request = context.Request;
public PreviewAuthenticationMiddleware(ILogger<PreviewAuthenticationMiddleware> logger) => _logger = logger;
/// <inheritdoc/>
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
// do not process if client-side request
if (request.IsClientSideRequest())
{
var request = context.Request;
await next(context);
return;
}
// do not process if client-side request
if (request.IsClientSideRequest())
try
{
var isPreview = request.HasPreviewCookie()
&& !request.IsBackOfficeRequest();
if (isPreview)
{
await next(context);
return;
}
CookieAuthenticationOptions? cookieOptions = context.RequestServices
.GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>()
.Get(Core.Constants.Security.BackOfficeAuthenticationType);
try
{
var isPreview = request.HasPreviewCookie()
&& context.User != null
&& !request.IsBackOfficeRequest();
if (isPreview)
if (cookieOptions == null)
{
var cookieOptions = context.RequestServices.GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>()
.Get(Core.Constants.Security.BackOfficeAuthenticationType);
throw new InvalidOperationException("No cookie options found with name " +
Core.Constants.Security.BackOfficeAuthenticationType);
}
if (cookieOptions == null)
// If we've gotten this far it means a preview cookie has been set and a front-end umbraco document request is executing.
// In this case, authentication will not have occurred for an Umbraco back office User, however we need to perform the authentication
// for the user here so that the preview capability can be authorized otherwise only the non-preview page will be rendered.
if (cookieOptions.Cookie.Name is not null &&
request.Cookies.TryGetValue(cookieOptions.Cookie.Name, out var cookie))
{
AuthenticationTicket? unprotected = cookieOptions.TicketDataFormat.Unprotect(cookie);
ClaimsIdentity? backOfficeIdentity = unprotected?.Principal.GetUmbracoIdentity();
if (backOfficeIdentity != null)
{
throw new InvalidOperationException("No cookie options found with name " + Core.Constants.Security.BackOfficeAuthenticationType);
// Ok, we've got a real ticket, now we can add this ticket's identity to the current
// Principal, this means we'll have 2 identities assigned to the principal which we can
// use to authorize the preview and allow for a back office User.
context.User.AddIdentity(backOfficeIdentity);
}
// If we've gotten this far it means a preview cookie has been set and a front-end umbraco document request is executing.
// In this case, authentication will not have occurred for an Umbraco back office User, however we need to perform the authentication
// for the user here so that the preview capability can be authorized otherwise only the non-preview page will be rendered.
if (cookieOptions.Cookie.Name is not null && request.Cookies.TryGetValue(cookieOptions.Cookie.Name, out var cookie))
{
var unprotected = cookieOptions.TicketDataFormat.Unprotect(cookie);
var backOfficeIdentity = unprotected?.Principal.GetUmbracoIdentity();
if (backOfficeIdentity != null)
{
// Ok, we've got a real ticket, now we can add this ticket's identity to the current
// Principal, this means we'll have 2 identities assigned to the principal which we can
// use to authorize the preview and allow for a back office User.
context.User?.AddIdentity(backOfficeIdentity);
}
}
}
}
catch (Exception ex)
{
// log any errors and continue the request without preview
_logger.LogError($"Unable to perform preview authentication: {ex.Message}");
}
finally
{
await next(context);
}
}
catch (Exception ex)
{
// log any errors and continue the request without preview
_logger.LogError($"Unable to perform preview authentication: {ex.Message}");
}
finally
{
await next(context);
}
}
}

View File

@@ -1,48 +1,45 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Serilog.Context;
using Umbraco.Cms.Core.Logging.Serilog.Enrichers;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Middleware
{
/// <summary>
/// Adds request based serilog enrichers to the LogContext for each request
/// </summary>
public class UmbracoRequestLoggingMiddleware : IMiddleware
{
private readonly HttpSessionIdEnricher _sessionIdEnricher;
private readonly HttpRequestNumberEnricher _requestNumberEnricher;
private readonly HttpRequestIdEnricher _requestIdEnricher;
namespace Umbraco.Cms.Web.Common.Middleware;
public UmbracoRequestLoggingMiddleware(
HttpSessionIdEnricher sessionIdEnricher,
HttpRequestNumberEnricher requestNumberEnricher,
HttpRequestIdEnricher requestIdEnricher)
/// <summary>
/// Adds request based serilog enrichers to the LogContext for each request
/// </summary>
public class UmbracoRequestLoggingMiddleware : IMiddleware
{
private readonly HttpRequestIdEnricher _requestIdEnricher;
private readonly HttpRequestNumberEnricher _requestNumberEnricher;
private readonly HttpSessionIdEnricher _sessionIdEnricher;
public UmbracoRequestLoggingMiddleware(
HttpSessionIdEnricher sessionIdEnricher,
HttpRequestNumberEnricher requestNumberEnricher,
HttpRequestIdEnricher requestIdEnricher)
{
_sessionIdEnricher = sessionIdEnricher;
_requestNumberEnricher = requestNumberEnricher;
_requestIdEnricher = requestIdEnricher;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// do not process if client-side request
if (context.Request.IsClientSideRequest())
{
_sessionIdEnricher = sessionIdEnricher;
_requestNumberEnricher = requestNumberEnricher;
_requestIdEnricher = requestIdEnricher;
await next(context);
return;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
// TODO: Need to decide if we want this stuff still, there's new request logging in serilog:
// https://github.com/serilog/serilog-aspnetcore#request-logging which i think would suffice and replace all of this?
using (LogContext.Push(_sessionIdEnricher))
using (LogContext.Push(_requestNumberEnricher))
using (LogContext.Push(_requestIdEnricher))
{
// do not process if client-side request
if (context.Request.IsClientSideRequest())
{
await next(context);
return;
}
// TODO: Need to decide if we want this stuff still, there's new request logging in serilog:
// https://github.com/serilog/serilog-aspnetcore#request-logging which i think would suffice and replace all of this?
using (LogContext.Push(_sessionIdEnricher))
using (LogContext.Push(_requestNumberEnricher))
using (LogContext.Push(_requestIdEnricher))
{
await next(context);
}
await next(context);
}
}
}

View File

@@ -1,6 +1,3 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.DependencyInjection;
@@ -19,246 +16,249 @@ using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.PublishedCache;
using Umbraco.Cms.Infrastructure.WebAssets;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Cms.Web.Common.Profiler;
using Umbraco.Cms.Web.Common.Routing;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.Middleware
namespace Umbraco.Cms.Web.Common.Middleware;
/// <summary>
/// Manages Umbraco request objects and their lifetime
/// </summary>
/// <remarks>
/// <para>
/// This is responsible for initializing the content cache
/// </para>
/// <para>
/// This is responsible for creating and assigning an <see cref="IUmbracoContext" />
/// </para>
/// </remarks>
public class UmbracoRequestMiddleware : IMiddleware
{
private readonly BackOfficeWebAssets _backOfficeWebAssets;
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly IEventAggregator _eventAggregator;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILogger<UmbracoRequestMiddleware> _logger;
private readonly WebProfiler? _profiler;
private readonly IRequestCache _requestCache;
private readonly IRuntimeState _runtimeState;
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly IOptions<UmbracoRequestOptions> _umbracoRequestOptions;
private readonly UmbracoRequestPaths _umbracoRequestPaths;
private readonly IVariationContextAccessor _variationContextAccessor;
private SmidgeOptions _smidgeOptions;
/// <summary>
/// Manages Umbraco request objects and their lifetime
/// Initializes a new instance of the <see cref="UmbracoRequestMiddleware" /> class.
/// </summary>
/// <remarks>
/// <para>
/// This is responsible for initializing the content cache
/// </para>
/// <para>
/// This is responsible for creating and assigning an <see cref="IUmbracoContext"/>
/// </para>
/// </remarks>
public class UmbracoRequestMiddleware : IMiddleware
// Obsolete, scheduled for removal in V11
[Obsolete("Use constructor that takes an IOptions<UmbracoRequestOptions>")]
public UmbracoRequestMiddleware(
ILogger<UmbracoRequestMiddleware> logger,
IUmbracoContextFactory umbracoContextFactory,
IRequestCache requestCache,
IEventAggregator eventAggregator,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
UmbracoRequestPaths umbracoRequestPaths,
BackOfficeWebAssets backOfficeWebAssets,
IOptionsMonitor<SmidgeOptions> smidgeOptions,
IRuntimeState runtimeState,
IVariationContextAccessor variationContextAccessor,
IDefaultCultureAccessor defaultCultureAccessor)
: this(
logger,
umbracoContextFactory,
requestCache,
eventAggregator,
profiler,
hostingEnvironment,
umbracoRequestPaths,
backOfficeWebAssets,
smidgeOptions,
runtimeState,
variationContextAccessor,
defaultCultureAccessor,
StaticServiceProvider.Instance.GetRequiredService<IOptions<UmbracoRequestOptions>>())
{
private readonly ILogger<UmbracoRequestMiddleware> _logger;
}
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly IRequestCache _requestCache;
private readonly IEventAggregator _eventAggregator;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly UmbracoRequestPaths _umbracoRequestPaths;
private readonly BackOfficeWebAssets _backOfficeWebAssets;
private readonly IRuntimeState _runtimeState;
private readonly IVariationContextAccessor _variationContextAccessor;
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly IOptions<UmbracoRequestOptions> _umbracoRequestOptions;
private SmidgeOptions _smidgeOptions;
private readonly WebProfiler? _profiler;
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestMiddleware" /> class.
/// </summary>
public UmbracoRequestMiddleware(
ILogger<UmbracoRequestMiddleware> logger,
IUmbracoContextFactory umbracoContextFactory,
IRequestCache requestCache,
IEventAggregator eventAggregator,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
UmbracoRequestPaths umbracoRequestPaths,
BackOfficeWebAssets backOfficeWebAssets,
IOptionsMonitor<SmidgeOptions> smidgeOptions,
IRuntimeState runtimeState,
IVariationContextAccessor variationContextAccessor,
IDefaultCultureAccessor defaultCultureAccessor,
IOptions<UmbracoRequestOptions> umbracoRequestOptions)
{
_logger = logger;
_umbracoContextFactory = umbracoContextFactory;
_requestCache = requestCache;
_eventAggregator = eventAggregator;
_hostingEnvironment = hostingEnvironment;
_umbracoRequestPaths = umbracoRequestPaths;
_backOfficeWebAssets = backOfficeWebAssets;
_runtimeState = runtimeState;
_variationContextAccessor = variationContextAccessor;
_defaultCultureAccessor = defaultCultureAccessor;
_umbracoRequestOptions = umbracoRequestOptions;
_smidgeOptions = smidgeOptions.CurrentValue;
_profiler = profiler as WebProfiler; // Ignore if not a WebProfiler
#pragma warning disable IDE0044 // Add readonly modifier
private static bool s_firstBackOfficeRequest;
private static bool s_firstBackOfficeReqestFlag;
private static object s_firstBackOfficeRequestLocker = new object();
#pragma warning restore IDE0044 // Add readonly modifier
smidgeOptions.OnChange(x => _smidgeOptions = x);
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestMiddleware"/> class.
/// </summary>
// Obsolete, scheduled for removal in V11
[Obsolete("Use constructor that takes an IOptions<UmbracoRequestOptions>")]
public UmbracoRequestMiddleware(
ILogger<UmbracoRequestMiddleware> logger,
IUmbracoContextFactory umbracoContextFactory,
IRequestCache requestCache,
IEventAggregator eventAggregator,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
UmbracoRequestPaths umbracoRequestPaths,
BackOfficeWebAssets backOfficeWebAssets,
IOptionsMonitor<SmidgeOptions> smidgeOptions,
IRuntimeState runtimeState,
IVariationContextAccessor variationContextAccessor,
IDefaultCultureAccessor defaultCultureAccessor)
: this(
logger,
umbracoContextFactory,
requestCache,
eventAggregator,
profiler,
hostingEnvironment,
umbracoRequestPaths,
backOfficeWebAssets,
smidgeOptions,
runtimeState,
variationContextAccessor,
defaultCultureAccessor,
StaticServiceProvider.Instance.GetRequiredService<IOptions<UmbracoRequestOptions>>())
/// <inheritdoc />
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// do not process if client-side request
if (context.Request.IsClientSideRequest() &&
!_umbracoRequestOptions.Value.HandleAsServerSideRequest(context.Request))
{
// we need this here because for bundle requests, these are 'client side' requests that we need to handle
LazyInitializeBackOfficeServices(context.Request.Path);
await next(context);
return;
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestMiddleware"/> class.
/// </summary>
public UmbracoRequestMiddleware(
ILogger<UmbracoRequestMiddleware> logger,
IUmbracoContextFactory umbracoContextFactory,
IRequestCache requestCache,
IEventAggregator eventAggregator,
IProfiler profiler,
IHostingEnvironment hostingEnvironment,
UmbracoRequestPaths umbracoRequestPaths,
BackOfficeWebAssets backOfficeWebAssets,
IOptionsMonitor<SmidgeOptions> smidgeOptions,
IRuntimeState runtimeState,
IVariationContextAccessor variationContextAccessor,
IDefaultCultureAccessor defaultCultureAccessor,
IOptions<UmbracoRequestOptions> umbracoRequestOptions)
// Profiling start needs to be one of the first things that happens.
// Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await
_profiler?.UmbracoApplicationBeginRequest(context, _runtimeState.Level);
_variationContextAccessor.VariationContext ??= new VariationContext(_defaultCultureAccessor.DefaultCulture);
UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
Uri? currentApplicationUrl = GetApplicationUrlFromCurrentRequest(context.Request);
_hostingEnvironment.EnsureApplicationMainUrl(currentApplicationUrl);
var pathAndQuery = context.Request.GetEncodedPathAndQuery();
try
{
_logger = logger;
_umbracoContextFactory = umbracoContextFactory;
_requestCache = requestCache;
_eventAggregator = eventAggregator;
_hostingEnvironment = hostingEnvironment;
_umbracoRequestPaths = umbracoRequestPaths;
_backOfficeWebAssets = backOfficeWebAssets;
_runtimeState = runtimeState;
_variationContextAccessor = variationContextAccessor;
_defaultCultureAccessor = defaultCultureAccessor;
_umbracoRequestOptions = umbracoRequestOptions;
_smidgeOptions = smidgeOptions.CurrentValue;
_profiler = profiler as WebProfiler; // Ignore if not a WebProfiler
smidgeOptions.OnChange(x => _smidgeOptions = x);
}
/// <inheritdoc/>
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// do not process if client-side request
if (context.Request.IsClientSideRequest() && !_umbracoRequestOptions.Value.HandleAsServerSideRequest(context.Request))
{
// we need this here because for bundle requests, these are 'client side' requests that we need to handle
LazyInitializeBackOfficeServices(context.Request.Path);
await next(context);
return;
}
// Profiling start needs to be one of the first things that happens.
// Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await
_profiler?.UmbracoApplicationBeginRequest(context, _runtimeState.Level);
_variationContextAccessor.VariationContext ??= new VariationContext(_defaultCultureAccessor.DefaultCulture);
UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
Uri? currentApplicationUrl = GetApplicationUrlFromCurrentRequest(context.Request);
_hostingEnvironment.EnsureApplicationMainUrl(currentApplicationUrl);
var pathAndQuery = context.Request.GetEncodedPathAndQuery();
// Verbose log start of every request
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache);
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery);
try
{
// Verbose log start of every request
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid? httpRequestId, _requestCache);
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery);
try
{
LazyInitializeBackOfficeServices(context.Request.Path);
await _eventAggregator.PublishAsync(new UmbracoRequestBeginNotification(umbracoContextReference.UmbracoContext));
}
catch (Exception ex)
{
// try catch so we don't kill everything in all requests
_logger.LogError(ex.Message);
}
finally
{
try
{
await next(context);
}
finally
{
await _eventAggregator.PublishAsync(new UmbracoRequestEndNotification(umbracoContextReference.UmbracoContext));
}
}
LazyInitializeBackOfficeServices(context.Request.Path);
await _eventAggregator.PublishAsync(
new UmbracoRequestBeginNotification(umbracoContextReference.UmbracoContext));
}
catch (Exception ex)
{
// try catch so we don't kill everything in all requests
_logger.LogError(ex.Message);
}
finally
{
// 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
{
DisposeHttpContextItems(context.Request);
await next(context);
}
finally
{
// Dispose the umbraco context reference which will in turn dispose the UmbracoContext itself.
umbracoContextReference.Dispose();
await _eventAggregator.PublishAsync(
new UmbracoRequestEndNotification(umbracoContextReference.UmbracoContext));
}
}
}
finally
{
// 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);
// Profiling end needs to be last of the first things that happens.
// Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await
_profiler?.UmbracoApplicationEndRequest(context, _runtimeState.Level);
try
{
DisposeHttpContextItems(context.Request);
}
finally
{
// Dispose the umbraco context reference which will in turn dispose the UmbracoContext itself.
umbracoContextReference.Dispose();
}
}
/// <summary>
/// Used to lazily initialize any back office services when the first request to the back office is made
/// </summary>
/// <param name="umbracoContext"></param>
/// <returns></returns>
private void LazyInitializeBackOfficeServices(PathString absPath)
{
if (s_firstBackOfficeRequest)
{
return;
}
// Profiling end needs to be last of the first things that happens.
// Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await
_profiler?.UmbracoApplicationEndRequest(context, _runtimeState.Level);
}
if (_umbracoRequestPaths.IsBackOfficeRequest(absPath)
|| (absPath.Value?.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.CompositeFilePath}") ?? false)
|| (absPath.Value?.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.BundleFilePath}") ?? false))
{
LazyInitializer.EnsureInitialized(ref s_firstBackOfficeRequest, ref s_firstBackOfficeReqestFlag, ref s_firstBackOfficeRequestLocker, () =>
/// <summary>
/// Used to lazily initialize any back office services when the first request to the back office is made
/// </summary>
private void LazyInitializeBackOfficeServices(PathString absPath)
{
if (s_firstBackOfficeRequest)
{
return;
}
if (_umbracoRequestPaths.IsBackOfficeRequest(absPath)
|| (absPath.Value?.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.CompositeFilePath}") ?? false)
|| (absPath.Value?.InvariantStartsWith($"/{_smidgeOptions.UrlOptions.BundleFilePath}") ?? false))
{
LazyInitializer.EnsureInitialized(ref s_firstBackOfficeRequest, ref s_firstBackOfficeReqestFlag,
ref s_firstBackOfficeRequestLocker, () =>
{
_backOfficeWebAssets.CreateBundles();
return true;
});
}
}
private Uri? GetApplicationUrlFromCurrentRequest(HttpRequest request)
{
// We only consider GET and POST.
// Especially the DEBUG sent when debugging the application is annoying because it uses http, even when the https is available.
if (request.Method == "GET" || request.Method == "POST")
{
return new Uri($"{request.Scheme}://{request.Host}{request.PathBase}", UriKind.Absolute);
}
return null;
}
/// <summary>
/// Dispose some request scoped objects that we are maintaining the lifecycle for.
/// </summary>
private void DisposeHttpContextItems(HttpRequest request)
{
// do not process if client-side request
if (request.IsClientSideRequest())
{
return;
}
// ensure this is disposed by DI at the end of the request
IHttpScopeReference httpScopeReference = request.HttpContext.RequestServices.GetRequiredService<IHttpScopeReference>();
httpScopeReference.Register();
}
}
private Uri? GetApplicationUrlFromCurrentRequest(HttpRequest request)
{
// We only consider GET and POST.
// Especially the DEBUG sent when debugging the application is annoying because it uses http, even when the https is available.
if (request.Method == "GET" || request.Method == "POST")
{
return new Uri($"{request.Scheme}://{request.Host}{request.PathBase}", UriKind.Absolute);
}
return null;
}
/// <summary>
/// Dispose some request scoped objects that we are maintaining the lifecycle for.
/// </summary>
private void DisposeHttpContextItems(HttpRequest request)
{
// do not process if client-side request
if (request.IsClientSideRequest())
{
return;
}
// ensure this is disposed by DI at the end of the request
IHttpScopeReference httpScopeReference =
request.HttpContext.RequestServices.GetRequiredService<IHttpScopeReference>();
httpScopeReference.Register();
}
#pragma warning disable IDE0044 // Add readonly modifier
private static bool s_firstBackOfficeRequest;
private static bool s_firstBackOfficeReqestFlag;
private static object s_firstBackOfficeRequestLocker = new();
#pragma warning restore IDE0044 // Add readonly modifier
}