Reacting to Routing repo's EndpointFinder changes
This commit is contained in:
parent
4dd4e5ef3e
commit
54c14b8782
|
|
@ -30,7 +30,8 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
ParsedTemplate = TemplateParser.Parse(template);
|
ParsedTemplate = TemplateParser.Parse(template);
|
||||||
|
|
||||||
Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
|
Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
|
||||||
Defaults = GetDefaults(ParsedTemplate, defaults);
|
Defaults = defaults;
|
||||||
|
MergedDefaults = GetDefaults(ParsedTemplate, defaults);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +42,13 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Template { get; }
|
public string Template { get; }
|
||||||
|
|
||||||
|
// Non-inline defaults
|
||||||
public RouteValueDictionary Defaults { get; }
|
public RouteValueDictionary Defaults { get; }
|
||||||
|
|
||||||
|
// Inline and non-inline defaults merged into one
|
||||||
|
public RouteValueDictionary MergedDefaults { get; }
|
||||||
|
|
||||||
public IDictionary<string, IRouteConstraint> Constraints { get; }
|
public IDictionary<string, IRouteConstraint> Constraints { get; }
|
||||||
public RouteValueDictionary DataTokens { get; }
|
public RouteValueDictionary DataTokens { get; }
|
||||||
internal RouteTemplate ParsedTemplate { get; private set; }
|
internal RouteTemplate ParsedTemplate { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
if (action.AttributeRouteInfo == null)
|
if (action.AttributeRouteInfo == null)
|
||||||
{
|
{
|
||||||
|
// In traditional conventional routing setup, the routes defined by a user have a static order
|
||||||
|
// defined by how they are added into the list. We would like to maintain the same order when building
|
||||||
|
// up the endpoints too.
|
||||||
|
//
|
||||||
|
// Start with an order of '1' for conventional routes as attribute routes have a default order of '0'.
|
||||||
|
// This is for scenarios dealing with migrating existing Routing based code to Dispatcher world.
|
||||||
|
var conventionalRouteOrder = 0;
|
||||||
|
|
||||||
// Check each of the conventional templates to see if the action would be reachable
|
// Check each of the conventional templates to see if the action would be reachable
|
||||||
// If the action and template are compatible then create an endpoint with the
|
// If the action and template are compatible then create an endpoint with the
|
||||||
// area/controller/action parameter parts replaced with literals
|
// area/controller/action parameter parts replaced with literals
|
||||||
|
|
@ -100,7 +108,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
var subTemplate = RouteTemplateWriter.ToString(newEndpointTemplate.Segments.Take(i));
|
var subTemplate = RouteTemplateWriter.ToString(newEndpointTemplate.Segments.Take(i));
|
||||||
|
|
||||||
var subEndpoint = CreateEndpoint(action, subTemplate, 0, endpointInfo);
|
var subEndpoint = CreateEndpoint(
|
||||||
|
action,
|
||||||
|
endpointInfo.Name,
|
||||||
|
subTemplate,
|
||||||
|
endpointInfo.Defaults,
|
||||||
|
++conventionalRouteOrder,
|
||||||
|
endpointInfo);
|
||||||
_endpoints.Add(subEndpoint);
|
_endpoints.Add(subEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,14 +133,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var newTemplate = RouteTemplateWriter.ToString(newEndpointTemplate.Segments);
|
var newTemplate = RouteTemplateWriter.ToString(newEndpointTemplate.Segments);
|
||||||
|
|
||||||
var endpoint = CreateEndpoint(action, newTemplate, 0, endpointInfo);
|
var endpoint = CreateEndpoint(
|
||||||
|
action,
|
||||||
|
endpointInfo.Name,
|
||||||
|
newTemplate,
|
||||||
|
endpointInfo.Defaults,
|
||||||
|
++conventionalRouteOrder,
|
||||||
|
endpointInfo);
|
||||||
_endpoints.Add(endpoint);
|
_endpoints.Add(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var endpoint = CreateEndpoint(action, action.AttributeRouteInfo.Template, action.AttributeRouteInfo.Order, action.AttributeRouteInfo);
|
var endpoint = CreateEndpoint(
|
||||||
|
action,
|
||||||
|
action.AttributeRouteInfo.Name,
|
||||||
|
action.AttributeRouteInfo.Template,
|
||||||
|
nonInlineDefaults: null,
|
||||||
|
action.AttributeRouteInfo.Order,
|
||||||
|
action.AttributeRouteInfo);
|
||||||
_endpoints.Add(endpoint);
|
_endpoints.Add(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +170,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseDefaultValuePlusRemainingSegementsOptional(int segmentIndex, ActionDescriptor action, MvcEndpointInfo endpointInfo, RouteTemplate template)
|
private bool UseDefaultValuePlusRemainingSegementsOptional(
|
||||||
|
int segmentIndex,
|
||||||
|
ActionDescriptor action,
|
||||||
|
MvcEndpointInfo endpointInfo,
|
||||||
|
RouteTemplate template)
|
||||||
{
|
{
|
||||||
// Check whether the remaining segments are all optional and one or more of them is
|
// Check whether the remaining segments are all optional and one or more of them is
|
||||||
// for area/controller/action and has a default value
|
// for area/controller/action and has a default value
|
||||||
|
|
@ -164,7 +194,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
if (IsMvcParameter(part.Name))
|
if (IsMvcParameter(part.Name))
|
||||||
{
|
{
|
||||||
if (endpointInfo.Defaults[part.Name] is string defaultValue
|
if (endpointInfo.MergedDefaults[part.Name] is string defaultValue
|
||||||
&& action.RouteValues.TryGetValue(part.Name, out var routeValue)
|
&& action.RouteValues.TryGetValue(part.Name, out var routeValue)
|
||||||
&& string.Equals(defaultValue, routeValue, StringComparison.OrdinalIgnoreCase))
|
&& string.Equals(defaultValue, routeValue, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
@ -196,7 +226,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (endpointInfo.Defaults != null && string.Equals(actionValue, endpointInfo.Defaults[routeKey] as string, StringComparison.OrdinalIgnoreCase))
|
if (endpointInfo.MergedDefaults != null && string.Equals(actionValue, endpointInfo.MergedDefaults[routeKey] as string, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +265,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatcherEndpoint CreateEndpoint(ActionDescriptor action, string template, int order, object source)
|
private MatcherEndpoint CreateEndpoint(
|
||||||
|
ActionDescriptor action,
|
||||||
|
string routeName,
|
||||||
|
string template,
|
||||||
|
object nonInlineDefaults,
|
||||||
|
int order,
|
||||||
|
object source)
|
||||||
{
|
{
|
||||||
RequestDelegate invokerDelegate = (context) =>
|
RequestDelegate invokerDelegate = (context) =>
|
||||||
{
|
{
|
||||||
|
|
@ -260,10 +296,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
metadata.Add(source);
|
metadata.Add(source);
|
||||||
metadata.Add(action);
|
metadata.Add(action);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(routeName))
|
||||||
|
{
|
||||||
|
metadata.Add(new RouteNameMetadata(routeName));
|
||||||
|
}
|
||||||
|
|
||||||
// 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).Select(f => f.Filter));
|
metadata.AddRange(action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer)
|
||||||
|
.Select(f => f.Filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
|
if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
|
||||||
|
|
@ -281,14 +323,41 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var endpoint = new MatcherEndpoint(
|
var endpoint = new MatcherEndpoint(
|
||||||
next => invokerDelegate,
|
next => invokerDelegate,
|
||||||
template,
|
template,
|
||||||
action.RouteValues,
|
new RouteValueDictionary(nonInlineDefaults),
|
||||||
|
new RouteValueDictionary(action.RouteValues),
|
||||||
order,
|
order,
|
||||||
metadataCollection,
|
metadataCollection,
|
||||||
action.DisplayName,
|
action.DisplayName);
|
||||||
address: null);
|
|
||||||
|
// Use defaults after the endpoint is created as it merges both the inline and
|
||||||
|
// non-inline defaults into one.
|
||||||
|
EnsureRequiredValuesInDefaults(endpoint.RequiredValues, endpoint.Defaults);
|
||||||
|
|
||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure required values are a subset of defaults
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// Template: {controller}/{action}/{category}/{id?}
|
||||||
|
// Defaults(in-line or non in-line): category=products
|
||||||
|
// Required values: controller=foo, action=bar
|
||||||
|
// Final constructed template: foo/bar/{category}/{id?}
|
||||||
|
// Final defaults: controller=foo, action=bar, category=products
|
||||||
|
//
|
||||||
|
// Template: {controller=Home}/{action=Index}/{category=products}/{id?}
|
||||||
|
// Defaults: controller=Home, action=Index, category=products
|
||||||
|
// Required values: controller=foo, action=bar
|
||||||
|
// Final constructed template: foo/bar/{category}/{id?}
|
||||||
|
// Final defaults: controller=foo, action=bar, category=products
|
||||||
|
private void EnsureRequiredValuesInDefaults(RouteValueDictionary requiredValues, RouteValueDictionary defaults)
|
||||||
|
{
|
||||||
|
foreach (var kvp in requiredValues)
|
||||||
|
{
|
||||||
|
defaults[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IChangeToken GetCompositeChangeToken()
|
private IChangeToken GetCompositeChangeToken()
|
||||||
{
|
{
|
||||||
if (_actionDescriptorChangeProviders.Length == 1)
|
if (_actionDescriptorChangeProviders.Length == 1)
|
||||||
|
|
@ -321,5 +390,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
public override IReadOnlyList<Endpoint> Endpoints => _endpoints;
|
public override IReadOnlyList<Endpoint> Endpoints => _endpoints;
|
||||||
|
|
||||||
public List<MvcEndpointInfo> ConventionalEndpointInfos { get; }
|
public List<MvcEndpointInfo> ConventionalEndpointInfos { get; }
|
||||||
|
|
||||||
|
private class RouteNameMetadata : IRouteNameMetadata
|
||||||
|
{
|
||||||
|
public RouteNameMetadata(string routeName)
|
||||||
|
{
|
||||||
|
Name = routeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.EndpointFinders;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Routing
|
namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
|
|
@ -15,16 +16,21 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
{
|
{
|
||||||
private readonly ILogger<DispatcherUrlHelper> _logger;
|
private readonly ILogger<DispatcherUrlHelper> _logger;
|
||||||
private readonly ILinkGenerator _linkGenerator;
|
private readonly ILinkGenerator _linkGenerator;
|
||||||
|
private readonly IEndpointFinder<RouteValuesBasedEndpointFinderContext> _routeValuesBasedEndpointFinder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DispatcherUrlHelper"/> class using the specified
|
/// Initializes a new instance of the <see cref="DispatcherUrlHelper"/> class using the specified
|
||||||
/// <paramref name="actionContext"/>.
|
/// <paramref name="actionContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current request.</param>
|
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current request.</param>
|
||||||
|
/// <param name="routeValuesBasedEndpointFinder">
|
||||||
|
/// The <see cref="IEndpointFinder{T}"/> which finds endpoints by required route values.
|
||||||
|
/// </param>
|
||||||
/// <param name="linkGenerator">The <see cref="ILinkGenerator"/> used to generate the link.</param>
|
/// <param name="linkGenerator">The <see cref="ILinkGenerator"/> used to generate the link.</param>
|
||||||
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||||
public DispatcherUrlHelper(
|
public DispatcherUrlHelper(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
|
IEndpointFinder<RouteValuesBasedEndpointFinderContext> routeValuesBasedEndpointFinder,
|
||||||
ILinkGenerator linkGenerator,
|
ILinkGenerator linkGenerator,
|
||||||
ILogger<DispatcherUrlHelper> logger)
|
ILogger<DispatcherUrlHelper> logger)
|
||||||
: base(actionContext)
|
: base(actionContext)
|
||||||
|
|
@ -40,6 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
_linkGenerator = linkGenerator;
|
_linkGenerator = linkGenerator;
|
||||||
|
_routeValuesBasedEndpointFinder = routeValuesBasedEndpointFinder;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,12 +86,17 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
valuesDictionary["controller"] = urlActionContext.Controller;
|
valuesDictionary["controller"] = urlActionContext.Controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
var endpoints = _routeValuesBasedEndpointFinder.FindEndpoints(
|
||||||
new LinkGeneratorContext()
|
new RouteValuesBasedEndpointFinderContext()
|
||||||
{
|
{
|
||||||
SuppliedValues = valuesDictionary,
|
ExplicitValues = valuesDictionary,
|
||||||
AmbientValues = AmbientValues
|
AmbientValues = AmbientValues
|
||||||
},
|
});
|
||||||
|
|
||||||
|
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
||||||
|
endpoints,
|
||||||
|
valuesDictionary,
|
||||||
|
AmbientValues,
|
||||||
out var link);
|
out var link);
|
||||||
|
|
||||||
if (!successfullyGeneratedLink)
|
if (!successfullyGeneratedLink)
|
||||||
|
|
@ -107,13 +119,18 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
|
|
||||||
var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
|
var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
|
||||||
|
|
||||||
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
var endpoints = _routeValuesBasedEndpointFinder.FindEndpoints(
|
||||||
new LinkGeneratorContext()
|
new RouteValuesBasedEndpointFinderContext()
|
||||||
{
|
{
|
||||||
Address = new Address(routeContext.RouteName),
|
RouteName = routeContext.RouteName,
|
||||||
SuppliedValues = valuesDictionary,
|
ExplicitValues = valuesDictionary,
|
||||||
AmbientValues = AmbientValues
|
AmbientValues = AmbientValues
|
||||||
},
|
});
|
||||||
|
|
||||||
|
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
||||||
|
endpoints,
|
||||||
|
valuesDictionary,
|
||||||
|
AmbientValues,
|
||||||
out var link);
|
out var link);
|
||||||
|
|
||||||
if (!successfullyGeneratedLink)
|
if (!successfullyGeneratedLink)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.EndpointFinders;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -49,9 +50,16 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
var endpointFeature = httpContext.Features.Get<IEndpointFeature>();
|
var endpointFeature = httpContext.Features.Get<IEndpointFeature>();
|
||||||
if (endpointFeature?.Endpoint != null)
|
if (endpointFeature?.Endpoint != null)
|
||||||
{
|
{
|
||||||
var linkGenerator = httpContext.RequestServices.GetRequiredService<ILinkGenerator>();
|
var services = httpContext.RequestServices;
|
||||||
var logger = httpContext.RequestServices.GetRequiredService<ILogger<DispatcherUrlHelper>>();
|
var linkGenerator = services.GetRequiredService<ILinkGenerator>();
|
||||||
urlHelper = new DispatcherUrlHelper(context, linkGenerator, logger);
|
var routeValuesBasedEndpointFinder = services.GetRequiredService<IEndpointFinder<RouteValuesBasedEndpointFinderContext>>();
|
||||||
|
var logger = services.GetRequiredService<ILogger<DispatcherUrlHelper>>();
|
||||||
|
|
||||||
|
urlHelper = new DispatcherUrlHelper(
|
||||||
|
context,
|
||||||
|
routeValuesBasedEndpointFinder,
|
||||||
|
linkGenerator,
|
||||||
|
logger);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Matchers;
|
using Microsoft.AspNetCore.Routing.Matchers;
|
||||||
|
|
@ -20,7 +19,7 @@ using Microsoft.Extensions.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
public class MvcEndpointDataSourceTests
|
public class MvcEndpointDataSourceTests
|
||||||
{
|
{
|
||||||
|
|
@ -29,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var routeValue = "Value";
|
var routeValue = "Value";
|
||||||
var routeValues = new Dictionary<string, string>
|
var requiredValues = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["Name"] = routeValue
|
["Name"] = routeValue
|
||||||
};
|
};
|
||||||
|
|
@ -43,7 +42,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
{
|
{
|
||||||
new ActionDescriptor
|
new ActionDescriptor
|
||||||
{
|
{
|
||||||
RouteValues = routeValues,
|
RouteValues = requiredValues,
|
||||||
DisplayName = displayName,
|
DisplayName = displayName,
|
||||||
AttributeRouteInfo = new AttributeRouteInfo
|
AttributeRouteInfo = new AttributeRouteInfo
|
||||||
{
|
{
|
||||||
|
|
@ -66,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
var endpoint = Assert.Single(dataSource.Endpoints);
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
|
||||||
var endpointValue = matcherEndpoint.Values["Name"];
|
var endpointValue = matcherEndpoint.RequiredValues["Name"];
|
||||||
Assert.Equal(routeValue, endpointValue);
|
Assert.Equal(routeValue, endpointValue);
|
||||||
|
|
||||||
Assert.Equal(displayName, matcherEndpoint.DisplayName);
|
Assert.Equal(displayName, matcherEndpoint.DisplayName);
|
||||||
|
|
@ -187,13 +186,9 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
public void InitializeEndpoints_SingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
public void InitializeEndpoints_SingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
new { controller = "TestController", action = "TestAction" });
|
||||||
{
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
CreateActionDescriptor("TestController", "TestAction")
|
|
||||||
}, 0));
|
|
||||||
|
|
||||||
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -218,13 +213,9 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
public void InitializeEndpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
public void InitializeEndpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
new { controller = "TestController", action = "TestAction", area = "TestArea" });
|
||||||
{
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
CreateActionDescriptor("TestController", "TestAction", "TestArea")
|
|
||||||
}, 0));
|
|
||||||
|
|
||||||
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -243,13 +234,9 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
public void InitializeEndpoints_SingleAction_WithActionDefault()
|
public void InitializeEndpoints_SingleAction_WithActionDefault()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
new { controller = "TestController", action = "TestAction" });
|
||||||
{
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
CreateActionDescriptor("TestController", "TestAction")
|
|
||||||
}, 0));
|
|
||||||
|
|
||||||
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
||||||
string.Empty,
|
string.Empty,
|
||||||
"{controller}/{action}",
|
"{controller}/{action}",
|
||||||
|
|
@ -268,15 +255,11 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
public void InitializeEndpoints_MultipleActions_WithActionConstraint()
|
public void InitializeEndpoints_MultipleActions_WithActionConstraint()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
new { controller = "TestController", action = "TestAction" },
|
||||||
{
|
new { controller = "TestController", action = "TestAction1" },
|
||||||
CreateActionDescriptor("TestController", "TestAction"),
|
new { controller = "TestController", action = "TestAction2" });
|
||||||
CreateActionDescriptor("TestController", "TestAction1"),
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
CreateActionDescriptor("TestController", "TestAction2")
|
|
||||||
}, 0));
|
|
||||||
|
|
||||||
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
||||||
string.Empty,
|
string.Empty,
|
||||||
"{controller}/{action}",
|
"{controller}/{action}",
|
||||||
|
|
@ -297,16 +280,12 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
public void InitializeEndpoints_MultipleActions(string endpointInfoRoute, string[] finalEndpointTemplates)
|
public void InitializeEndpoints_MultipleActions(string endpointInfoRoute, string[] finalEndpointTemplates)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
new { controller = "TestController1", action = "TestAction1" },
|
||||||
{
|
new { controller = "TestController1", action = "TestAction2" },
|
||||||
CreateActionDescriptor("TestController1", "TestAction1"),
|
new { controller = "TestController1", action = "TestAction3" },
|
||||||
CreateActionDescriptor("TestController1", "TestAction2"),
|
new { controller = "TestController2", action = "TestAction1" });
|
||||||
CreateActionDescriptor("TestController1", "TestAction3"),
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
CreateActionDescriptor("TestController2", "TestAction1")
|
|
||||||
}, 0));
|
|
||||||
|
|
||||||
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
||||||
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
||||||
string.Empty,
|
string.Empty,
|
||||||
endpointInfoRoute));
|
endpointInfoRoute));
|
||||||
|
|
@ -322,6 +301,276 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
Assert.Collection(dataSource.Endpoints, inspectors);
|
Assert.Collection(dataSource.Endpoints, inspectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConventionalRoute_WithNoRouteName_DoesNotAddRouteNameMetadata()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
|
new { controller = "Home", action = "Index" });
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(
|
||||||
|
CreateEndpointInfo(string.Empty, "named/{controller}/{action}/{id?}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
||||||
|
Assert.Null(routeNameMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanCreateMultipleEndpoints_WithSameRouteName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
|
new { controller = "Home", action = "Index" },
|
||||||
|
new { controller = "Products", action = "Details" });
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(
|
||||||
|
CreateEndpointInfo("namedRoute", "named/{controller}/{action}/{id?}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
dataSource.Endpoints,
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
||||||
|
Assert.NotNull(routeNameMetadata);
|
||||||
|
Assert.Equal("namedRoute", routeNameMetadata.Name);
|
||||||
|
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.Template);
|
||||||
|
},
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
||||||
|
Assert.NotNull(routeNameMetadata);
|
||||||
|
Assert.Equal("namedRoute", routeNameMetadata.Name);
|
||||||
|
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.Template);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InitializeEndpoints_ConventionalRoutes_StaticallyDefinedOrder_IsMaintained()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
||||||
|
new { controller = "Home", action = "Index" },
|
||||||
|
new { controller = "Products", action = "Details" });
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
||||||
|
name: string.Empty,
|
||||||
|
template: "{controller}/{action}/{id?}"));
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
||||||
|
name: "namedRoute",
|
||||||
|
"named/{controller}/{action}/{id?}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
dataSource.Endpoints,
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
Assert.Equal("Home/Index/{id?}", matcherEndpoint.Template);
|
||||||
|
Assert.Equal(1, matcherEndpoint.Order);
|
||||||
|
},
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.Template);
|
||||||
|
Assert.Equal(2, matcherEndpoint.Order);
|
||||||
|
},
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
Assert.Equal("Products/Details/{id?}", matcherEndpoint.Template);
|
||||||
|
Assert.Equal(1, matcherEndpoint.Order);
|
||||||
|
},
|
||||||
|
(ep) =>
|
||||||
|
{
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
||||||
|
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.Template);
|
||||||
|
Assert.Equal(2, matcherEndpoint.Order);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RequiredValue_WithNoCorresponding_TemplateParameter_DoesNotProduceEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(new { area = "admin", controller = "home", action = "index" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(dataSource.Endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since area, controller, action and page are special, check to see if the followin test succeeds for a
|
||||||
|
// custom required value too.
|
||||||
|
[Fact(Skip = "Needs review")]
|
||||||
|
public void NonReservedRequiredValue_WithNoCorresponding_TemplateParameter_DoesNotProduceEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(new { controller = "home", action = "index", foo = "bar" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(dataSource.Endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TemplateParameter_WithNoDefaultOrRequiredValue_DoesNotProduceEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(new { controller = "home", action = "index" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area}/{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(dataSource.Endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TemplateParameter_WithDefaultValue_AndNullRequiredValue_DoesNotProduceEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(new { area = (string)null, controller = "home", action = "index" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area=admin}/{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(dataSource.Endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TemplateParameter_WithNullRequiredValue_DoesNotProduceEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(new { area = (string)null, controller = "home", action = "index" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area}/{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(dataSource.Endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoDefaultValues_RequiredValues_UsedToCreateDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDefaults = new RouteValueDictionary(new { controller = "Foo", action = "Bar" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: expectedDefaults);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
Assert.Equal("Foo/Bar", matcherEndpoint.Template);
|
||||||
|
AssertIsSubset(expectedDefaults, matcherEndpoint.Defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RequiredValues_NotPresent_InDefaultValues_IsAddedToDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(
|
||||||
|
new { controller = "Foo", action = "Bar", subarea = "test" });
|
||||||
|
var expectedDefaults = requiredValues;
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(
|
||||||
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
Assert.Equal("Foo/Bar", matcherEndpoint.Template);
|
||||||
|
AssertIsSubset(expectedDefaults, matcherEndpoint.Defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RequiredValues_IsSubsetOf_DefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(
|
||||||
|
new { controller = "Foo", action = "Bar", subarea = "test" });
|
||||||
|
var expectedDefaults = new RouteValueDictionary(
|
||||||
|
new { controller = "Foo", action = "Bar", subarea = "test", subscription = "general" });
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(
|
||||||
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}/{subscription=general}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.Template);
|
||||||
|
AssertIsSubset(expectedDefaults, matcherEndpoint.Defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RequiredValues_HavingNull_AndNotPresentInDefaultValues_IsAddedToDefaultValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requiredValues = new RouteValueDictionary(
|
||||||
|
new { area = (string)null, controller = "Foo", action = "Bar", page = (string)null });
|
||||||
|
var expectedDefaults = requiredValues;
|
||||||
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
||||||
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
||||||
|
dataSource.ConventionalEndpointInfos.Add(
|
||||||
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
dataSource.InitializeEndpoints();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var endpoint = Assert.Single(dataSource.Endpoints);
|
||||||
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
||||||
|
Assert.Equal("Foo/Bar", matcherEndpoint.Template);
|
||||||
|
AssertIsSubset(expectedDefaults, matcherEndpoint.Defaults);
|
||||||
|
}
|
||||||
|
|
||||||
private MvcEndpointDataSource CreateMvcEndpointDataSource(
|
private MvcEndpointDataSource CreateMvcEndpointDataSource(
|
||||||
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null,
|
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null,
|
||||||
MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null,
|
MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null,
|
||||||
|
|
@ -362,18 +611,45 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Internal
|
||||||
return new MvcEndpointInfo(name, template, defaults, constraints, dataTokens, constraintResolver);
|
return new MvcEndpointInfo(name, template, defaults, constraints, dataTokens, constraintResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IActionDescriptorCollectionProvider GetActionDescriptorCollection(params object[] requiredValues)
|
||||||
|
{
|
||||||
|
var actionDescriptors = new List<ActionDescriptor>();
|
||||||
|
foreach (var requiredValue in requiredValues)
|
||||||
|
{
|
||||||
|
actionDescriptors.Add(CreateActionDescriptor(requiredValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionDescriptorCollectionProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
|
actionDescriptorCollectionProvider
|
||||||
|
.Setup(m => m.ActionDescriptors)
|
||||||
|
.Returns(new ActionDescriptorCollection(actionDescriptors, version: 0));
|
||||||
|
return actionDescriptorCollectionProvider.Object;
|
||||||
|
}
|
||||||
|
|
||||||
private ActionDescriptor CreateActionDescriptor(string controller, string action, string area = null)
|
private ActionDescriptor CreateActionDescriptor(string controller, string action, string area = null)
|
||||||
{
|
{
|
||||||
return new ActionDescriptor
|
return CreateActionDescriptor(new { controller = controller, action = action, area = area });
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionDescriptor CreateActionDescriptor(object requiredValues)
|
||||||
|
{
|
||||||
|
var actionDescriptor = new ActionDescriptor();
|
||||||
|
var routeValues = new RouteValueDictionary(requiredValues);
|
||||||
|
foreach (var kvp in routeValues)
|
||||||
{
|
{
|
||||||
RouteValues =
|
actionDescriptor.RouteValues[kvp.Key] = kvp.Value?.ToString();
|
||||||
{
|
}
|
||||||
["controller"] = controller,
|
return actionDescriptor;
|
||||||
["action"] = action,
|
}
|
||||||
["area"] = area
|
|
||||||
},
|
private void AssertIsSubset(RouteValueDictionary subset, RouteValueDictionary fullSet)
|
||||||
DisplayName = string.Empty,
|
{
|
||||||
};
|
foreach (var subsetPair in subset)
|
||||||
|
{
|
||||||
|
var isPresent = fullSet.TryGetValue(subsetPair.Key, out var fullSetPairValue);
|
||||||
|
Assert.True(isPresent);
|
||||||
|
Assert.Equal(subsetPair.Value, fullSetPairValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,12 +36,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
endpoints.Add(new MatcherEndpoint(
|
endpoints.Add(new MatcherEndpoint(
|
||||||
next => httpContext => Task.CompletedTask,
|
next => httpContext => Task.CompletedTask,
|
||||||
template,
|
template,
|
||||||
null,
|
new RouteValueDictionary(),
|
||||||
|
new RouteValueDictionary(),
|
||||||
0,
|
0,
|
||||||
EndpointMetadataCollection.Empty,
|
EndpointMetadataCollection.Empty,
|
||||||
null,
|
null));
|
||||||
new Address(routeName)
|
|
||||||
));
|
|
||||||
return CreateUrlHelper(endpoints, appRoot, host, protocol);
|
return CreateUrlHelper(endpoints, appRoot, host, protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,10 +52,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
Endpoint = new MatcherEndpoint(
|
Endpoint = new MatcherEndpoint(
|
||||||
next => cntxt => Task.CompletedTask,
|
next => cntxt => Task.CompletedTask,
|
||||||
"/",
|
"/",
|
||||||
new { },
|
new RouteValueDictionary(),
|
||||||
|
new RouteValueDictionary(),
|
||||||
0,
|
0,
|
||||||
EndpointMetadataCollection.Empty,
|
EndpointMetadataCollection.Empty,
|
||||||
null,
|
|
||||||
null)
|
null)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -79,7 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
string template,
|
string template,
|
||||||
object defaults)
|
object defaults)
|
||||||
{
|
{
|
||||||
var endpoint = GetEndpoint(routeName, template, defaults);
|
var endpoint = GetEndpoint(routeName, template, new RouteValueDictionary(defaults));
|
||||||
var services = CreateServices(new[] { endpoint });
|
var services = CreateServices(new[] { endpoint });
|
||||||
var httpContext = CreateHttpContext(services, appRoot: "", host: null, protocol: null);
|
var httpContext = CreateHttpContext(services, appRoot: "", host: null, protocol: null);
|
||||||
var actionContext = CreateActionContext(httpContext);
|
var actionContext = CreateActionContext(httpContext);
|
||||||
|
|
@ -101,25 +100,36 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
private List<MatcherEndpoint> GetDefaultEndpoints()
|
private List<MatcherEndpoint> GetDefaultEndpoints()
|
||||||
{
|
{
|
||||||
var endpoints = new List<MatcherEndpoint>();
|
var endpoints = new List<MatcherEndpoint>();
|
||||||
endpoints.Add(new MatcherEndpoint(
|
endpoints.Add(CreateEndpoint(null, "home/newaction/{id?}", new { id = "defaultid", controller = "home", action = "newaction" }, 1));
|
||||||
next => (httpContext) => Task.CompletedTask,
|
endpoints.Add(CreateEndpoint(null, "home/contact/{id?}", new { id = "defaultid", controller = "home", action = "contact" }, 2));
|
||||||
"{controller}/{action}/{id}",
|
endpoints.Add(CreateEndpoint(null, "home2/newaction/{id?}", new { id = "defaultid", controller = "home2", action = "newaction" }, 3));
|
||||||
new { id = "defaultid" },
|
endpoints.Add(CreateEndpoint(null, "home2/contact/{id?}", new { id = "defaultid", controller = "home2", action = "contact" }, 4));
|
||||||
0,
|
endpoints.Add(CreateEndpoint(null, "home3/contact/{id?}", new { id = "defaultid", controller = "home3", action = "contact" }, 5));
|
||||||
EndpointMetadataCollection.Empty,
|
endpoints.Add(CreateEndpoint("namedroute", "named/home/newaction/{id?}", new { id = "defaultid", controller = "home", action = "newaction" }, 6));
|
||||||
"RouteWithNoName",
|
endpoints.Add(CreateEndpoint("namedroute", "named/home2/newaction/{id?}", new { id = "defaultid", controller = "home2", action = "newaction" }, 7));
|
||||||
address: null));
|
endpoints.Add(CreateEndpoint("namedroute", "named/home/contact/{id?}", new { id = "defaultid", controller = "home", action = "contact" }, 8));
|
||||||
endpoints.Add(new MatcherEndpoint(
|
endpoints.Add(CreateEndpoint("MyRouteName", "any/url", new { }, 9));
|
||||||
next => (httpContext) => Task.CompletedTask,
|
|
||||||
"named/{controller}/{action}/{id}",
|
|
||||||
new { id = "defaultid" },
|
|
||||||
0,
|
|
||||||
EndpointMetadataCollection.Empty,
|
|
||||||
"RouteWithNoName",
|
|
||||||
new Address("namedroute")));
|
|
||||||
return endpoints;
|
return endpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MatcherEndpoint CreateEndpoint(string routeName, string template, object defaults, int order)
|
||||||
|
{
|
||||||
|
var metadata = EndpointMetadataCollection.Empty;
|
||||||
|
if (!string.IsNullOrEmpty(routeName))
|
||||||
|
{
|
||||||
|
metadata = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MatcherEndpoint(
|
||||||
|
next => (httpContext) => Task.CompletedTask,
|
||||||
|
template,
|
||||||
|
new RouteValueDictionary(defaults),
|
||||||
|
new RouteValueDictionary(),
|
||||||
|
order,
|
||||||
|
metadata,
|
||||||
|
"DisplayName");
|
||||||
|
}
|
||||||
|
|
||||||
private IServiceProvider CreateServices(IEnumerable<Endpoint> endpoints)
|
private IServiceProvider CreateServices(IEnumerable<Endpoint> endpoints)
|
||||||
{
|
{
|
||||||
if (endpoints == null)
|
if (endpoints == null)
|
||||||
|
|
@ -135,16 +145,26 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
return services.BuildServiceProvider();
|
return services.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatcherEndpoint GetEndpoint(string name, string template, object defaults)
|
private MatcherEndpoint GetEndpoint(string name, string template, RouteValueDictionary defaults)
|
||||||
{
|
{
|
||||||
return new MatcherEndpoint(
|
return new MatcherEndpoint(
|
||||||
next => c => Task.CompletedTask,
|
next => c => Task.CompletedTask,
|
||||||
template,
|
template,
|
||||||
defaults,
|
defaults,
|
||||||
|
new RouteValueDictionary(),
|
||||||
0,
|
0,
|
||||||
EndpointMetadataCollection.Empty,
|
EndpointMetadataCollection.Empty,
|
||||||
null,
|
null);
|
||||||
new Address(name));
|
}
|
||||||
|
|
||||||
|
private class RouteNameMetadata : IRouteNameMetadata
|
||||||
|
{
|
||||||
|
public RouteNameMetadata(string routeName)
|
||||||
|
{
|
||||||
|
Name = routeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue