Fix #710 - add support for data tokens

This commit is contained in:
Ryan Nowak 2018-08-15 21:24:41 -07:00
parent 97f54c532b
commit 4fa0b068b8
5 changed files with 147 additions and 1 deletions

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 System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Metadata that defines data tokens for an <see cref="Endpoint"/>. This metadata
/// type provides data tokens value for <see cref="RouteData.DataTokens"/> associated
/// with an endpoint.
/// </summary>
public sealed class DataTokensMetadata : IDataTokensMetadata
{
public DataTokensMetadata(IReadOnlyDictionary<string, object> dataTokens)
{
if (dataTokens == null)
{
throw new ArgumentNullException(nameof(dataTokens));
}
DataTokens = dataTokens;
}
/// <summary>
/// Get the data tokens.
/// </summary>
public IReadOnlyDictionary<string, object> DataTokens { get; }
}
}

View File

@ -54,7 +54,20 @@ namespace Microsoft.AspNetCore.Routing
{
if (_routeData == null)
{
_routeData = new RouteData(_values);
_routeData = _values == null ? new RouteData() : new RouteData(_values);
// Note: DataTokens won't update if someone else overwrites the Endpoint
// after route values has been set. This seems find since endpoints are a new
// feature and DataTokens are for back-compat.
var dataTokensMetadata = Endpoint?.Metadata.GetMetadata<IDataTokensMetadata>();
if (dataTokensMetadata != null)
{
var dataTokens = _routeData.DataTokens;
foreach (var kvp in dataTokensMetadata.DataTokens)
{
_routeData.DataTokens.Add(kvp.Key, kvp.Value);
}
}
}
return _routeData;

View File

@ -0,0 +1,20 @@
// 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 System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Metadata that defines data tokens for an <see cref="Endpoint"/>. This metadata
/// type provides data tokens value for <see cref="RouteData.DataTokens"/> associated
/// with an endpoint.
/// </summary>
public interface IDataTokensMetadata
{
/// <summary>
/// Get the data tokens.
/// </summary>
IReadOnlyDictionary<string, object> DataTokens { get; }
}
}

View File

@ -0,0 +1,58 @@
// 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 System.Linq;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using Xunit;
namespace Microsoft.AspNetCore.Routing
{
public class EndpointFeatureTest
{
[Fact]
public void RouteData_CanIntializeDataTokens_WithMetadata()
{
// Arrange
var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", });
var feature = new EndpointFeature()
{
Endpoint = new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse("/"),
0,
new EndpointMetadataCollection(new DataTokensMetadata(expected)),
"test"),
};
// Act
var routeData = ((IRoutingFeature)feature).RouteData;
// Assert
Assert.NotSame(expected, routeData.DataTokens);
Assert.Equal(expected.OrderBy(kvp => kvp.Key), routeData.DataTokens.OrderBy(kvp => kvp.Key));
}
[Fact]
public void RouteData_DataTokensIsEmpty_WithoutMetadata()
{
// Arrange
var feature = new EndpointFeature()
{
Endpoint = new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse("/"),
0,
new EndpointMetadataCollection(),
"test"),
};
// Act
var routeData = ((IRoutingFeature)feature).RouteData;
// Assert
Assert.Empty(routeData.DataTokens);
}
}
}

View File

@ -82,6 +82,30 @@ namespace Microsoft.AspNetCore.Routing
Assert.Equal("testValue", endpointFeature.Values["testKey"]);
}
[Fact]
public async Task Invoke_BackCompatGetDataTokens_ValueUsedFromEndpointMetadata()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = new TestServiceProvider();
var middleware = CreateMiddleware();
// Act
await middleware.Invoke(httpContext);
var routeData = httpContext.GetRouteData();
var routeValue = httpContext.GetRouteValue("controller");
var endpointFeature = httpContext.Features.Get<IEndpointFeature>();
// Assert
Assert.NotNull(routeData);
Assert.Equal("Home", (string)routeValue);
// changing route data value is reflected in endpoint feature values
routeData.Values["testKey"] = "testValue";
Assert.Equal("testValue", endpointFeature.Values["testKey"]);
}
private EndpointRoutingMiddleware CreateMiddleware(Logger<EndpointRoutingMiddleware> logger = null)
{
RequestDelegate next = (c) => Task.FromResult<object>(null);