diff --git a/samples/MvcSandbox/Startup.cs b/samples/MvcSandbox/Startup.cs index 5c241cdc32..dac9f9d057 100644 --- a/samples/MvcSandbox/Startup.cs +++ b/samples/MvcSandbox/Startup.cs @@ -33,12 +33,11 @@ namespace MvcSandbox pattern: "/endpoints", displayName: "Home"); - builder.MapMvcRoute( + builder.MapControllerRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); - builder.MapMvcControllers(); - builder.MapRazorPages(); + builder.MapApplication(); builder.MapHealthChecks("/healthz"); }); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs index 289a1a2f34..5712fa6a55 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs @@ -43,7 +43,6 @@ namespace Microsoft.AspNetCore.Builder public string Name { get; } public string Pattern { get; } - public Type ControllerType { get; set; } // Non-inline defaults public RouteValueDictionary Defaults { get; } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointRouteBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointRouteBuilderExtensions.cs index a4d392a3b5..bef1b53372 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointRouteBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointRouteBuilderExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Linq; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; @@ -12,14 +12,21 @@ namespace Microsoft.AspNetCore.Builder { public static class MvcEndpointRouteBuilderExtensions { - public static IEndpointConventionBuilder MapMvcControllers( + public static IEndpointConventionBuilder MapApplication( this IEndpointRouteBuilder routeBuilder) { - return MapMvcControllers(routeBuilder); + return MapActionDescriptors(routeBuilder, null); } - public static IEndpointConventionBuilder MapMvcControllers( - this IEndpointRouteBuilder routeBuilder) where TController : ControllerBase + public static IEndpointConventionBuilder MapAssembly( + this IEndpointRouteBuilder routeBuilder) + { + return MapActionDescriptors(routeBuilder, typeof(TContainingType)); + } + + private static IEndpointConventionBuilder MapActionDescriptors( + this IEndpointRouteBuilder routeBuilder, + Type containingType) { var mvcEndpointDataSource = routeBuilder.DataSources.OfType().FirstOrDefault(); @@ -31,92 +38,64 @@ namespace Microsoft.AspNetCore.Builder var conventionBuilder = new DefaultEndpointConventionBuilder(); + var assemblyFilter = containingType?.Assembly; + mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add(actionDescriptor => { - if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor && - typeof(TController).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo)) + // Filter a descriptor by the assembly + // Note that this will only filter actions on controllers + // Does not support filtering Razor pages embedded in assemblies + if (assemblyFilter != null) { - return conventionBuilder; + if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor) + { + if (controllerActionDescriptor.ControllerTypeInfo.Assembly != assemblyFilter) + { + return null; + } + } } - return null; + return conventionBuilder; }); return conventionBuilder; } - public static IEndpointConventionBuilder MapMvcRoute( + public static IEndpointConventionBuilder MapControllerRoute( this IEndpointRouteBuilder routeBuilder, string name, string template) { - return MapMvcRoute(routeBuilder, name, template, defaults: null); + return MapControllerRoute(routeBuilder, name, template, defaults: null); } - public static IEndpointConventionBuilder MapMvcRoute( + public static IEndpointConventionBuilder MapControllerRoute( this IEndpointRouteBuilder routeBuilder, string name, string template, object defaults) { - return MapMvcRoute(routeBuilder, name, template, defaults, constraints: null); + return MapControllerRoute(routeBuilder, name, template, defaults, constraints: null); } - public static IEndpointConventionBuilder MapMvcRoute( + public static IEndpointConventionBuilder MapControllerRoute( this IEndpointRouteBuilder routeBuilder, string name, string template, object defaults, object constraints) { - return MapMvcRoute(routeBuilder, name, template, defaults, constraints, dataTokens: null); + return MapControllerRoute(routeBuilder, name, template, defaults, constraints, dataTokens: null); } - public static IEndpointConventionBuilder MapMvcRoute( + public static IEndpointConventionBuilder MapControllerRoute( this IEndpointRouteBuilder routeBuilder, string name, string template, object defaults, object constraints, object dataTokens) - { - return MapMvcRoute(routeBuilder, name, template, defaults, constraints, dataTokens); - } - - public static IEndpointConventionBuilder MapMvcRoute( - this IEndpointRouteBuilder routeBuilder, - string name, - string template) where TController : ControllerBase - { - return MapMvcRoute(routeBuilder, name, template, defaults: null); - } - - public static IEndpointConventionBuilder MapMvcRoute( - this IEndpointRouteBuilder routeBuilder, - string name, - string template, - object defaults) where TController : ControllerBase - { - return MapMvcRoute(routeBuilder, name, template, defaults, constraints: null); - } - - public static IEndpointConventionBuilder MapMvcRoute( - this IEndpointRouteBuilder routeBuilder, - string name, - string template, - object defaults, - object constraints) where TController : ControllerBase - { - return MapMvcRoute(routeBuilder, name, template, defaults, constraints, dataTokens: null); - } - - public static IEndpointConventionBuilder MapMvcRoute( - this IEndpointRouteBuilder routeBuilder, - string name, - string template, - object defaults, - object constraints, - object dataTokens) where TController : ControllerBase { var mvcEndpointDataSource = routeBuilder.DataSources.OfType().FirstOrDefault(); @@ -134,8 +113,6 @@ namespace Microsoft.AspNetCore.Builder new RouteValueDictionary(dataTokens), routeBuilder.ServiceProvider.GetRequiredService()); - endpointInfo.ControllerType = typeof(TController); - mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo); return endpointInfo; diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/MvcEndpointDataSource.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/MvcEndpointDataSource.cs index f4b7d895d9..53e37a4595 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/MvcEndpointDataSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/MvcEndpointDataSource.cs @@ -139,16 +139,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing // - Home/Login foreach (var endpointInfo in ConventionalEndpointInfos) { - if (endpointInfo.ControllerType != null && - endpointInfo.ControllerType != typeof(ControllerBase)) - { - if (!ValidateControllerConstraint(action, endpointInfo)) - { - // Action descriptor does not belong to a controller of the specified type - continue; - } - } - // An 'endpointInfo' is applicable if: // 1. it has a parameter (or default value) for 'required' non-null route value // 2. it does not have a parameter (or default value) for 'required' null route value @@ -244,16 +234,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing return null; } - private static bool ValidateControllerConstraint(ActionDescriptor action, MvcEndpointInfo endpointInfo) - { - if (action is ControllerActionDescriptor controllerActionDescriptor) - { - return endpointInfo.ControllerType.IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo); - } - - return false; - } - // CreateEndpoints processes the route pattern, replacing area/controller/action parameters with endpoint values // Because of default values it is possible for a route pattern to resolve to multiple endpoints private int CreateEndpoints(