Fix for #819 : Attribute Routing: Resolve Name + Token Replacement
This commit is contained in:
parent
92554fa634
commit
9d2b1822d9
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue