Endpoing routing 3.0 registration (#8470)
This commit is contained in:
parent
a3e999762f
commit
e9737a90e5
|
|
@ -48,8 +48,8 @@
|
||||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-a-alpha1-master-builder-17073</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreRoutingPackageVersion>
|
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-a-alpha1-master-builder-17073</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||||
<MicrosoftAspNetCoreSessionPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreSessionPackageVersion>
|
<MicrosoftAspNetCoreSessionPackageVersion>3.0.0-alpha1-10617</MicrosoftAspNetCoreSessionPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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 MvcSandbox.AuthorizationMiddleware;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class AuthorizationAppBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
if (app == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(app));
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.UseMiddleware<AuthorizationMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace MvcSandbox.AuthorizationMiddleware
|
||||||
|
{
|
||||||
|
public static class AuthorizationEndpointConventionBuilder
|
||||||
|
{
|
||||||
|
public static T RequireAuthorization<T>(this T builder, params string[] roles) where T : IEndpointConventionBuilder
|
||||||
|
{
|
||||||
|
builder.Apply(model => model.Metadata.Add(new AuthorizeMetadataAttribute(roles)));
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace MvcSandbox.AuthorizationMiddleware
|
||||||
|
{
|
||||||
|
public class AuthorizationMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public AuthorizationMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
if (next == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var endpoint = httpContext.Features.Get<IEndpointFeature>()?.Endpoint;
|
||||||
|
var metadata = endpoint?.Metadata?.GetMetadata<AuthorizeMetadataAttribute>();
|
||||||
|
|
||||||
|
// Only run authorization if endpoint has metadata
|
||||||
|
if (metadata != null)
|
||||||
|
{
|
||||||
|
// Check if role querystring value is a valid role
|
||||||
|
if (!httpContext.Request.Query.TryGetValue("role", out var role) ||
|
||||||
|
!metadata.Roles.Contains(role.ToString(), StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
httpContext.Response.StatusCode = 401;
|
||||||
|
httpContext.Response.ContentType = "text/plain";
|
||||||
|
await httpContext.Response.WriteAsync($"Unauthorized access to '{endpoint.DisplayName}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _next(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MvcSandbox.AuthorizationMiddleware
|
||||||
|
{
|
||||||
|
public class AuthorizeMetadataAttribute : Attribute
|
||||||
|
{
|
||||||
|
public AuthorizeMetadataAttribute(string[] roles)
|
||||||
|
{
|
||||||
|
Roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] Roles { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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 Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MvcSandbox.AuthorizationMiddleware;
|
||||||
|
|
||||||
namespace MvcSandbox.Controllers
|
namespace MvcSandbox.Controllers
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MvcSandbox.Controllers
|
||||||
|
{
|
||||||
|
[Route("[controller]/[action]")]
|
||||||
|
public class LoginController : Controller
|
||||||
|
{
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class HealthChecksEndpointRouteBuilderExtensions
|
||||||
|
{
|
||||||
|
private static readonly Random _random = new Random();
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapHealthChecks(this IEndpointRouteBuilder builder, string pattern)
|
||||||
|
{
|
||||||
|
return builder.MapGet(
|
||||||
|
pattern,
|
||||||
|
async httpContext =>
|
||||||
|
{
|
||||||
|
await httpContext.Response.WriteAsync(_random.Next() % 2 == 0 ? "Up!" : "Down!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
// 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.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MvcSandbox.AuthorizationMiddleware;
|
||||||
|
|
||||||
namespace MvcSandbox
|
namespace MvcSandbox
|
||||||
{
|
{
|
||||||
|
|
@ -20,14 +27,46 @@ namespace MvcSandbox
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseEndpointRouting(builder =>
|
||||||
app.UseStaticFiles();
|
|
||||||
app.UseMvc(routes =>
|
|
||||||
{
|
{
|
||||||
routes.MapRoute(
|
builder.MapGet(
|
||||||
|
requestDelegate: WriteEndpoints,
|
||||||
|
pattern: "/endpoints",
|
||||||
|
displayName: "Home");
|
||||||
|
|
||||||
|
builder.MapMvcRoute(
|
||||||
name: "default",
|
name: "default",
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
template: "{controller=Home}/{action=Index}/{id?}");
|
||||||
|
|
||||||
|
builder.MapMvcControllers();
|
||||||
|
builder.MapRazorPages();
|
||||||
|
|
||||||
|
builder.MapHealthChecks("/healthz");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task WriteEndpoints(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var dataSource = httpContext.RequestServices.GetRequiredService<CompositeEndpointDataSource>();
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("Endpoints:");
|
||||||
|
foreach (var endpoint in dataSource.Endpoints.OfType<RouteEndpoint>().OrderBy(e => e.RoutePattern.RawText, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"- {endpoint.RoutePattern.RawText} '{endpoint.DisplayName}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = httpContext.Response;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
return response.WriteAsync(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
internal class DefaultEndpointConventionBuilder : IEndpointConventionBuilder
|
||||||
|
{
|
||||||
|
public DefaultEndpointConventionBuilder()
|
||||||
|
{
|
||||||
|
Conventions = new List<Action<EndpointModel>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Action<EndpointModel>> Conventions { get; }
|
||||||
|
|
||||||
|
public void Apply(Action<EndpointModel> convention)
|
||||||
|
{
|
||||||
|
Conventions.Add(convention);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -90,9 +90,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
if (options.Value.EnableEndpointRouting)
|
if (options.Value.EnableEndpointRouting)
|
||||||
{
|
{
|
||||||
var mvcEndpointDataSource = app.ApplicationServices
|
var mvcEndpointDataSource = app.ApplicationServices
|
||||||
.GetRequiredService<IEnumerable<EndpointDataSource>>()
|
.GetRequiredService<MvcEndpointDataSource>();
|
||||||
.OfType<MvcEndpointDataSource>()
|
|
||||||
.First();
|
|
||||||
var parameterPolicyFactory = app.ApplicationServices
|
var parameterPolicyFactory = app.ApplicationServices
|
||||||
.GetRequiredService<ParameterPolicyFactory>();
|
.GetRequiredService<ParameterPolicyFactory>();
|
||||||
|
|
||||||
|
|
@ -122,11 +120,21 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include all controllers with attribute routing and Razor pages
|
||||||
|
var defaultEndpointConventionBuilder = new DefaultEndpointConventionBuilder();
|
||||||
|
mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add((actionDescriptor) =>
|
||||||
|
{
|
||||||
|
return defaultEndpointConventionBuilder;
|
||||||
|
});
|
||||||
|
|
||||||
if (!app.Properties.TryGetValue(EndpointRoutingRegisteredKey, out _))
|
if (!app.Properties.TryGetValue(EndpointRoutingRegisteredKey, out _))
|
||||||
{
|
{
|
||||||
// Matching middleware has not been registered yet
|
// Matching middleware has not been registered yet
|
||||||
// For back-compat register middleware so an endpoint is matched and then immediately used
|
// For back-compat register middleware so an endpoint is matched and then immediately used
|
||||||
app.UseEndpointRouting();
|
app.UseEndpointRouting(routerBuilder =>
|
||||||
|
{
|
||||||
|
routerBuilder.DataSources.Add(mvcEndpointDataSource);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.UseEndpoint();
|
return app.UseEndpoint();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
internal class MvcEndpointInfo
|
internal class MvcEndpointInfo : DefaultEndpointConventionBuilder
|
||||||
{
|
{
|
||||||
public MvcEndpointInfo(
|
public MvcEndpointInfo(
|
||||||
string name,
|
string name,
|
||||||
|
|
@ -43,6 +43,7 @@ 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; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class MvcEndpointRouteBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IEndpointConventionBuilder MapMvcControllers(
|
||||||
|
this IEndpointRouteBuilder routeBuilder)
|
||||||
|
{
|
||||||
|
return MapMvcControllers<ControllerBase>(routeBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapMvcControllers<TController>(
|
||||||
|
this IEndpointRouteBuilder routeBuilder) where TController : ControllerBase
|
||||||
|
{
|
||||||
|
var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (mvcEndpointDataSource == null)
|
||||||
|
{
|
||||||
|
mvcEndpointDataSource = routeBuilder.ServiceProvider.GetRequiredService<MvcEndpointDataSource>();
|
||||||
|
routeBuilder.DataSources.Add(mvcEndpointDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
var conventionBuilder = new DefaultEndpointConventionBuilder();
|
||||||
|
|
||||||
|
mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add(actionDescriptor =>
|
||||||
|
{
|
||||||
|
if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor &&
|
||||||
|
typeof(TController).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo))
|
||||||
|
{
|
||||||
|
return conventionBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return conventionBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapMvcRoute(
|
||||||
|
this IEndpointRouteBuilder routeBuilder,
|
||||||
|
string name,
|
||||||
|
string template)
|
||||||
|
{
|
||||||
|
return MapMvcRoute<ControllerBase>(routeBuilder, name, template, defaults: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapMvcRoute(
|
||||||
|
this IEndpointRouteBuilder routeBuilder,
|
||||||
|
string name,
|
||||||
|
string template,
|
||||||
|
object defaults)
|
||||||
|
{
|
||||||
|
return MapMvcRoute<ControllerBase>(routeBuilder, name, template, defaults, constraints: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapMvcRoute(
|
||||||
|
this IEndpointRouteBuilder routeBuilder,
|
||||||
|
string name,
|
||||||
|
string template,
|
||||||
|
object defaults,
|
||||||
|
object constraints)
|
||||||
|
{
|
||||||
|
return MapMvcRoute<ControllerBase>(routeBuilder, name, template, defaults, constraints, dataTokens: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEndpointConventionBuilder MapMvcRoute(
|
||||||
|
this IEndpointRouteBuilder routeBuilder,
|
||||||
|
string name,
|
||||||
|
string template,
|
||||||
|
object defaults,
|
||||||
|
object constraints,
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (mvcEndpointDataSource == null)
|
||||||
|
{
|
||||||
|
mvcEndpointDataSource = routeBuilder.ServiceProvider.GetRequiredService<MvcEndpointDataSource>();
|
||||||
|
routeBuilder.DataSources.Add(mvcEndpointDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpointInfo = new MvcEndpointInfo(
|
||||||
|
name,
|
||||||
|
template,
|
||||||
|
new RouteValueDictionary(defaults),
|
||||||
|
new RouteValueDictionary(constraints),
|
||||||
|
new RouteValueDictionary(dataTokens),
|
||||||
|
routeBuilder.ServiceProvider.GetRequiredService<ParameterPolicyFactory>());
|
||||||
|
|
||||||
|
endpointInfo.ControllerType = typeof(TController);
|
||||||
|
|
||||||
|
mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo);
|
||||||
|
|
||||||
|
return endpointInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -270,8 +270,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
//
|
//
|
||||||
// Endpoint Routing / Endpoints
|
// Endpoint Routing / Endpoints
|
||||||
//
|
//
|
||||||
services.TryAddEnumerable(
|
services.TryAddSingleton<MvcEndpointDataSource>();
|
||||||
ServiceDescriptor.Singleton<EndpointDataSource, MvcEndpointDataSource>());
|
|
||||||
services.TryAddSingleton<MvcEndpointInvokerFactory>();
|
services.TryAddSingleton<MvcEndpointInvokerFactory>();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using System.Threading;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
@ -57,6 +58,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
_parameterPolicyFactory = parameterPolicyFactory;
|
_parameterPolicyFactory = parameterPolicyFactory;
|
||||||
|
|
||||||
ConventionalEndpointInfos = new List<MvcEndpointInfo>();
|
ConventionalEndpointInfos = new List<MvcEndpointInfo>();
|
||||||
|
AttributeRoutingConventionResolvers = new List<Func<ActionDescriptor, DefaultEndpointConventionBuilder>>();
|
||||||
|
|
||||||
// IMPORTANT: this needs to be the last thing we do in the constructor. Change notifications can happen immediately!
|
// IMPORTANT: this needs to be the last thing we do in the constructor. Change notifications can happen immediately!
|
||||||
//
|
//
|
||||||
|
|
@ -72,6 +74,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
public List<MvcEndpointInfo> ConventionalEndpointInfos { get; }
|
public List<MvcEndpointInfo> ConventionalEndpointInfos { get; }
|
||||||
|
|
||||||
|
public List<Func<ActionDescriptor, DefaultEndpointConventionBuilder>> AttributeRoutingConventionResolvers { get; }
|
||||||
|
|
||||||
public override IReadOnlyList<Endpoint> Endpoints
|
public override IReadOnlyList<Endpoint> Endpoints
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -134,6 +138,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// - 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
|
||||||
|
|
@ -164,11 +178,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
endpointInfo.DataTokens,
|
endpointInfo.DataTokens,
|
||||||
endpointInfo.ParameterPolicies,
|
endpointInfo.ParameterPolicies,
|
||||||
suppressLinkGeneration: false,
|
suppressLinkGeneration: false,
|
||||||
suppressPathMatching: false);
|
suppressPathMatching: false,
|
||||||
|
endpointInfo.Conventions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var conventionBuilder = ResolveActionConventionBuilder(action);
|
||||||
|
if (conventionBuilder == null)
|
||||||
|
{
|
||||||
|
// No convention builder for this action
|
||||||
|
// Do not create an endpoint for it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);
|
var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);
|
||||||
|
|
||||||
CreateEndpoints(
|
CreateEndpoints(
|
||||||
|
|
@ -183,7 +206,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
dataTokens: null,
|
dataTokens: null,
|
||||||
allParameterPolicies: null,
|
allParameterPolicies: null,
|
||||||
action.AttributeRouteInfo.SuppressLinkGeneration,
|
action.AttributeRouteInfo.SuppressLinkGeneration,
|
||||||
action.AttributeRouteInfo.SuppressPathMatching);
|
action.AttributeRouteInfo.SuppressPathMatching,
|
||||||
|
conventionBuilder.Conventions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,6 +229,30 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DefaultEndpointConventionBuilder ResolveActionConventionBuilder(ActionDescriptor action)
|
||||||
|
{
|
||||||
|
foreach (var filter in AttributeRoutingConventionResolvers)
|
||||||
|
{
|
||||||
|
var conventionBuilder = filter(action);
|
||||||
|
if (conventionBuilder != null)
|
||||||
|
{
|
||||||
|
return conventionBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
|
@ -219,7 +267,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
RouteValueDictionary dataTokens,
|
RouteValueDictionary dataTokens,
|
||||||
IDictionary<string, IList<IParameterPolicy>> allParameterPolicies,
|
IDictionary<string, IList<IParameterPolicy>> allParameterPolicies,
|
||||||
bool suppressLinkGeneration,
|
bool suppressLinkGeneration,
|
||||||
bool suppressPathMatching)
|
bool suppressPathMatching,
|
||||||
|
List<Action<EndpointModel>> conventions)
|
||||||
{
|
{
|
||||||
var newPathSegments = routePattern.PathSegments.ToList();
|
var newPathSegments = routePattern.PathSegments.ToList();
|
||||||
|
|
||||||
|
|
@ -245,7 +294,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
routeOrder++,
|
routeOrder++,
|
||||||
dataTokens,
|
dataTokens,
|
||||||
suppressLinkGeneration,
|
suppressLinkGeneration,
|
||||||
suppressPathMatching);
|
suppressPathMatching,
|
||||||
|
conventions);
|
||||||
endpoints.Add(subEndpoint);
|
endpoints.Add(subEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,7 +364,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
routeOrder++,
|
routeOrder++,
|
||||||
dataTokens,
|
dataTokens,
|
||||||
suppressLinkGeneration,
|
suppressLinkGeneration,
|
||||||
suppressPathMatching);
|
suppressPathMatching,
|
||||||
|
conventions);
|
||||||
endpoints.Add(endpoint);
|
endpoints.Add(endpoint);
|
||||||
|
|
||||||
return routeOrder;
|
return routeOrder;
|
||||||
|
|
@ -448,7 +499,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
int order,
|
int order,
|
||||||
RouteValueDictionary dataTokens,
|
RouteValueDictionary dataTokens,
|
||||||
bool suppressLinkGeneration,
|
bool suppressLinkGeneration,
|
||||||
bool suppressPathMatching)
|
bool suppressPathMatching,
|
||||||
|
List<Action<EndpointModel>> conventions)
|
||||||
{
|
{
|
||||||
RequestDelegate requestDelegate = (context) =>
|
RequestDelegate requestDelegate = (context) =>
|
||||||
{
|
{
|
||||||
|
|
@ -463,7 +515,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var defaults = new RouteValueDictionary(nonInlineDefaults);
|
var defaults = new RouteValueDictionary(nonInlineDefaults);
|
||||||
EnsureRequiredValuesInDefaults(action.RouteValues, defaults);
|
EnsureRequiredValuesInDefaults(action.RouteValues, defaults);
|
||||||
|
|
||||||
var metadataCollection = BuildEndpointMetadata(
|
var model = new RouteEndpointModel(requestDelegate, RoutePatternFactory.Pattern(patternRawText, defaults, parameterPolicies: null, segments), order);
|
||||||
|
|
||||||
|
AddEndpointMetadata(
|
||||||
|
model.Metadata,
|
||||||
action,
|
action,
|
||||||
routeName,
|
routeName,
|
||||||
new RouteValueDictionary(action.RouteValues),
|
new RouteValueDictionary(action.RouteValues),
|
||||||
|
|
@ -471,17 +526,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
suppressLinkGeneration,
|
suppressLinkGeneration,
|
||||||
suppressPathMatching);
|
suppressPathMatching);
|
||||||
|
|
||||||
var endpoint = new RouteEndpoint(
|
model.DisplayName = action.DisplayName;
|
||||||
requestDelegate,
|
|
||||||
RoutePatternFactory.Pattern(patternRawText, defaults, parameterPolicies: null, segments),
|
|
||||||
order,
|
|
||||||
metadataCollection,
|
|
||||||
action.DisplayName);
|
|
||||||
|
|
||||||
return endpoint;
|
if (conventions != null)
|
||||||
|
{
|
||||||
|
foreach (var convention in conventions)
|
||||||
|
{
|
||||||
|
convention(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (RouteEndpoint)model.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EndpointMetadataCollection BuildEndpointMetadata(
|
private static void AddEndpointMetadata(
|
||||||
|
IList<object> metadata,
|
||||||
ActionDescriptor action,
|
ActionDescriptor action,
|
||||||
string routeName,
|
string routeName,
|
||||||
RouteValueDictionary requiredValues,
|
RouteValueDictionary requiredValues,
|
||||||
|
|
@ -489,14 +548,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
bool suppressLinkGeneration,
|
bool suppressLinkGeneration,
|
||||||
bool suppressPathMatching)
|
bool suppressPathMatching)
|
||||||
{
|
{
|
||||||
var metadata = new List<object>
|
metadata.Add(action);
|
||||||
{
|
|
||||||
action
|
|
||||||
};
|
|
||||||
|
|
||||||
if (action.EndpointMetadata != null)
|
if (action.EndpointMetadata != null)
|
||||||
{
|
{
|
||||||
metadata.AddRange(action.EndpointMetadata);
|
foreach (var d in action.EndpointMetadata)
|
||||||
|
{
|
||||||
|
metadata.Add(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataTokens != null)
|
if (dataTokens != null)
|
||||||
|
|
@ -509,8 +568,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// Add filter descriptors to endpoint metadata
|
// Add filter descriptors to endpoint metadata
|
||||||
if (action.FilterDescriptors != null && action.FilterDescriptors.Count > 0)
|
if (action.FilterDescriptors != null && action.FilterDescriptors.Count > 0)
|
||||||
{
|
{
|
||||||
metadata.AddRange(action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer)
|
foreach (var filter in action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer).Select(f => f.Filter))
|
||||||
.Select(f => f.Filter));
|
{
|
||||||
|
metadata.Add(filter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
|
if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
|
||||||
|
|
@ -549,9 +610,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
metadata.Add(new SuppressMatchingMetadata());
|
metadata.Add(new SuppressMatchingMetadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadataCollection = new EndpointMetadataCollection(metadata);
|
|
||||||
return metadataCollection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure required values are a subset of defaults
|
// Ensure required values are a subset of defaults
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class RazorPagesEndpointRouteBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IEndpointConventionBuilder MapRazorPages(
|
||||||
|
this IEndpointRouteBuilder routeBuilder,
|
||||||
|
string basePath = null)
|
||||||
|
{
|
||||||
|
var mvcEndpointDataSource = routeBuilder.DataSources.OfType<MvcEndpointDataSource>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (mvcEndpointDataSource == null)
|
||||||
|
{
|
||||||
|
mvcEndpointDataSource = routeBuilder.ServiceProvider.GetRequiredService<MvcEndpointDataSource>();
|
||||||
|
routeBuilder.DataSources.Add(mvcEndpointDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
var conventionBuilder = new DefaultEndpointConventionBuilder();
|
||||||
|
|
||||||
|
mvcEndpointDataSource.AttributeRoutingConventionResolvers.Add(actionDescriptor =>
|
||||||
|
{
|
||||||
|
if (actionDescriptor is PageActionDescriptor pageActionDescriptor)
|
||||||
|
{
|
||||||
|
// TODO: Filter pages by path
|
||||||
|
return conventionBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return conventionBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Builder.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -56,12 +57,10 @@ namespace Microsoft.AspNetCore.Mvc.Core.Builder
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
template: "{controller=Home}/{action=Index}/{id?}");
|
||||||
});
|
});
|
||||||
|
|
||||||
var mvcEndpointDataSource = appBuilder.ApplicationServices
|
var routeOptions = appBuilder.ApplicationServices
|
||||||
.GetRequiredService<IEnumerable<EndpointDataSource>>()
|
.GetRequiredService<IOptions<RouteOptions>>();
|
||||||
.OfType<MvcEndpointDataSource>()
|
|
||||||
.First();
|
|
||||||
|
|
||||||
Assert.Empty(mvcEndpointDataSource.ConventionalEndpointInfos);
|
Assert.Empty(routeOptions.Value.EndpointDataSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -83,10 +82,10 @@ namespace Microsoft.AspNetCore.Mvc.Core.Builder
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
template: "{controller=Home}/{action=Index}/{id?}");
|
||||||
});
|
});
|
||||||
|
|
||||||
var mvcEndpointDataSource = appBuilder.ApplicationServices
|
var routeOptions = appBuilder.ApplicationServices
|
||||||
.GetRequiredService<IEnumerable<EndpointDataSource>>()
|
.GetRequiredService<IOptions<RouteOptions>>();
|
||||||
.OfType<MvcEndpointDataSource>()
|
|
||||||
.First();
|
var mvcEndpointDataSource = (MvcEndpointDataSource)Assert.Single(routeOptions.Value.EndpointDataSources, ds => ds is MvcEndpointDataSource);
|
||||||
|
|
||||||
var endpointInfo = Assert.Single(mvcEndpointDataSource.ConventionalEndpointInfos);
|
var endpointInfo = Assert.Single(mvcEndpointDataSource.ConventionalEndpointInfos);
|
||||||
Assert.Equal("default", endpointInfo.Name);
|
Assert.Equal("default", endpointInfo.Name);
|
||||||
|
|
|
||||||
|
|
@ -315,13 +315,6 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
typeof(ApiBehaviorApplicationModelProvider),
|
typeof(ApiBehaviorApplicationModelProvider),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
typeof(EndpointDataSource),
|
|
||||||
new Type[]
|
|
||||||
{
|
|
||||||
typeof(MvcEndpointDataSource),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
typeof(IStartupFilter),
|
typeof(IStartupFilter),
|
||||||
new Type[]
|
new Type[]
|
||||||
|
|
|
||||||
|
|
@ -804,6 +804,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
mvcEndpointInvokerFactory ?? new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())),
|
mvcEndpointInvokerFactory ?? new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())),
|
||||||
serviceProvider.GetRequiredService<ParameterPolicyFactory>());
|
serviceProvider.GetRequiredService<ParameterPolicyFactory>());
|
||||||
|
|
||||||
|
var defaultEndpointConventionBuilder = new DefaultEndpointConventionBuilder();
|
||||||
|
dataSource.AttributeRoutingConventionResolvers.Add((actionDescriptor) =>
|
||||||
|
{
|
||||||
|
return defaultEndpointConventionBuilder;
|
||||||
|
});
|
||||||
|
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(MvcCoreRouteOptionsSetup),
|
typeof(MvcCoreRouteOptionsSetup),
|
||||||
|
typeof(MvcCoreRouteOptionsSetup),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue