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,56 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Web.Common.Attributes;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
// TODO: This should just exist in the back office project
/// <summary>
/// An application model provider for all Umbraco Back Office controllers
/// </summary>
public class BackOfficeApplicationModelProvider : IApplicationModelProvider
{
// TODO: This should just exist in the back office project
/// <summary>
/// An application model provider for all Umbraco Back Office controllers
/// </summary>
public class BackOfficeApplicationModelProvider : IApplicationModelProvider
private readonly List<IActionModelConvention> _actionModelConventions = new()
{
private readonly List<IActionModelConvention> _actionModelConventions = new List<IActionModelConvention>()
{
new BackOfficeIdentityCultureConvention()
};
new BackOfficeIdentityCultureConvention(),
};
/// <inheritdoc />
/// <summary>
/// Will execute after <see cref="DefaultApplicationModelProvider"/>
/// </summary>
public int Order => 0;
/// <inheritdoc />
/// <summary>
/// Will execute after <see cref="DefaultApplicationModelProvider" />
/// </summary>
public int Order => 0;
/// <inheritdoc/>
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
/// <inheritdoc />
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
/// <inheritdoc/>
public void OnProvidersExecuting(ApplicationModelProviderContext context)
/// <inheritdoc />
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
{
foreach (ControllerModel controller in context.Result.Controllers)
if (!IsBackOfficeController(controller))
{
if (!IsBackOfficeController(controller))
{
continue;
}
continue;
}
foreach (ActionModel action in controller.Actions)
foreach (ActionModel action in controller.Actions)
{
foreach (IActionModelConvention convention in _actionModelConventions)
{
foreach (IActionModelConvention convention in _actionModelConventions)
{
convention.Apply(action);
}
convention.Apply(action);
}
}
}
private bool IsBackOfficeController(ControllerModel controller)
=> controller.Attributes.OfType<IsBackOfficeAttribute>().Any();
}
private bool IsBackOfficeController(ControllerModel controller)
=> controller.Attributes.OfType<IsBackOfficeAttribute>().Any();
}

View File

@@ -1,14 +1,11 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Umbraco.Cms.Web.Common.Filters;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
// TODO: This should just exist in the back office project
public class BackOfficeIdentityCultureConvention : IActionModelConvention
{
// TODO: This should just exist in the back office project
public class BackOfficeIdentityCultureConvention : IActionModelConvention
{
/// <inheritdoc/>
public void Apply(ActionModel action) => action.Filters.Add(new BackOfficeCultureFilter());
}
/// <inheritdoc />
public void Apply(ActionModel action) => action.Filters.Add(new BackOfficeCultureFilter());
}

View File

