Merge branch 'rel/1.0.1' into dev
This commit is contained in:
commit
b56cab6414
|
|
@ -293,7 +293,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue);
|
||||
}
|
||||
|
||||
|
||||
//TODO: modify comment
|
||||
// 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")]
|
||||
// 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
|
||||
// don't have enough context to really know at this point so we just pass it on.
|
||||
var routeProviders = new List<IRouteTemplateProvider>();
|
||||
|
|
@ -530,18 +540,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (attribute == routeProvider)
|
||||
if (ReferenceEquals(attribute, routeProvider))
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
else if (routeProviders.Contains(attribute))
|
||||
else if (InRouteProviders(routeProviders, attribute))
|
||||
{
|
||||
// Exclude other route template providers
|
||||
// Example:
|
||||
// [HttpGet("template")]
|
||||
// [Route("template/{id}")]
|
||||
}
|
||||
else if (
|
||||
routeProvider is IActionHttpMethodProvider &&
|
||||
attribute is IActionHttpMethodProvider)
|
||||
{
|
||||
// Example:
|
||||
// [HttpGet("template")]
|
||||
// [AcceptVerbs("GET", "POST")]
|
||||
//
|
||||
// Exclude other http method providers if this route is an
|
||||
// http method provider.
|
||||
}
|
||||
|
|
@ -559,7 +576,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (!routeProviders.Contains(attribute))
|
||||
if (!InRouteProviders(routeProviders, attribute))
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
|
|
@ -572,6 +589,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
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)
|
||||
{
|
||||
var selectorModel = new SelectorModel();
|
||||
|
|
|
|||
|
|
@ -792,6 +792,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
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]
|
||||
public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs()
|
||||
{
|
||||
|
|
@ -1124,6 +1147,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
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
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -157,6 +157,66 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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]
|
||||
[InlineData("http://localhost/api/v1/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));
|
||||
}
|
||||
|
||||
public static implicit operator string (LinkBuilder builder)
|
||||
public static implicit operator string(LinkBuilder builder)
|
||||
{
|
||||
return builder.ToString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue