Fix for #819 : Attribute Routing: Resolve Name + Token Replacement

This commit is contained in:
Kirthi Krishnamraju 2015-04-20 22:47:09 -07:00
parent 92554fa634
commit 9d2b1822d9
5 changed files with 210 additions and 0 deletions

View File

@ -544,6 +544,13 @@ namespace Microsoft.AspNet.Mvc
actionDescriptor.AttributeRouteInfo.Template = AttributeRouteModel.ReplaceTokens(
actionDescriptor.AttributeRouteInfo.Template,
actionDescriptor.RouteValueDefaults);
if (actionDescriptor.AttributeRouteInfo.Name != null)
{
actionDescriptor.AttributeRouteInfo.Name = AttributeRouteModel.ReplaceTokens(
actionDescriptor.AttributeRouteInfo.Name,
actionDescriptor.RouteValueDefaults);
}
}
catch (InvalidOperationException ex)
{

View File

@ -864,6 +864,90 @@ namespace Microsoft.AspNet.Mvc.Test
}
}
[Fact]
public void AttributeRouting_RouteNameTokenReplace_AllowsMultipleActions_WithSameRouteNameTemplate()
{
// Arrange
var provider = GetProvider(typeof(ActionRouteNameTemplatesController).GetTypeInfo());
var editActionName = nameof(ActionRouteNameTemplatesController.Edit);
var getActionName = nameof(ActionRouteNameTemplatesController.Get);
// Act
var actions = provider.GetDescriptors();
// Assert
var getActions = actions.Where(a => a.Name.Equals(getActionName));
Assert.Equal(2, getActions.Count());
foreach (var getAction in getActions)
{
Assert.NotNull(getAction.AttributeRouteInfo);
Assert.Equal("Products/Get", getAction.AttributeRouteInfo.Template, StringComparer.OrdinalIgnoreCase);
Assert.Equal("Products_Get", getAction.AttributeRouteInfo.Name, StringComparer.OrdinalIgnoreCase);
}
var editAction = Assert.Single(actions, a => a.Name.Equals(editActionName));
Assert.NotNull(editAction.AttributeRouteInfo);
Assert.Equal("Products/Edit", editAction.AttributeRouteInfo.Template, StringComparer.OrdinalIgnoreCase);
Assert.Equal("Products_Edit", editAction.AttributeRouteInfo.Name, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public void AttributeRouting_RouteNameTokenReplace_AreaControllerActionTokensInRoute()
{
// Arrange
var provider = GetProvider(typeof(ControllerActionRouteNameTemplatesController).GetTypeInfo());
var editActionName = nameof(ControllerActionRouteNameTemplatesController.Edit);
var getActionName = nameof(ControllerActionRouteNameTemplatesController.Get);
// Act
var actions = provider.GetDescriptors();
// Assert
var getActions = actions.Where(a => a.Name.Equals(getActionName));
Assert.Equal(2, getActions.Count());
foreach (var getAction in getActions)
{
Assert.NotNull(getAction.AttributeRouteInfo);
Assert.Equal(
"ControllerActionRouteNameTemplates/Get",
getAction.AttributeRouteInfo.Template, StringComparer.OrdinalIgnoreCase);
Assert.Equal(
"Products_ControllerActionRouteNameTemplates_Get",
getAction.AttributeRouteInfo.Name, StringComparer.OrdinalIgnoreCase);
}
var editAction = Assert.Single(actions, a => a.Name.Equals(editActionName));
Assert.NotNull(editAction.AttributeRouteInfo);
Assert.Equal(
"ControllerActionRouteNameTemplates/Edit",
editAction.AttributeRouteInfo.Template, StringComparer.OrdinalIgnoreCase);
Assert.Equal(
"Products_ControllerActionRouteNameTemplates_Edit",
editAction.AttributeRouteInfo.Name, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public void AttributeRouting_RouteNameTokenReplace_InvalidToken()
{
// Arrange
var provider = GetProvider(typeof(RouteNameIncorrectTokenController).GetTypeInfo());
var expectedMessage =
"The following errors occurred with attribute routing information:" + Environment.NewLine +
Environment.NewLine +
"Error 1:" + Environment.NewLine +
"For action: 'Microsoft.AspNet.Mvc.Test.ControllerActionDescriptorProviderTests+" +
"RouteNameIncorrectTokenController.Get'" + Environment.NewLine +
"Error: While processing template 'Products_[unknown]', a replacement value for the token 'unknown' " +
"could not be found. Available tokens: 'action, controller'.";
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => { provider.GetDescriptors(); });
Assert.Equal(expectedMessage, ex.Message);
}
[Fact]
public void AttributeRouting_RouteGroupConstraint_IsAddedOnceForNonAttributeRoutes()
{
@ -1630,6 +1714,37 @@ namespace Microsoft.AspNet.Mvc.Test
public void PatchItems() { }
}
[Route("Products/[action]", Name = "Products_[action]")]
private class ActionRouteNameTemplatesController
{
[HttpGet]
public void Get() { }
[HttpPost]
public void Get(int id) { }
public void Edit() { }
}
[Area("Products")]
[Route("[controller]/[action]", Name = "[area]_[controller]_[action]")]
private class ControllerActionRouteNameTemplatesController
{
[HttpGet]
public void Get() { }
[HttpPost]
public void Get(int id) { }
public void Edit() { }
}
[Route("Products/[action]", Name = "Products_[unknown]")]
private class RouteNameIncorrectTokenController
{
public void Get() { }
}
private class DifferentCasingsAttributeRouteNamesController
{
[HttpGet("{id}", Name = "Products")]

View File

@ -1373,6 +1373,33 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData("/Order/Add/1", "GET", "Add")]
[InlineData("/Order/Add", "POST", "Add")]
[InlineData("/Order/Edit/1", "PUT", "Edit")]
[InlineData("/Order/GetOrder", "GET", "GetOrder")]
public async Task AttributeRouting_RouteNameTokenReplace_Reachable(string path, string verb, string actionName)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var request = new HttpRequestMessage(new HttpMethod(verb), "http://localhost" + path);
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Contains(path, result.ExpectedUrls);
Assert.Equal("Order", result.Controller);
Assert.Equal(actionName, result.Action);
}
private static LinkBuilder LinkFrom(string url)
{
return new LinkBuilder(url);

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace RoutingWebSite.Areas.Order
{
[Area("Order")]
[Route("Order/[action]", Name = "[area]_[action]")]
public class OrderController : Controller
{
private readonly TestResponseGenerator _generator;
public OrderController(TestResponseGenerator generator)
{
_generator = generator;
}
[HttpGet]
public IActionResult GetOrder()
{
return _generator.Generate("/Order/GetOrder");
}
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace RoutingWebSite.Controllers
{
[Route("Order/[action]/{orderId?}", Name = "Order_[action]")]
public class OrderController : Controller
{
private readonly TestResponseGenerator _generator;
public OrderController(TestResponseGenerator generator)
{
_generator = generator;
}
[HttpGet]
public IActionResult Add(int orderId)
{
return _generator.Generate("/Order/Add/1");
}
[HttpPost]
public IActionResult Add()
{
return _generator.Generate("/Order/Add");
}
[HttpPut]
public IActionResult Edit(int orderId)
{
return _generator.Generate("/Order/Edit/1");
}
}
}