Merge branch 'rel/1.0.1' into dev

This commit is contained in:
jacalvar 2016-08-01 14:16:58 -07:00
commit b56cab6414
4 changed files with 166 additions and 5 deletions

View File

@ -293,7 +293,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{ {
actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue); actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue);
} }
//TODO: modify comment //TODO: modify comment
// Now we need to determine the action selection info (cross-section of routes and constraints) // Now we need to determine the action selection info (cross-section of routes and constraints)
// //
@ -468,6 +468,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// 1. [HttpPost("Api/Things")] // 1. [HttpPost("Api/Things")]
// 2. [HttpGet], [AcceptVerbs("POST", "PUT")] // 2. [HttpGet], [AcceptVerbs("POST", "PUT")]
// //
// Another example of this situation is:
//
// [Route("api/Products")]
// [AcceptVerbs("GET", "HEAD")]
// [HttpPost("api/Products/new")]
//
// This will generate 2 selectors:
// 1. [AcceptVerbs("GET", "HEAD")]
// 2. [HttpPost]
//
// Note that having a route attribute that doesn't define a route template _might_ be an error. We // Note that having a route attribute that doesn't define a route template _might_ be an error. We
// don't have enough context to really know at this point so we just pass it on. // don't have enough context to really know at this point so we just pass it on.
var routeProviders = new List<IRouteTemplateProvider>(); var routeProviders = new List<IRouteTemplateProvider>();
@ -530,18 +540,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var filteredAttributes = new List<object>(); var filteredAttributes = new List<object>();
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
if (attribute == routeProvider) if (ReferenceEquals(attribute, routeProvider))
{ {
filteredAttributes.Add(attribute); filteredAttributes.Add(attribute);
} }
else if (routeProviders.Contains(attribute)) else if (InRouteProviders(routeProviders, attribute))
{ {
// Exclude other route template providers // Exclude other route template providers
// Example:
// [HttpGet("template")]
// [Route("template/{id}")]
} }
else if ( else if (
routeProvider is IActionHttpMethodProvider && routeProvider is IActionHttpMethodProvider &&
attribute is IActionHttpMethodProvider) attribute is IActionHttpMethodProvider)
{ {
// Example:
// [HttpGet("template")]
// [AcceptVerbs("GET", "POST")]
//
// Exclude other http method providers if this route is an // Exclude other http method providers if this route is an
// http method provider. // http method provider.
} }
@ -559,7 +576,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var filteredAttributes = new List<object>(); var filteredAttributes = new List<object>();
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
if (!routeProviders.Contains(attribute)) if (!InRouteProviders(routeProviders, attribute))
{ {
filteredAttributes.Add(attribute); filteredAttributes.Add(attribute);
} }
@ -572,6 +589,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return selectorModels; return selectorModels;
} }
private static bool InRouteProviders(List<IRouteTemplateProvider> routeProviders, object attribute)
{
foreach (var rp in routeProviders)
{
if (ReferenceEquals(rp, attribute))
{
return true;
}
}
return false;
}
private static SelectorModel CreateSelectorModel(IRouteTemplateProvider route, IList<object> attributes) private static SelectorModel CreateSelectorModel(IRouteTemplateProvider route, IList<object> attributes)
{ {
var selectorModel = new SelectorModel(); var selectorModel = new SelectorModel();

View File

@ -792,6 +792,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Equal<string>(new string[] { "POST" }, methodConstraint.HttpMethods); Assert.Equal<string>(new string[] { "POST" }, methodConstraint.HttpMethods);
} }
[Fact]
public void CreateActionModel_MixedHttpVerbsAndRoutes_WithRouteOnController()
{
// Arrange
var builder = new TestApplicationModelProvider();
var typeInfo = typeof(RouteAttributeOnController).GetTypeInfo();
var actionName = nameof(RouteAttributeOnController.Get);
// Act
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
// Assert
Assert.Equal(2, action.Selectors.Count);
var selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel == null);
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
Assert.Equal(new string[] { "GET" }, methodConstraint.HttpMethods);
selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel?.Template == "id/{id?}");
methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
Assert.Equal(new string[] { "GET" }, methodConstraint.HttpMethods);
}
[Fact] [Fact]
public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs() public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs()
{ {
@ -1124,6 +1147,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void Invalid() { } public void Invalid() { }
} }
[Route("api/[controller]")]
private class RouteAttributeOnController : Controller
{
[HttpGet]
[HttpGet("id/{id?}")]
public object Get(short? id)
{
return null;
}
[HttpDelete("{id}")]
public object Delete(int id)
{
return null;
}
}
// Here the constraints on the methods are acting as an IActionHttpMethodProvider and // Here the constraints on the methods are acting as an IActionHttpMethodProvider and
// not as an IRouteTemplateProvider given that there is no RouteAttribute // not as an IRouteTemplateProvider given that there is no RouteAttribute
// on the controller and the template for all the constraints on a method is null. // on the controller and the template for all the constraints on a method is null.

View File

@ -157,6 +157,66 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
result.RouteValues); result.RouteValues);
} }
[Theory]
[InlineData("Get", "/Friends")]
[InlineData("Get", "/Friends/Peter")]
[InlineData("Delete", "/Friends")]
public async Task AttributeRoutedAction_AcceptRequestsWithValidMethods_InRoutesWithoutExtraTemplateSegmentsOnTheAction(
string method,
string url)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(method), $"http://localhost{url}");
// Assert
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(url, result.ExpectedUrls);
Assert.Equal("Friends", result.Controller);
Assert.Equal(method, result.Action);
Assert.Contains(
new KeyValuePair<string, object>("controller", "Friends"),
result.RouteValues);
Assert.Contains(
new KeyValuePair<string, object>("action", method),
result.RouteValues);
if (result.RouteValues.ContainsKey("id"))
{
Assert.Contains(
new KeyValuePair<string, object>("id", "Peter"),
result.RouteValues);
}
}
[Theory]
[InlineData("Post", "/Friends")]
[InlineData("Put", "/Friends")]
[InlineData("Patch", "/Friends")]
[InlineData("Options", "/Friends")]
[InlineData("Head", "/Friends")]
public async Task AttributeRoutedAction_RejectsRequestsWithWrongMethods_InRoutesWithoutExtraTemplateSegmentsOnTheAction(
string method,
string url)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(method), $"http://localhost{url}");
// Assert
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory] [Theory]
[InlineData("http://localhost/api/v1/Maps")] [InlineData("http://localhost/api/v1/Maps")]
[InlineData("http://localhost/api/v2/Maps")] [InlineData("http://localhost/api/v2/Maps")]
@ -1190,7 +1250,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
return Url + "?" + string.Join("&", Values.Select(kvp => kvp.Key + "=" + kvp.Value)); return Url + "?" + string.Join("&", Values.Select(kvp => kvp.Key + "=" + kvp.Value));
} }
public static implicit operator string (LinkBuilder builder) public static implicit operator string(LinkBuilder builder)
{ {
return builder.ToString(); return builder.ToString();
} }

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc;
namespace RoutingWebSite
{
[Route("Friends")]
public class FriendsController : Controller
{
private readonly TestResponseGenerator _generator;
public FriendsController(TestResponseGenerator generator)
{
_generator = generator;
}
[HttpGet]
[HttpGet("{id}")]
public IActionResult Get([FromRoute]string id)
{
return _generator.Generate(id == null ? "/Friends" : $"/Friends/{id}");
}
[HttpDelete]
public IActionResult Delete()
{
return _generator.Generate("/Friends");
}
}
}