Support route data tokens with Endpoint Routing (#8360)

This commit is contained in:
James Newton-King 2018-08-30 15:14:34 +12:00 committed by GitHub
parent 5cdc172b17
commit 927e7c8bfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 42 deletions

View File

@ -174,6 +174,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
endpointInfo.Defaults,
++conventionalRouteOrder,
endpointInfo,
endpointInfo.DataTokens,
suppressLinkGeneration: false,
suppressPathMatching: false);
endpoints.Add(subEndpoint);
@ -214,6 +215,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
endpointInfo.Defaults,
++conventionalRouteOrder,
endpointInfo,
endpointInfo.DataTokens,
suppressLinkGeneration: false,
suppressPathMatching: false);
endpoints.Add(endpoint);
@ -229,6 +231,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
nonInlineDefaults: null,
action.AttributeRouteInfo.Order,
action.AttributeRouteInfo,
dataTokens: null,
suppressLinkGeneration: action.AttributeRouteInfo.SuppressLinkGeneration,
suppressPathMatching: action.AttributeRouteInfo.SuppressPathMatching);
endpoints.Add(endpoint);
@ -378,20 +381,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
object nonInlineDefaults,
int order,
object source,
RouteValueDictionary dataTokens,
bool suppressLinkGeneration,
bool suppressPathMatching)
{
RequestDelegate requestDelegate = (context) =>
{
var values = context.Features.Get<IRouteValuesFeature>().RouteValues;
var routeData = new RouteData();
foreach (var kvp in values)
{
if (kvp.Value != null)
{
routeData.Values.Add(kvp.Key, kvp.Value);
}
}
var routeData = context.GetRouteData();
var actionContext = new ActionContext(context, routeData, action);
@ -407,6 +403,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
routeName,
new RouteValueDictionary(action.RouteValues),
source,
dataTokens,
suppressLinkGeneration,
suppressPathMatching);
@ -425,6 +422,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
string routeName,
RouteValueDictionary requiredValues,
object source,
RouteValueDictionary dataTokens,
bool suppressLinkGeneration,
bool suppressPathMatching)
{
@ -438,6 +436,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
metadata.AddRange(action.EndpointMetadata);
}
if (dataTokens != null)
{
metadata.Add(new DataTokensMetadata(dataTokens));
}
metadata.Add(new RouteValuesAddressMetadata(routeName, requiredValues));
// Add filter descriptors to endpoint metadata
@ -506,7 +509,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
foreach (var kvp in requiredValues)
{
defaults[kvp.Key] = kvp.Value;
if (kvp.Value != null)
{
defaults[kvp.Key] = kvp.Value;
}
}
}
}

View File

@ -89,6 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var featureCollection = new FeatureCollection();
featureCollection.Set<IEndpointFeature>(endpointFeature);
featureCollection.Set<IRouteValuesFeature>(endpointFeature);
featureCollection.Set<IRoutingFeature>(endpointFeature);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(m => m.Features).Returns(featureCollection);
@ -694,28 +695,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
[Fact]
public void RequiredValues_HavingNull_AndNotPresentInDefaultValues_IsAddedToDefaultValues()
{
// Arrange
var requiredValues = new RouteValueDictionary(
new { area = (string)null, controller = "Foo", action = "Bar", page = (string)null });
var expectedDefaults = requiredValues;
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
private MvcEndpointDataSource CreateMvcEndpointDataSource(
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null,
MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null)

View File

@ -72,6 +72,30 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public string[] Routers { get; set; }
}
[Fact]
public async Task DataTokens_ReturnsDataTokensForRoute()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/DataTokensRoute/DataTokens/Index");
// Assert
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(body);
Assert.Single(result, kvp => kvp.Key == "hasDataTokens" && ((bool)kvp.Value) == true);
}
[Fact]
public async Task DataTokens_ReturnsNoDataTokensForRoute()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/DataTokens/Index");
// Assert
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(body);
Assert.Empty(result);
}
[Fact]
public virtual async Task ConventionalRoutedController_ActionIsReachable()
{

View File

@ -0,0 +1,15 @@
// 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
{
public class DataTokensController : Controller
{
public object Index()
{
return RouteData.DataTokens;
}
}
}

View File

@ -24,12 +24,19 @@ namespace RoutingWebSite
{
app.UseMvc(routes =>
{
routes.MapRoute(
"DataTokensRoute",
"DataTokensRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "DataTokens" },
dataTokens: new { hasDataTokens = true });
routes.MapAreaRoute(
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" },
new { area = "Travel" });
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { area = "Travel" });
routes.MapRoute(
"ActionAsMethod",

View File

@ -24,12 +24,19 @@ namespace RoutingWebSite
{
app.UseMvc(routes =>
{
routes.MapRoute(
"DataTokensRoute",
"DataTokensRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "DataTokens" },
dataTokens: new { hasDataTokens = true });
routes.MapAreaRoute(
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" },
new { area = "Travel" });
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { area = "Travel" });
routes.MapRoute(
"ActionAsMethod",