[Fixes #1035] RouteGroupConstraint should only be added once for non attribute routed actions
1. Changed ReflectedActionDescriptorProvider to add RouteGroupConstraint only once for non attribute routed actions. 2. Added tests to cover the scenario.
This commit is contained in:
parent
6f0fa67170
commit
2eec0bbf6d
|
|
@ -289,10 +289,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
actions.Add(actionDescriptor);
|
actions.Add(actionDescriptor);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var actionDescriptor in actions)
|
foreach (var actionDescriptor in actions)
|
||||||
{
|
|
||||||
foreach (var key in removalConstraints)
|
|
||||||
{
|
{
|
||||||
if (actionDescriptor.AttributeRouteInfo == null ||
|
if (actionDescriptor.AttributeRouteInfo == null ||
|
||||||
actionDescriptor.AttributeRouteInfo.Template == null)
|
actionDescriptor.AttributeRouteInfo.Template == null)
|
||||||
|
|
@ -306,6 +305,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
RouteKeyHandling.DenyKey));
|
RouteKeyHandling.DenyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var key in removalConstraints)
|
||||||
|
{
|
||||||
if (!HasConstraint(actionDescriptor.RouteConstraints, key))
|
if (!HasConstraint(actionDescriptor.RouteConstraints, key))
|
||||||
{
|
{
|
||||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
|
||||||
|
|
@ -313,6 +314,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
RouteKeyHandling.DenyKey));
|
RouteKeyHandling.DenyKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We still want to add a 'null' for any constraint with DenyKey so that link generation
|
// We still want to add a 'null' for any constraint with DenyKey so that link generation
|
||||||
|
|
@ -320,6 +322,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
//
|
//
|
||||||
// Consider an action like { area = "", controller = "Home", action = "Index" }. Even if
|
// Consider an action like { area = "", controller = "Home", action = "Index" }. Even if
|
||||||
// it's attribute routed, it needs to know that area must be null to generate a link.
|
// it's attribute routed, it needs to know that area must be null to generate a link.
|
||||||
|
foreach (var key in removalConstraints)
|
||||||
|
{
|
||||||
if (!actionDescriptor.RouteValueDefaults.ContainsKey(key))
|
if (!actionDescriptor.RouteValueDefaults.ContainsKey(key))
|
||||||
{
|
{
|
||||||
actionDescriptor.RouteValueDefaults.Add(key, null);
|
actionDescriptor.RouteValueDefaults.Add(key, null);
|
||||||
|
|
@ -327,7 +331,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (routeTemplateErrors.Any())
|
if (routeTemplateErrors.Any())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -176,13 +176,9 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for Javier's cool bug.
|
|
||||||
if (!results.ContainsKey(constraint.RouteKey))
|
|
||||||
{
|
|
||||||
results.Add(constraint.RouteKey, value);
|
results.Add(constraint.RouteKey, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,71 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
Assert.Equal(expectedMessage, ex.Message);
|
Assert.Equal(expectedMessage, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AttributeRouting_RouteGroupConstraint_IsAddedOnceForNonAttributeRoutes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = GetProvider(
|
||||||
|
typeof(MixedAttributeRouteController).GetTypeInfo(),
|
||||||
|
typeof(ConstrainedController).GetTypeInfo());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actionDescriptors = provider.GetDescriptors();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(actionDescriptors);
|
||||||
|
Assert.Equal(4, actionDescriptors.Count());
|
||||||
|
|
||||||
|
foreach (var actionDescriptor in actionDescriptors.Where(ad => ad.AttributeRouteInfo == null))
|
||||||
|
{
|
||||||
|
Assert.Equal(6, actionDescriptor.RouteConstraints.Count);
|
||||||
|
var routeGroupConstraint = Assert.Single(actionDescriptor.RouteConstraints,
|
||||||
|
rc => rc.RouteKey.Equals(AttributeRouting.RouteGroupKey));
|
||||||
|
Assert.Equal(RouteKeyHandling.DenyKey, routeGroupConstraint.KeyHandling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AttributeRouting_AddsDefaultRouteValues_ForAttributeRoutedActions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = GetProvider(
|
||||||
|
typeof(MixedAttributeRouteController).GetTypeInfo(),
|
||||||
|
typeof(ConstrainedController).GetTypeInfo());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actionDescriptors = provider.GetDescriptors();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(actionDescriptors);
|
||||||
|
Assert.Equal(4, actionDescriptors.Count());
|
||||||
|
|
||||||
|
var indexAction = Assert.Single(actionDescriptors, ad => ad.Name.Equals("Index"));
|
||||||
|
|
||||||
|
Assert.Equal(1, indexAction.RouteConstraints.Count);
|
||||||
|
|
||||||
|
var routeGroupConstraint = Assert.Single(indexAction.RouteConstraints, rc => rc.RouteKey.Equals(AttributeRouting.RouteGroupKey));
|
||||||
|
Assert.Equal(RouteKeyHandling.RequireKey, routeGroupConstraint.KeyHandling);
|
||||||
|
Assert.NotNull(routeGroupConstraint.RouteValue);
|
||||||
|
|
||||||
|
Assert.Equal(5, indexAction.RouteValueDefaults.Count);
|
||||||
|
|
||||||
|
var controllerDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("controller", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Equal("MixedAttributeRoute", controllerDefault.Value);
|
||||||
|
|
||||||
|
var actionDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("action", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Equal("Index", actionDefault.Value);
|
||||||
|
|
||||||
|
var areaDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("area", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Equal("Home", areaDefault.Value);
|
||||||
|
|
||||||
|
var myRouteConstraintDefault = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("key", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Null(myRouteConstraintDefault.Value);
|
||||||
|
|
||||||
|
var anotherRouteConstraint = Assert.Single(indexAction.RouteValueDefaults, rd => rd.Key.Equals("second", StringComparison.OrdinalIgnoreCase));
|
||||||
|
Assert.Null(anotherRouteConstraint.Value);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AttributeRouting_TokenReplacement_CaseInsensitive()
|
public void AttributeRouting_TokenReplacement_CaseInsensitive()
|
||||||
{
|
{
|
||||||
|
|
@ -338,6 +403,26 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReflectedActionDescriptorProvider GetProvider(
|
||||||
|
params TypeInfo[] controllerTypeInfo)
|
||||||
|
{
|
||||||
|
var conventions = new StaticActionDiscoveryConventions(controllerTypeInfo);
|
||||||
|
|
||||||
|
var assemblyProvider = new Mock<IControllerAssemblyProvider>();
|
||||||
|
assemblyProvider
|
||||||
|
.SetupGet(ap => ap.CandidateAssemblies)
|
||||||
|
.Returns(new Assembly[] { controllerTypeInfo.First().Assembly });
|
||||||
|
|
||||||
|
var provider = new ReflectedActionDescriptorProvider(
|
||||||
|
assemblyProvider.Object,
|
||||||
|
conventions,
|
||||||
|
Enumerable.Empty<IFilter>(),
|
||||||
|
new MockMvcOptionsAccessor(),
|
||||||
|
Mock.Of<IInlineConstraintResolver>());
|
||||||
|
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<ActionDescriptor> GetDescriptors(params TypeInfo[] controllerTypeInfos)
|
private IEnumerable<ActionDescriptor> GetDescriptors(params TypeInfo[] controllerTypeInfos)
|
||||||
{
|
{
|
||||||
var conventions = new StaticActionDiscoveryConventions(controllerTypeInfos);
|
var conventions = new StaticActionDiscoveryConventions(controllerTypeInfos);
|
||||||
|
|
@ -386,6 +471,14 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class MySecondRouteConstraintAttribute : RouteConstraintAttribute
|
||||||
|
{
|
||||||
|
public MySecondRouteConstraintAttribute(bool blockNonAttributedActions)
|
||||||
|
: base("second", "value", blockNonAttributedActions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MyRouteConstraintAttribute(blockNonAttributedActions: true)]
|
[MyRouteConstraintAttribute(blockNonAttributedActions: true)]
|
||||||
private class BlockNonAttributedActionsController
|
private class BlockNonAttributedActionsController
|
||||||
{
|
{
|
||||||
|
|
@ -458,5 +551,24 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
[HttpGet("stub/Action1")]
|
[HttpGet("stub/Action1")]
|
||||||
public void Action2() { }
|
public void Action2() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Area("Home")]
|
||||||
|
private class MixedAttributeRouteController
|
||||||
|
{
|
||||||
|
[HttpGet("Index")]
|
||||||
|
public void Index() { }
|
||||||
|
|
||||||
|
[HttpGet("Edit")]
|
||||||
|
public void Edit() { }
|
||||||
|
|
||||||
|
public void AnotherNonAttributedAction() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MyRouteConstraint(blockNonAttributedActions: true)]
|
||||||
|
[MySecondRouteConstraint(blockNonAttributedActions: true)]
|
||||||
|
private class ConstrainedController
|
||||||
|
{
|
||||||
|
public void ConstrainedNonAttributedAction() { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue