Refactor MVC endpoint integration for templates (#8695)

This commit is contained in:
James Newton-King 2018-11-15 17:04:26 +13:00 committed by GitHub
parent 9ff5a441f5
commit 6270ea48a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 80 deletions

View File

@ -33,12 +33,11 @@ namespace MvcSandbox
pattern: "/endpoints", pattern: "/endpoints",
displayName: "Home"); displayName: "Home");
builder.MapMvcRoute( builder.MapControllerRoute(
name: "default", name: "default",
template: "{controller=Home}/{action=Index}/{id?}"); template: "{controller=Home}/{action=Index}/{id?}");
builder.MapMvcControllers(); builder.MapApplication();
builder.MapRazorPages();
builder.MapHealthChecks("/healthz"); builder.MapHealthChecks("/healthz");
}); });

View File

@ -43,7 +43,6 @@ namespace Microsoft.AspNetCore.Builder
public string Name { get; } public string Name { get; }
public string Pattern { get; } public string Pattern { get; }
public Type ControllerType { get; set; }
// Non-inline defaults // Non-inline defaults
public RouteValueDictionary Defaults { get; } public RouteValueDictionary Defaults { get; }

View File

@ -1,8 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
@ -12,14 +12,21 @@ namespace Microsoft.AspNetCore.Builder
{ {
public static class MvcEndpointRouteBuilderExtensions public static class MvcEndpointRouteBuilderExtensions
{ {
public static IEndpointConventionBuilder MapMvcControllers( public static IEndpointConventionBuilder MapApplication(
this IEndpointRouteBuilder routeBuilder) this IEndpointRouteBuilder routeBuilder)
{ {
return MapMvcControllers<ControllerBase>(routeBuilder); return MapActionDescriptors(routeBuilder, null);
} }
public static IEndpointConventionBuilder MapMvcControllers<TController>( public static IEndpointConventionBuilder MapAssembly<TContainingType>(
this IEndpointRouteBuilder routeBuilder) where TController : ControllerBase this IEndpointRouteBuilder routeBuilder)
{
return MapActionDescriptors(routeBuilder, typeof(TContainingType));
}
private static IEndpointConventionBuilder MapActionDescriptors(
this IEndpointRouteBuilder routeBuilder,
Type containingType)
{ {
var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault(); var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault();
@ -31,92 +38,64 @@ namespace Microsoft.AspNetCore.Builder
var conventionBuilder = new DefaultEndpointConventionBuilder(); var conventionBuilder = new DefaultEndpointConventionBuilder();
var assemblyFilter = containingType?.Assembly;
mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add(actionDescriptor => mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add(actionDescriptor =>
{ {
if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor && // Filter a descriptor by the assembly
typeof(TController).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo)) // 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; return conventionBuilder;
} }
public static IEndpointConventionBuilder MapMvcRoute( public static IEndpointConventionBuilder MapControllerRoute(
this IEndpointRouteBuilder routeBuilder, this IEndpointRouteBuilder routeBuilder,
string name, string name,
string template) string template)
{ {
return MapMvcRoute<ControllerBase>(routeBuilder, name, template, defaults: null); return MapControllerRoute(routeBuilder, name, template, defaults: null);
} }
public static IEndpointConventionBuilder MapMvcRoute( public static IEndpointConventionBuilder MapControllerRoute(
this IEndpointRouteBuilder routeBuilder, this IEndpointRouteBuilder routeBuilder,
string name, string name,
string template, string template,
object defaults) object defaults)
{ {
return MapMvcRoute<ControllerBase>(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, this IEndpointRouteBuilder routeBuilder,
string name, string name,
string template, string template,
object defaults, object defaults,
object constraints) object constraints)
{ {
return MapMvcRoute<ControllerBase>(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, this IEndpointRouteBuilder routeBuilder,
string name, string name,
string template, string template,
object defaults, object defaults,
object constraints, object constraints,
object dataTokens) object dataTokens)
{
return MapMvcRoute<ControllerBase>(routeBuilder, name, template, defaults, constraints, dataTokens);
}
public static IEndpointConventionBuilder MapMvcRoute<TController>(
this IEndpointRouteBuilder routeBuilder,
string name,
string template) where TController : ControllerBase
{
return MapMvcRoute<TController>(routeBuilder, name, template, defaults: null);
}
public static IEndpointConventionBuilder MapMvcRoute<TController>(
this IEndpointRouteBuilder routeBuilder,
string name,
string template,
object defaults) where TController : ControllerBase
{
return MapMvcRoute<TController>(routeBuilder, name, template, defaults, constraints: null);
}
public static IEndpointConventionBuilder MapMvcRoute<TController>(
this IEndpointRouteBuilder routeBuilder,
string name,
string template,
object defaults,
object constraints) where TController : ControllerBase
{
return MapMvcRoute<TController>(routeBuilder, name, template, defaults, constraints, dataTokens: null);
}
public static IEndpointConventionBuilder MapMvcRoute<TController>(
this IEndpointRouteBuilder routeBuilder,
string name,
string template,
object defaults,
object constraints,
object dataTokens) where TController : ControllerBase
{ {
var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault(); var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault();
@ -134,8 +113,6 @@ namespace Microsoft.AspNetCore.Builder
new RouteValueDictionary(dataTokens), new RouteValueDictionary(dataTokens),
routeBuilder.ServiceProvider.GetRequiredService<ParameterPolicyFactory>()); routeBuilder.ServiceProvider.GetRequiredService<ParameterPolicyFactory>());
endpointInfo.ControllerType = typeof(TController);
mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo); mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo);
return endpointInfo; return endpointInfo;

View File

@ -139,16 +139,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing
// - Home/Login // - Home/Login
foreach (var endpointInfo in ConventionalEndpointInfos) 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: // An 'endpointInfo' is applicable if:
// 1. it has a parameter (or default value) for 'required' non-null route value // 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 // 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; 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 // 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 // Because of default values it is possible for a route pattern to resolve to multiple endpoints
private int CreateEndpoints( private int CreateEndpoints(