Merge pull request #7992 from umbraco/netcore/feature/AB6241-reimplement-action-filters
Netcore: Reimplementing action filters
This commit is contained in:
@@ -2,9 +2,7 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.WebAssets;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.Common.ActionResults;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Filters
|
||||
@@ -14,10 +12,11 @@ namespace Umbraco.Web.BackOffice.Filters
|
||||
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
// logic before action goes here
|
||||
var hostingEnvironment = context.HttpContext.RequestServices.GetService<IHostingEnvironment>();
|
||||
var serviceProvider = context.HttpContext.RequestServices;
|
||||
var hostingEnvironment = serviceProvider.GetService<IHostingEnvironment>();
|
||||
if (!hostingEnvironment.IsDebugMode)
|
||||
{
|
||||
var runtimeMinifier = context.HttpContext.RequestServices.GetService<IRuntimeMinifier>();
|
||||
var runtimeMinifier = serviceProvider.GetService<IRuntimeMinifier>();
|
||||
|
||||
if (context.Result is JavaScriptResult jsResult)
|
||||
{
|
||||
|
||||
12
src/Umbraco.Web.Common/Constants/ViewConstants.cs
Normal file
12
src/Umbraco.Web.Common/Constants/ViewConstants.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Web.Common.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// constants
|
||||
/// </summary>
|
||||
internal static class ViewConstants
|
||||
{
|
||||
internal const string ViewLocation = "~/Views";
|
||||
|
||||
internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Web.Common/Controllers/RenderController.cs
Normal file
9
src/Umbraco.Web.Common/Controllers/RenderController.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Common.Controllers
|
||||
{
|
||||
public abstract class RenderController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
17
src/Umbraco.Web.Common/Events/ActionExecutedEventArgs.cs
Normal file
17
src/Umbraco.Web.Common/Events/ActionExecutedEventArgs.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Common.Events
|
||||
{
|
||||
public class ActionExecutedEventArgs : EventArgs
|
||||
{
|
||||
public Controller Controller { get; set; }
|
||||
public object Model { get; set; }
|
||||
|
||||
public ActionExecutedEventArgs(Controller controller, object model)
|
||||
{
|
||||
Controller = controller;
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ namespace Umbraco.Web.Common.Extensions
|
||||
IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo,
|
||||
ITypeFinder typeFinder)
|
||||
{
|
||||
var connectionStringConfig = configs.ConnectionStrings()[Constants.System.UmbracoConnectionName];
|
||||
var connectionStringConfig = configs.ConnectionStrings()[Core.Constants.System.UmbracoConnectionName];
|
||||
var dbProviderFactoryCreator = new SqlServerDbProviderFactoryCreator(
|
||||
connectionStringConfig?.ProviderName,
|
||||
DbProviderFactories.GetFactory);
|
||||
@@ -185,7 +185,7 @@ namespace Umbraco.Web.Common.Extensions
|
||||
public static IServiceCollection AddUmbracoRuntimeMinifier(this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
services.AddSmidge(configuration.GetSection(Constants.Configuration.ConfigRuntimeMinification));
|
||||
services.AddSmidge(configuration.GetSection(Core.Constants.Configuration.ConfigRuntimeMinification));
|
||||
services.AddSmidgeNuglify();
|
||||
|
||||
return services;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures that the request is not cached by the browser
|
||||
/// </summary>
|
||||
public class DisableBrowserCacheAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
base.OnResultExecuting(context);
|
||||
|
||||
var httpResponse = context.HttpContext.Response;
|
||||
|
||||
if (httpResponse.StatusCode != 200) return;
|
||||
|
||||
httpResponse.GetTypedHeaders().CacheControl =
|
||||
new CacheControlHeaderValue()
|
||||
{
|
||||
NoCache = true,
|
||||
MaxAge = TimeSpan.Zero,
|
||||
MustRevalidate = true,
|
||||
NoStore = true
|
||||
};
|
||||
|
||||
httpResponse.Headers[HeaderNames.LastModified] = DateTime.Now.ToString("R"); // Format RFC1123
|
||||
httpResponse.Headers[HeaderNames.Pragma] = "no-cache";
|
||||
httpResponse.Headers[HeaderNames.Expires] = new DateTime(1990, 1, 1, 0, 0, 0).ToString("R");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Umbraco.Web.Common.Constants;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a special filter which is required for the RTE to be able to render Partial View Macros that
|
||||
/// contain forms when the RTE value is resolved outside of an MVC view being rendered
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The entire way that we support partial view macros that contain forms isn't really great, these forms
|
||||
/// need to be executed as ChildActions so that the ModelState,ViewData,TempData get merged into that action
|
||||
/// so the form can show errors, viewdata, etc...
|
||||
/// Under normal circumstances, macros will be rendered after a ViewContext is created but in some cases
|
||||
/// developers will resolve the RTE value in the controller, in this case the Form won't be rendered correctly
|
||||
/// with merged ModelState from the controller because the special DataToken hasn't been set yet (which is
|
||||
/// normally done in the UmbracoViewPageOfModel when a real ViewContext is available.
|
||||
/// So we need to detect if the currently rendering controller is IRenderController and if so we'll ensure that
|
||||
/// this DataToken exists before the action executes in case the developer resolves an RTE value that contains
|
||||
/// a partial view macro form.
|
||||
/// </remarks>
|
||||
public class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the custom ViewContext datatoken is set before the RenderController action is invoked,
|
||||
/// this ensures that any calls to GetPropertyValue with regards to RTE or Grid editors can still
|
||||
/// render any PartialViewMacro with a form and maintain ModelState
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (!(context.Controller is Controller controller)) return;
|
||||
|
||||
//ignore anything that is not IRenderController
|
||||
if (!(controller is RenderController)) return;
|
||||
|
||||
SetViewContext(context, controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the custom ViewContext datatoken is set after the RenderController action is invoked,
|
||||
/// this ensures that any custom ModelState that may have been added in the RenderController itself is
|
||||
/// passed onwards in case it is required when rendering a PartialViewMacro with a form
|
||||
/// </summary>
|
||||
/// <param name="context">The filter context.</param>
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if (!(context.Controller is Controller controller)) return;
|
||||
|
||||
//ignore anything that is not IRenderController
|
||||
if (!(controller is RenderController)) return;
|
||||
|
||||
SetViewContext(context, controller);
|
||||
}
|
||||
|
||||
private void SetViewContext(ActionContext context, Controller controller)
|
||||
{
|
||||
var viewCtx = new ViewContext(
|
||||
context,
|
||||
new DummyView(),
|
||||
controller.ViewData,
|
||||
controller.TempData,
|
||||
new StringWriter(),
|
||||
new HtmlHelperOptions());
|
||||
|
||||
//set the special data token
|
||||
context.RouteData.DataTokens[ViewConstants.DataTokenCurrentViewContext] = viewCtx;
|
||||
}
|
||||
|
||||
private class DummyView : IView
|
||||
{
|
||||
public Task RenderAsync(ViewContext context)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Umbraco.Web.Common.Events;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
{
|
||||
public class PreRenderViewActionFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
if (!(context.Controller is Controller umbController) || !(context.Result is ViewResult result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var model = result.Model;
|
||||
if (model == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var args = new ActionExecutedEventArgs(umbController, model);
|
||||
OnActionExecuted(args);
|
||||
|
||||
if (args.Model != model)
|
||||
{
|
||||
result.ViewData.Model = args.Model;
|
||||
}
|
||||
|
||||
base.OnActionExecuted(context);
|
||||
}
|
||||
|
||||
|
||||
public static event EventHandler<ActionExecutedEventArgs> ActionExecuted;
|
||||
|
||||
private static void OnActionExecuted(ActionExecutedEventArgs e)
|
||||
{
|
||||
var handler = ActionExecuted;
|
||||
handler?.Invoke(null, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/Umbraco.Web.Common/Filters/StatusCodeResultAttribute.cs
Normal file
39
src/Umbraco.Web.Common/Filters/StatusCodeResultAttribute.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Forces the response to have a specific http status code
|
||||
/// </summary>
|
||||
public class StatusCodeResultAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly HttpStatusCode _statusCode;
|
||||
|
||||
public StatusCodeResultAttribute(HttpStatusCode statusCode)
|
||||
{
|
||||
_statusCode = statusCode;
|
||||
}
|
||||
|
||||
public override void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
base.OnActionExecuted(context);
|
||||
|
||||
var httpContext = context.HttpContext;
|
||||
|
||||
httpContext.Response.StatusCode = (int)_statusCode;
|
||||
|
||||
var disableIisCustomErrors = httpContext.RequestServices.GetService<IWebRoutingSettings>().TrySkipIisCustomErrors;
|
||||
var statusCodePagesFeature = httpContext.Features.Get<IStatusCodePagesFeature>();
|
||||
|
||||
if (statusCodePagesFeature != null)
|
||||
{
|
||||
// if IIS Custom Errors are disabled, we won't enable the Status Code Pages
|
||||
statusCodePagesFeature.Enabled = !disableIisCustomErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ namespace Umbraco.Web.Common.RuntimeMinification
|
||||
public void Reset()
|
||||
{
|
||||
var version = DateTime.UtcNow.Ticks.ToString();
|
||||
_configManipulator.SaveConfigValue(Constants.Configuration.ConfigRuntimeMinificationVersion, version.ToString());
|
||||
_configManipulator.SaveConfigValue(Core.Constants.Configuration.ConfigRuntimeMinificationVersion, version.ToString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
app.UseStatusCodePages();
|
||||
app.UseUmbracoCore();
|
||||
app.UseUmbracoWebsite();
|
||||
app.UseUmbracoBackOffice();
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// Migrated already to .Net Core
|
||||
public class ActionExecutedEventArgs : EventArgs
|
||||
{
|
||||
public Controller Controller { get; set; }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/// <summary>
|
||||
/// constants
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string ViewLocation = "~/Views";
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// this DataToken exists before the action executes in case the developer resolves an RTE value that contains
|
||||
/// a partial view macro form.
|
||||
/// </remarks>
|
||||
/// Migrated already to .Net Core
|
||||
internal class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <summary>
|
||||
/// A marker interface to designate that a controller will be used for Umbraco front-end requests and/or route hijacking
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core
|
||||
public interface IRenderController : IController
|
||||
{
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <remarks>
|
||||
/// Only minifies in release mode
|
||||
/// </remarks>
|
||||
/// Migrated already to .Net Core
|
||||
public class MinifyJavaScriptResultAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// Migrated already to .Net Core
|
||||
public class PreRenderViewActionFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuted(ActionExecutedContext filterContext)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <summary>
|
||||
/// Forces the response to have a specific http status code
|
||||
/// </summary>
|
||||
/// Migrated already to .Net Core
|
||||
internal class StatusCodeResultAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly HttpStatusCode _statusCode;
|
||||
|
||||
Reference in New Issue
Block a user