@@ -1,90 +1,86 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Web.Common.Attributes;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
/// <summary>
/// An application model provider for Umbraco API controllers to behave like WebApi controllers
/// </summary>
/// <remarks>
/// <para>
/// Conventions will be applied to controllers attributed with <see cref="UmbracoApiControllerAttribute" />
/// </para>
/// <para>
/// This is nearly a copy of aspnetcore's ApiBehaviorApplicationModelProvider which supplies a convention for the
/// [ApiController] attribute, however that convention is too strict for our purposes so we will have our own.
/// Uses UmbracoJsonModelBinder for complex parameters and those with BindingSource of Body, but leaves the rest
/// alone see GH #11554
/// </para>
/// <para>
/// See https://shazwazza.com/post/custom-body-model-binding-per-controller-in-asp-net-core/
/// and https://github.com/dotnet/aspnetcore/issues/21724
/// </para>
/// </remarks>
public class UmbracoApiBehaviorApplicationModelProvider : IApplicationModelProvider
{
private readonly List<IActionModelConvention> _actionModelConventions;
/// <summary>
/// An application model provider for Umbraco API controllers to behave like WebApi controllers
/// Initializes a new instance of the <see cref="UmbracoApiBehaviorApplicationModelProvider" /> class.
/// </summary>
/// <remarks>
/// <para>
/// Conventions will be applied to controllers attributed with <see cref="UmbracoApiControllerAttribute"/>
/// </para>
/// <para>
/// This is nearly a copy of aspnetcore's ApiBehaviorApplicationModelProvider which supplies a convention for the
/// [ApiController] attribute, however that convention is too strict for our purposes so we will have our own.
/// Uses UmbracoJsonModelBinder for complex parameters and those with BindingSource of Body, but leaves the rest alone see GH #11554
/// </para>
/// <para>
/// See https://shazwazza.com/post/custom-body-model-binding-per-controller-in-asp-net-core/
/// and https://github.com/dotnet/aspnetcore/issues/21724
/// </para>
/// </remarks>
public class UmbracoApiBehaviorApplicationModelProvider : IApplicationModelProvider
public UmbracoApiBehaviorApplicationModelProvider(IModelMetadataProvider modelMetadataProvider)
{
private readonly List<IActionModelConvention> _actionModelConventions;
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoApiBehaviorApplicationModelProvider"/> class.
/// </summary>
public UmbracoApiBehaviorApplicationModelProvider(IModelMetadataProvider modelMetadataProvider)
// see see https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#apicontroller-attribute
// for what these things actually do
// NOTE: we don't have attribute routing requirements and we cannot use ApiVisibilityConvention without attribute routing
_actionModelConventions = new List<IActionModelConvention>
{
// see see https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#apicontroller-attribute
// for what these things actually do
// NOTE: we don't have attribute routing requirements and we cannot use ApiVisibilityConvention without attribute routing
new ClientErrorResultFilterConvention(), // Ensures the responses without any body is converted into a simple json object with info instead of a string like "Status Code: 404; Not Found"
new ConsumesConstraintForFormFileParameterConvention(), // If an controller accepts files, it must accept multipart/form-data.
_actionModelConventions = new List<IActionModelConvention>()
// This ensures that all parameters of type BindingSource.Body and those of complex type are bound
// using our own UmbracoJsonModelBinder
new UmbracoJsonModelBinderConvention(modelMetadataProvider),
};
Type defaultErrorType = typeof(ProblemDetails);
var defaultErrorTypeAttribute = new ProducesErrorResponseTypeAttribute(defaultErrorType);
_actionModelConventions.Add(new ApiConventionApplicationModelConvention(defaultErrorTypeAttribute));
}
/// <inheritdoc />
/// <summary>
/// Will execute after <see cref="DefaultApplicationModelProvider" />
/// </summary>
public int Order => 0;
/// <inheritdoc />
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
/// <inheritdoc />
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
{
if (!IsUmbracoApiController(controller))
{
new ClientErrorResultFilterConvention(), // Ensures the responses without any body is converted into a simple json object with info instead of a string like "Status Code: 404; Not Found"
new ConsumesConstraintForFormFileParameterConvention(), // If an controller accepts files, it must accept multipart/form-data.
continue;
}
// This ensures that all parameters of type BindingSource.Body and those of complex type are bound
// using our own UmbracoJsonModelBinder
new UmbracoJsonModelBinderConvention(modelMetadataProvider)
};
var defaultErrorType = typeof(ProblemDetails);
var defaultErrorTypeAttribute = new ProducesErrorResponseTypeAttribute(defaultErrorType);
_actionModelConventions.Add(new ApiConventionApplicationModelConvention(defaultErrorTypeAttribute));
}
/// <inheritdoc/>
/// <summary>
/// Will execute after <see cref="DefaultApplicationModelProvider"/>
/// </summary>
public int Order => 0;
/// <inheritdoc/>
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
/// <inheritdoc/>
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
foreach (ActionModel action in controller.Actions)
{
if (!IsUmbracoApiController(controller))
foreach (IActionModelConvention convention in _actionModelConventions)
{
continue;
}
foreach (ActionModel action in controller.Actions)
{
foreach (IActionModelConvention convention in _actionModelConventions)
{
convention.Apply(action);
}
convention.Apply(action);
}
}
}
private static bool IsUmbracoApiController(ICommonModel controller)
=> controller.Attributes.OfType<UmbracoApiControllerAttribute>().Any();
}
private static bool IsUmbracoApiController(ICommonModel controller)
=> controller.Attributes.OfType<UmbracoApiControllerAttribute>().Any();
}

View File

