Refactor MVC endpoint integration for templates (#8695)
This commit is contained in:
parent
9ff5a441f5
commit
6270ea48a6
|
|
@ -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");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue