diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs index d7f6e63c55..8f9c5102ab 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs @@ -156,7 +156,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected abstract void PersistUpdatedItem(TEntity item); - protected abstract Sql GetBaseQuery(bool isCount); // TODO: obsolete, use QueryType instead everywhere + // TODO: obsolete, use QueryType instead everywhere like GetBaseQuery(QueryType queryType); + protected abstract Sql GetBaseQuery(bool isCount); protected abstract string GetBaseWhereClause(); diff --git a/src/Umbraco.Web.Common/Controllers/RenderController.cs b/src/Umbraco.Web.Common/Controllers/RenderController.cs index ec73c061e2..099dfd59cd 100644 --- a/src/Umbraco.Web.Common/Controllers/RenderController.cs +++ b/src/Umbraco.Web.Common/Controllers/RenderController.cs @@ -15,12 +15,12 @@ namespace Umbraco.Web.Common.Controllers /// /// Represents the default front-end rendering controller. /// - [TypeFilter(typeof(ModelBindingExceptionFilter))] + [ModelBindingException] public class RenderController : UmbracoController, IRenderController { private readonly ILogger _logger; private readonly ICompositeViewEngine _compositeViewEngine; - private UmbracoRouteValues _routeValues; + private UmbracoRouteValues _umbracoRouteValues; /// /// Initializes a new instance of the class. @@ -43,9 +43,9 @@ namespace Umbraco.Web.Common.Controllers { get { - if (_routeValues != null) + if (_umbracoRouteValues != null) { - return _routeValues; + return _umbracoRouteValues; } if (!ControllerContext.RouteData.Values.TryGetValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken, out var def)) @@ -53,8 +53,8 @@ namespace Umbraco.Web.Common.Controllers throw new InvalidOperationException($"No route value found with key {Core.Constants.Web.UmbracoRouteDefinitionDataToken}"); } - _routeValues = (UmbracoRouteValues)def; - return _routeValues; + _umbracoRouteValues = (UmbracoRouteValues)def; + return _umbracoRouteValues; } } diff --git a/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs b/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs new file mode 100644 index 0000000000..6e9d6d29a7 --- /dev/null +++ b/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs @@ -0,0 +1,88 @@ +using System; +using System.Net; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Umbraco.Core; +using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Common.ModelBinders; + +namespace Umbraco.Web.Common.Filters +{ + /// + /// An exception filter checking if we get a or with the same model. + /// In which case it returns a redirect to the same page after 1 sec if not in debug mode. + /// + /// + /// This is only enabled when running PureLive + /// + public sealed class ModelBindingExceptionAttribute : TypeFilterAttribute + { + /// + /// Initializes a new instance of the class. + /// + public ModelBindingExceptionAttribute() + : base(typeof(ModelBindingExceptionFilter)) + { + } + + private class ModelBindingExceptionFilter : IExceptionFilter + { + private static readonly Regex s_getPublishedModelsTypesRegex = new Regex("Umbraco.Web.PublishedModels.(\\w+)", RegexOptions.Compiled); + + private readonly ExceptionFilterSettings _exceptionFilterSettings; + private readonly IPublishedModelFactory _publishedModelFactory; + + public ModelBindingExceptionFilter(IOptions exceptionFilterSettings, IPublishedModelFactory publishedModelFactory) + { + _exceptionFilterSettings = exceptionFilterSettings.Value; + _publishedModelFactory = publishedModelFactory ?? throw new ArgumentNullException(nameof(publishedModelFactory)); + } + + public void OnException(ExceptionContext filterContext) + { + var disabled = _exceptionFilterSettings?.Disabled ?? false; + if (_publishedModelFactory.IsLiveFactory() + && !disabled + && !filterContext.ExceptionHandled + && (filterContext.Exception is ModelBindingException || filterContext.Exception is InvalidCastException) + && IsMessageAboutTheSameModelType(filterContext.Exception.Message)) + { + filterContext.HttpContext.Response.Headers.Add(HttpResponseHeader.RetryAfter.ToString(), "1"); + filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.GetEncodedUrl(), false); + + filterContext.ExceptionHandled = true; + } + } + + /// + /// Returns true if the message is about two models with the same name. + /// + /// + /// Message could be something like: + /// + /// InvalidCastException: + /// [A]Umbraco.Web.PublishedModels.Home cannot be cast to [B]Umbraco.Web.PublishedModels.Home. Type A originates from 'App_Web_all.generated.cs.8f9494c4.rtdigm_z, Version=0.0.0.3, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Users\User\AppData\Local\Temp\Temporary ASP.NET Files\root\c5c63f4d\c168d9d4\App_Web_all.generated.cs.8f9494c4.rtdigm_z.dll'. Type B originates from 'App_Web_all.generated.cs.8f9494c4.rbyqlplu, Version=0.0.0.5, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Users\User\AppData\Local\Temp\Temporary ASP.NET Files\root\c5c63f4d\c168d9d4\App_Web_all.generated.cs.8f9494c4.rbyqlplu.dll'. + /// + /// + /// ModelBindingException: + /// Cannot bind source content type Umbraco.Web.PublishedModels.Home to model type Umbraco.Web.PublishedModels.Home. Both view and content models are PureLive, with different versions. The application is in an unstable state and is going to be restarted. The application is restarting now. + /// + /// + private bool IsMessageAboutTheSameModelType(string exceptionMessage) + { + MatchCollection matches = s_getPublishedModelsTypesRegex.Matches(exceptionMessage); + + if (matches.Count >= 2) + { + return string.Equals(matches[0].Value, matches[1].Value, StringComparison.InvariantCulture); + } + + return false; + } + } + } +} diff --git a/src/Umbraco.Web.Common/Filters/ModelBindingExceptionFilter.cs b/src/Umbraco.Web.Common/Filters/ModelBindingExceptionFilter.cs deleted file mode 100644 index 6cec04e0b6..0000000000 --- a/src/Umbraco.Web.Common/Filters/ModelBindingExceptionFilter.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Net; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Options; -using Umbraco.Core; -using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Common.ModelBinders; - -namespace Umbraco.Web.Common.Filters -{ - /// - /// An exception filter checking if we get a or with the same model. - /// In which case it returns a redirect to the same page after 1 sec if not in debug mode. - /// - /// - /// This is only enabled when running PureLive - /// - public class ModelBindingExceptionFilter : ActionFilterAttribute, IExceptionFilter - { - private static readonly Regex _getPublishedModelsTypesRegex = new Regex("Umbraco.Web.PublishedModels.(\\w+)", RegexOptions.Compiled); - - private readonly ExceptionFilterSettings _exceptionFilterSettings; - private readonly IPublishedModelFactory _publishedModelFactory; - - public ModelBindingExceptionFilter(IOptions exceptionFilterSettings, IPublishedModelFactory publishedModelFactory) - { - _exceptionFilterSettings = exceptionFilterSettings.Value; - _publishedModelFactory = publishedModelFactory ?? throw new ArgumentNullException(nameof(publishedModelFactory)); - } - - public void OnException(ExceptionContext filterContext) - { - var disabled = _exceptionFilterSettings?.Disabled ?? false; - if (_publishedModelFactory.IsLiveFactory() - && !disabled - && !filterContext.ExceptionHandled - && ((filterContext.Exception is ModelBindingException || filterContext.Exception is InvalidCastException) - && IsMessageAboutTheSameModelType(filterContext.Exception.Message))) - { - filterContext.HttpContext.Response.Headers.Add(HttpResponseHeader.RetryAfter.ToString(), "1"); - filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.GetEncodedUrl(), false); - - filterContext.ExceptionHandled = true; - } - } - - /// - /// Returns true if the message is about two models with the same name. - /// - /// - /// Message could be something like: - /// - /// InvalidCastException: - /// [A]Umbraco.Web.PublishedModels.Home cannot be cast to [B]Umbraco.Web.PublishedModels.Home. Type A originates from 'App_Web_all.generated.cs.8f9494c4.rtdigm_z, Version=0.0.0.3, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Users\User\AppData\Local\Temp\Temporary ASP.NET Files\root\c5c63f4d\c168d9d4\App_Web_all.generated.cs.8f9494c4.rtdigm_z.dll'. Type B originates from 'App_Web_all.generated.cs.8f9494c4.rbyqlplu, Version=0.0.0.5, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Users\User\AppData\Local\Temp\Temporary ASP.NET Files\root\c5c63f4d\c168d9d4\App_Web_all.generated.cs.8f9494c4.rbyqlplu.dll'. - /// - /// - /// ModelBindingException: - /// Cannot bind source content type Umbraco.Web.PublishedModels.Home to model type Umbraco.Web.PublishedModels.Home. Both view and content models are PureLive, with different versions. The application is in an unstable state and is going to be restarted. The application is restarting now. - /// - /// - private bool IsMessageAboutTheSameModelType(string exceptionMessage) - { - var matches = _getPublishedModelsTypesRegex.Matches(exceptionMessage); - - if (matches.Count >= 2) - { - return string.Equals(matches[0].Value, matches[1].Value, StringComparison.InvariantCulture); - } - - return false; - } - } -}