@@ -4,58 +4,55 @@ using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Cms.Web.Common.ModelBinders;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
/// <summary>
/// Applies the <see cref="UmbracoJsonModelBinder" /> body model binder to any complex parameter and those with a
/// binding source of type <see cref="BindingSource.Body" />
/// </summary>
public class UmbracoJsonModelBinderConvention : IActionModelConvention
{
/// <summary>
/// Applies the <see cref="UmbracoJsonModelBinder"/> body model binder to any complex parameter and those with a
/// binding source of type <see cref="BindingSource.Body"/>
/// </summary>
public class UmbracoJsonModelBinderConvention : IActionModelConvention
private readonly IModelMetadataProvider _modelMetadataProvider;
public UmbracoJsonModelBinderConvention()
: this(StaticServiceProvider.Instance.GetRequiredService<IModelMetadataProvider>())
{
private readonly IModelMetadataProvider _modelMetadataProvider;
}
public UmbracoJsonModelBinderConvention()
: this(StaticServiceProvider.Instance.GetRequiredService<IModelMetadataProvider>())
{
}
public UmbracoJsonModelBinderConvention(IModelMetadataProvider modelMetadataProvider) =>
_modelMetadataProvider = modelMetadataProvider;
public UmbracoJsonModelBinderConvention(IModelMetadataProvider modelMetadataProvider)
/// <inheritdoc />
public void Apply(ActionModel action)
{
foreach (ParameterModel p in action.Parameters)
{
_modelMetadataProvider = modelMetadataProvider;
}
/// <inheritdoc/>
public void Apply(ActionModel action)
{
foreach (ParameterModel p in action.Parameters)
if (p.BindingInfo == null)
{
if (p.BindingInfo == null)
if (IsComplexTypeParameter(p))
{
if (IsComplexTypeParameter(p))
p.BindingInfo = new BindingInfo
{
p.BindingInfo = new BindingInfo
{
BindingSource = BindingSource.Body,
BinderType = typeof(UmbracoJsonModelBinder)
};
}
continue;
BindingSource = BindingSource.Body,
BinderType = typeof(UmbracoJsonModelBinder),
};
}
if (p.BindingInfo.BindingSource == BindingSource.Body)
{
p.BindingInfo.BinderType = typeof(UmbracoJsonModelBinder);
}
continue;
}
if (p.BindingInfo.BindingSource == BindingSource.Body)
{
p.BindingInfo.BinderType = typeof(UmbracoJsonModelBinder);
}
}
}
private bool IsComplexTypeParameter(ParameterModel parameter)
{
// No need for information from attributes on the parameter. Just use its type.
ModelMetadata metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterInfo.ParameterType);
private bool IsComplexTypeParameter(ParameterModel parameter)
{
// No need for information from attributes on the parameter. Just use its type.
ModelMetadata metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterInfo.ParameterType);
return metadata.IsComplexType;
}
return metadata.IsComplexType;
}
}

View File

@@ -1,61 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
/// <summary>
/// Applies the <see cref="VirtualPageConvention" /> to any action on a controller that is
/// <see cref="IVirtualPageController" />
/// </summary>
public class VirtualPageApplicationModelProvider : IApplicationModelProvider
{
private readonly List<IActionModelConvention> _actionModelConventions = new() { new VirtualPageConvention() };
/// <inheritdoc />
/// <summary>
/// Applies the <see cref="VirtualPageConvention"/> to any action on a controller that is <see cref="IVirtualPageController"/>
/// Will execute after <see cref="DefaultApplicationModelProvider" />
/// </summary>
public class VirtualPageApplicationModelProvider : IApplicationModelProvider
public int Order => 0;
/// <inheritdoc />
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
private readonly List<IActionModelConvention> _actionModelConventions = new List<IActionModelConvention>()
}
/// <inheritdoc />
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
{
new VirtualPageConvention()
};
/// <inheritdoc />
/// <summary>
/// Will execute after <see cref="DefaultApplicationModelProvider"/>
/// </summary>
public int Order => 0;
/// <inheritdoc />
public void OnProvidersExecuted(ApplicationModelProviderContext context) { }
/// <inheritdoc />
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
if (!IsVirtualPageController(controller))
{
if (!IsVirtualPageController(controller))
{
continue;
}
continue;
}
foreach (ActionModel action in controller.Actions.ToList())
foreach (ActionModel action in controller.Actions.ToList())
{
if (action.ActionName == nameof(IVirtualPageController.FindContent)
&& action.ActionMethod.ReturnType == typeof(IPublishedContent))
{
if (action.ActionName == nameof(IVirtualPageController.FindContent)
&& action.ActionMethod.ReturnType == typeof(IPublishedContent))
// this is not an action, it's just the implementation of IVirtualPageController
controller.Actions.Remove(action);
}
else
{
foreach (IActionModelConvention convention in _actionModelConventions)
{
// this is not an action, it's just the implementation of IVirtualPageController
controller.Actions.Remove(action);
}
else
{
foreach (IActionModelConvention convention in _actionModelConventions)
{
convention.Apply(action);
}
convention.Apply(action);
}
}
}
}
private bool IsVirtualPageController(ControllerModel controller)
=> controller.ControllerType.Implements<IVirtualPageController>();
}
private bool IsVirtualPageController(ControllerModel controller)
=> controller.ControllerType.Implements<IVirtualPageController>();
}

View File

@@ -1,14 +1,13 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Umbraco.Cms.Web.Common.Filters;
namespace Umbraco.Cms.Web.Common.ApplicationModels
namespace Umbraco.Cms.Web.Common.ApplicationModels;
/// <summary>
/// Adds the <see cref="UmbracoVirtualPageFilterAttribute" /> as a convention
/// </summary>
public class VirtualPageConvention : IActionModelConvention
{
/// <summary>
/// Adds the <see cref="UmbracoVirtualPageFilterAttribute"/> as a convention
/// </summary>
public class VirtualPageConvention : IActionModelConvention
{
/// <inheritdoc/>
public void Apply(ActionModel action) => action.Filters.Add(new UmbracoVirtualPageFilterAttribute());
}
/// <inheritdoc />
public void Apply(ActionModel action) => action.Filters.Add(new UmbracoVirtualPageFilterAttribute());
}