Allow parameter names to match required keys in templates (#872)

This commit is contained in:
James Newton-King 2018-10-22 11:29:41 +13:00 committed by GitHub
parent 2081160678
commit 1d05592cd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 34 deletions

View File

@ -85,6 +85,10 @@ namespace Microsoft.AspNetCore.Routing.Patterns
if (IsCatchAll)
{
builder.Append("*");
if (!EncodeSlashes)
{
builder.Append("*");
}
}
builder.Append(Name);

View File

@ -89,17 +89,6 @@ namespace Microsoft.AspNetCore.Routing.Template
_defaults = defaults;
_requiredKeys = requiredKeys?.ToArray() ?? Array.Empty<string>();
for (var i = 0; i < _requiredKeys.Length; i++)
{
var requiredKey = _requiredKeys[i];
if (_pattern.GetParameter(requiredKey) != null)
{
throw new InvalidOperationException(
$"The parameter {requiredKey} can not be used as a required key since it appears as " +
$"a parameter in the route pattern.");
}
}
// Any default that doesn't have a corresponding parameter is a 'filter' and if a value
// is provided for that 'filter' it must match the value in defaults.
var filters = new RouteValueDictionary(_defaults);

View File

@ -441,9 +441,9 @@ namespace Microsoft.AspNetCore.Routing
// Act
var uri = linkGenerator.GetPathByAddress(
httpContext,
1,
values: new RouteValueDictionary(new { action = "Index", }),
httpContext,
1,
values: new RouteValueDictionary(new { action = "Index", }),
ambientValues: new RouteValueDictionary(new { controller = "Home", }));
// Assert
@ -465,8 +465,8 @@ namespace Microsoft.AspNetCore.Routing
// Act
var uri = linkGenerator.GetUriByAddress(
httpContext,
1,
httpContext,
1,
values: new RouteValueDictionary(new { action = "Index", }),
ambientValues: new RouteValueDictionary(new { controller = "Home", }));
@ -490,7 +490,7 @@ namespace Microsoft.AspNetCore.Routing
var uri = linkGenerator.GetPathByAddress(
httpContext,
1,
values: new RouteValueDictionary(new { action = "Index", controller= "Home", }),
values: new RouteValueDictionary(new { action = "Index", controller = "Home", }),
pathBase: "/");
// Assert
@ -598,8 +598,11 @@ namespace Microsoft.AspNetCore.Routing
Assert.NotSame(original, actual);
}
[Fact]
public void GetPathByRouteValues_UsesFirstTemplateThatSucceeds()
[Theory]
[InlineData(new string[] { }, new string[] { }, "/")]
[InlineData(new string[] { "id" }, new string[] { "3" }, "/Home/Index/3")]
[InlineData(new string[] { "custom" }, new string[] { "Custom" }, "/?custom=Custom")]
public void GetPathByRouteValues_UsesFirstTemplateThatSucceeds(string[] routeNames, string[] routeValues, string expectedPath)
{
// Arrange
var endpointControllerAction = EndpointFactory.CreateRouteEndpoint(
@ -634,26 +637,103 @@ namespace Microsoft.AspNetCore.Routing
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++)
{
values[routeNames[i]] = routeValues[i];
}
// Act
var pathWithoutId = linkGenerator.GetPathByRouteValues(
var generatedPath = linkGenerator.GetPathByRouteValues(
httpContext,
routeName: null,
values: new RouteValueDictionary());
var pathWithId = linkGenerator.GetPathByRouteValues(
httpContext,
routeName: null,
values: new RouteValueDictionary(new { id = "3" }));
var pathWithCustom = linkGenerator.GetPathByRouteValues(
httpContext,
routeName: null,
values: new RouteValueDictionary(new { custom = "Custom" }));
values: values);
// Assert
Assert.Equal("/", pathWithoutId);
Assert.Equal("/Home/Index/3", pathWithId);
Assert.Equal("/?custom=Custom", pathWithCustom);
Assert.Equal(expectedPath, generatedPath);
}
[Theory]
[InlineData(new string[] { }, new string[] { }, "/")]
[InlineData(new string[] { "id" }, new string[] { "3" }, "/Home/Index/3")]
[InlineData(new string[] { "custom" }, new string[] { "Custom" }, "/?custom=Custom")]
[InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Login", "3" }, "/Home/Login/3")]
[InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Fake", "3" }, null)]
public void GetPathByRouteValues_ParameterMatchesRequireValues_HasAmbientValues(string[] routeNames, string[] routeValues, string expectedPath)
{
// Arrange
var homeIndex = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index", },
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) });
var homeLogin = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index", },
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Login", })) });
var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin);
var context = new EndpointSelectorContext()
{
RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", })
};
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++)
{
values[routeNames[i]] = routeValues[i];
}
// Act
var generatedPath = linkGenerator.GetPathByRouteValues(
httpContext,
routeName: null,
values: values);
// Assert
Assert.Equal(expectedPath, generatedPath);
}
[Theory]
[InlineData(new string[] { }, new string[] { }, null)]
[InlineData(new string[] { "id" }, new string[] { "3" }, null)]
[InlineData(new string[] { "custom" }, new string[] { "Custom" }, null)]
[InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Login", "3" }, "/Home/Login/3")]
[InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Fake", "3" }, null)]
public void GetPathByRouteValues_ParameterMatchesRequireValues_NoAmbientValues(string[] routeNames, string[] routeValues, string expectedPath)
{
// Arrange
var homeIndex = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index", },
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) });
var homeLogin = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index", },
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Login", })) });
var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin);
var context = new EndpointSelectorContext();
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++)
{
values[routeNames[i]] = routeValues[i];
}
// Act
var generatedPath = linkGenerator.GetPathByRouteValues(
httpContext,
routeName: null,
values: values);
// Assert
Assert.Equal(expectedPath, generatedPath);
}
protected override void AddAdditionalServices(IServiceCollection services)