Improve error for RRV substitution case

Fixes: #14789

Users can hit this error case when using a route parameter that has a
special meaning in MVC, in attribute routing with a route constraint.
This will cause us to fail while expanding the route pattern, because
the canonical value associated with the parameter won't satisfy the
constraint.

TLDR: don't do that. Using MVC's reserved parameter names for
application-level concerns will always cause bugs.

This was a case where you'd fail with a totally unactionable error
message. Updating it to reflect the proper root cause and our guidance
to fix it.
This commit is contained in:
Ryan Nowak 2019-11-26 12:49:38 -08:00
parent 84496d3c95
commit f3074d92fd
2 changed files with 24 additions and 1 deletions

View File

@ -132,7 +132,14 @@ namespace Microsoft.AspNetCore.Mvc.Routing
var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);
if (updatedRoutePattern == null)
{
throw new InvalidOperationException("Failed to update route pattern with required values.");
// This kind of thing can happen when a route pattern uses a *reserved* route value such as `action`.
// See: https://github.com/aspnet/AspNetCore/issues/14789
var formattedRouteKeys = string.Join(", ", resolvedRouteValues.Keys.Select(k => $"'{k}'"));
throw new InvalidOperationException(
$"Failed to update the route pattern '{resolvedRoutePattern.RawText}' with required route values. " +
$"This can occur when the route pattern contains parameters with reserved names such as: {formattedRouteKeys} " +
$"and also uses route constraints such as '{{action:int}}'. " +
$"To fix this error, choose a different parmaeter name.");
}
var builder = new RouteEndpointBuilder(_requestDelegate, updatedRoutePattern, action.AttributeRouteInfo.Order)

View File

@ -208,6 +208,22 @@ namespace Microsoft.AspNetCore.Mvc.Routing
Assert.Empty(endpoints);
}
[Fact]
public void AddEndpoints_AttributeRouted_ContainsParameterUsingReservedNameWithConstraint_ExceptionThrown()
{
// Arrange
var values = new { controller = "TestController", action = "TestAction", page = (string)null };
var action = CreateActionDescriptor(values, "Products/{action:int}");
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => CreateAttributeRoutedEndpoint(action));
Assert.Equal(
"Failed to update the route pattern 'Products/{action:int}' with required route values. " +
"This can occur when the route pattern contains parameters with reserved names such as: 'controller', 'action', 'page' and also uses route constraints such as '{action:int}'. " +
"To fix this error, choose a different parmaeter name.",
exception.Message);
}
[Fact]
public void AddEndpoints_AttributeRouted_ContainsParameterWithNullRequiredRouteValue_EndpointCreated()
{