From bc6d8b5ecee275b77942714a0075b50a2412ba8d Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Wed, 22 Dec 2021 13:03:38 +0100 Subject: [PATCH] v9: Throw error on duplicate routes (#11774) * Add conflicting route service and check * Added testclass * Cleanup * Update src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> * Implemented out variable Co-authored-by: Elitsa Marinovska Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> --- .../Services/IConflictingRouteService.cs | 7 ++++ .../Runtime/RuntimeState.cs | 34 +++++++++++++++++- .../UmbracoBuilderExtensions.cs | 1 + .../Services/ConflictingRouteService.cs | 35 +++++++++++++++++++ .../Testing/TestConflictingRouteService.cs | 14 ++++++++ .../Testing/UmbracoIntegrationTest.cs | 3 ++ 6 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Services/IConflictingRouteService.cs create mode 100644 src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs create mode 100644 tests/Umbraco.Tests.Integration/Testing/TestConflictingRouteService.cs diff --git a/src/Umbraco.Core/Services/IConflictingRouteService.cs b/src/Umbraco.Core/Services/IConflictingRouteService.cs new file mode 100644 index 0000000000..04d81d7f88 --- /dev/null +++ b/src/Umbraco.Core/Services/IConflictingRouteService.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Cms.Core.Services +{ + public interface IConflictingRouteService + { + public bool HasConflictingRoutes(out string controllerName); + } +} diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs index 8eab08bfee..73b6692e3a 100644 --- a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; @@ -12,6 +13,7 @@ using Umbraco.Cms.Core.Semver; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Upgrade; using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Runtime @@ -30,6 +32,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime private readonly ILogger _logger; private readonly PendingPackageMigrations _packageMigrationState; private readonly Dictionary _startupState = new Dictionary(); + private readonly IConflictingRouteService _conflictingRouteService; /// /// The initial @@ -51,6 +54,25 @@ namespace Umbraco.Cms.Infrastructure.Runtime IUmbracoDatabaseFactory databaseFactory, ILogger logger, PendingPackageMigrations packageMigrationState) + : this( + globalSettings, + unattendedSettings, + umbracoVersion, + databaseFactory, + logger, + packageMigrationState, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public RuntimeState( + IOptions globalSettings, + IOptions unattendedSettings, + IUmbracoVersion umbracoVersion, + IUmbracoDatabaseFactory databaseFactory, + ILogger logger, + PendingPackageMigrations packageMigrationState, + IConflictingRouteService conflictingRouteService) { _globalSettings = globalSettings; _unattendedSettings = unattendedSettings; @@ -58,9 +80,9 @@ namespace Umbraco.Cms.Infrastructure.Runtime _databaseFactory = databaseFactory; _logger = logger; _packageMigrationState = packageMigrationState; + _conflictingRouteService = conflictingRouteService; } - /// public Version Version => _umbracoVersion.Version; @@ -101,6 +123,16 @@ namespace Umbraco.Cms.Infrastructure.Runtime return; } + // Check if we have multiple controllers with the same name. + if (_conflictingRouteService.HasConflictingRoutes(out string controllerName)) + { + Level = RuntimeLevel.BootFailed; + Reason = RuntimeLevelReason.BootFailedOnException; + BootFailedException = new BootFailedException($"Conflicting routes, you cannot have multiple controllers with the same name: {controllerName}"); + + return; + } + // Check the database state, whether we can connect or if it's in an upgrade or empty state, etc... switch (GetUmbracoDatabaseState(_databaseFactory)) diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index aeb329efb4..7a15d640e6 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -114,6 +114,7 @@ namespace Umbraco.Extensions }); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); return builder; diff --git a/src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs b/src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs new file mode 100644 index 0000000000..2951ace9e1 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Services/ConflictingRouteService.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.Controllers; + +namespace Umbraco.Cms.Web.BackOffice.Services +{ + public class ConflictingRouteService : IConflictingRouteService + { + private readonly TypeLoader _typeLoader; + + /// + /// Initializes a new instance of the class. + /// + public ConflictingRouteService(TypeLoader typeLoader) => _typeLoader = typeLoader; + + /// + public bool HasConflictingRoutes(out string controllerName) + { + var controllers = _typeLoader.GetTypes().ToList(); + foreach (Type controller in controllers) + { + if (controllers.Count(x => x.Name == controller.Name) > 1) + { + controllerName = controller.Name; + return true; + } + } + + controllerName = string.Empty; + return false; + } + } +} diff --git a/tests/Umbraco.Tests.Integration/Testing/TestConflictingRouteService.cs b/tests/Umbraco.Tests.Integration/Testing/TestConflictingRouteService.cs new file mode 100644 index 0000000000..c61cbb8a1d --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Testing/TestConflictingRouteService.cs @@ -0,0 +1,14 @@ +using System; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Tests.Integration.Testing +{ + public class TestConflictingRouteService : IConflictingRouteService + { + public bool HasConflictingRoutes(out string controllername) + { + controllername = string.Empty; + return false; + } + } +} diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 314a614b49..fb505e6b83 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -201,6 +201,9 @@ namespace Umbraco.Cms.Tests.Integration.Testing IWebHostEnvironment webHostEnvironment = TestHelper.GetWebHostEnvironment(); services.AddRequiredNetCoreServices(TestHelper, webHostEnvironment); + // We register this service because we need it for IRuntimeState, if we don't this breaks 900 tests + services.AddSingleton(); + // Add it! Core.Hosting.IHostingEnvironment hostingEnvironment = TestHelper.GetHostingEnvironment(); TypeLoader typeLoader = services.AddTypeLoader(