Change metadata namespace

Change EndpointOptions visibility to internal
Add IRequiredValuesMetadata
This commit is contained in:
James Newton-King 2018-08-02 01:17:09 -07:00 committed by Kiran Challa
parent c8946a40e4
commit 091cb94094
27 changed files with 133 additions and 73 deletions

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Benchmarks
{
@ -18,9 +19,7 @@ namespace Benchmarks
{
services.AddRouting();
services.Configure<EndpointOptions>(options =>
{
options.DataSources.Add(new DefaultEndpointDataSource(new[]
var endpointDataSource = new DefaultEndpointDataSource(new[]
{
new MatcherEndpoint(
invoker: (next) => (httpContext) =>
@ -33,12 +32,12 @@ namespace Benchmarks
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
},
routePattern: RoutePatternFactory.Parse("/plaintext"),
requiredValues: new RouteValueDictionary(),
order: 0,
metadata: EndpointMetadataCollection.Empty,
displayName: "Plaintext"),
}));
});
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<EndpointDataSource>(endpointDataSource));
}
public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app)

View File

@ -7,7 +7,6 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Metadata;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
@ -44,7 +43,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template),
new RouteValueDictionary(),
0,
new EndpointMetadataCollection(metadata),
template);

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace RoutingSample.Web
{
@ -26,9 +27,7 @@ namespace RoutingSample.Web
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
});
services.Configure<EndpointOptions>(options =>
{
options.DataSources.Add(new DefaultEndpointDataSource(new[]
var endpointDataSource = new DefaultEndpointDataSource(new[]
{
new MatcherEndpoint((next) => (httpContext) =>
{
@ -40,7 +39,6 @@ namespace RoutingSample.Web
return response.Body.WriteAsync(_homePayload, 0, payloadLength);
},
RoutePatternFactory.Parse("/"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"Home"),
@ -54,7 +52,6 @@ namespace RoutingSample.Web
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
},
RoutePatternFactory.Parse("/plaintext"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"Plaintext"),
@ -66,7 +63,6 @@ namespace RoutingSample.Web
return response.WriteAsync("WithConstraints");
},
RoutePatternFactory.Parse("/withconstraints/{id:endsWith(_001)}"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"withconstraints"),
@ -78,12 +74,12 @@ namespace RoutingSample.Web
return response.WriteAsync("withoptionalconstraints");
},
RoutePatternFactory.Parse("/withoptionalconstraints/{id:endsWith(_001)?}"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"withoptionalconstraints"),
}));
});
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<EndpointDataSource>(endpointDataSource));
}
public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app)

View File

@ -8,7 +8,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Metadata;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Routing
@ -127,15 +126,18 @@ namespace Microsoft.AspNetCore.Routing
var template = matcherEndpoint.RoutePattern.RawText;
template = string.IsNullOrEmpty(template) ? "\"\"" : template;
sb.Append(template);
sb.Append(", Required Values: new { ");
sb.Append(string.Join(", ", FormatValues(matcherEndpoint.RequiredValues)));
sb.Append(" }");
sb.Append(", Defaults: new { ");
sb.Append(string.Join(", ", FormatValues(matcherEndpoint.RoutePattern.Defaults)));
sb.Append(" }");
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
sb.Append(", Route Name: ");
sb.Append(routeNameMetadata?.Name);
sb.Append(routeValuesAddressMetadata?.Name);
if (routeValuesAddressMetadata?.RequiredValues != null)
{
sb.Append(", Required Values: new { ");
sb.Append(string.Join(", ", FormatValues(routeValuesAddressMetadata.RequiredValues)));
sb.Append(" }");
}
sb.Append(", Order: ");
sb.Append(matcherEndpoint.Order);

View File

@ -5,7 +5,8 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing
{
public class EndpointOptions
// Internal for 2.2. Public API for configuring endpoints will be added in 3.0
internal class EndpointOptions
{
public IList<EndpointDataSource> DataSources { get; } = new List<EndpointDataSource>();
}

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Microsoft.AspNetCore.Routing.Metadata
namespace Microsoft.AspNetCore.Routing
{
[DebuggerDisplay("{DebuggerToString(),nq}")]
public sealed class HttpMethodMetadata : IHttpMethodMetadata

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing.Metadata
namespace Microsoft.AspNetCore.Routing
{
public interface IHttpMethodMetadata
{

View File

@ -0,0 +1,13 @@
// 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
{
public interface IRouteValuesAddressMetadata
{
string Name { get; }
IReadOnlyDictionary<string, object> RequiredValues { get; }
}
}

View File

@ -6,4 +6,4 @@ namespace Microsoft.AspNetCore.Routing
public interface ISuppressLinkGenerationMetadata
{
}
}
}

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Metadata;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Primitives;
@ -232,7 +231,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return Task.CompletedTask;
},
RoutePatternFactory.Parse("/"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
Http405EndpointDisplayName);

View File

@ -19,7 +19,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
public MatcherEndpoint(
Func<RequestDelegate, RequestDelegate> invoker,
RoutePattern routePattern,
RouteValueDictionary requiredValues,
int order,
EndpointMetadataCollection metadata,
string displayName)
@ -37,7 +36,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
Invoker = invoker;
RoutePattern = routePattern;
RequiredValues = requiredValues;
Order = order;
}
@ -45,9 +43,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
public int Order { get; }
// Values required by an endpoint for it to be successfully matched on link generation
public IReadOnlyDictionary<string, object> RequiredValues { get; }
public RoutePattern RoutePattern { get; }
}
}

View File

@ -0,0 +1,48 @@
// 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;
using System.Diagnostics;
using System.Linq;
namespace Microsoft.AspNetCore.Routing
{
[DebuggerDisplay("{DebuggerToString(),nq}")]
public sealed class RouteValuesAddressMetadata : IRouteValuesAddressMetadata
{
public RouteValuesAddressMetadata(string name, IReadOnlyDictionary<string, object> requiredValues)
{
Name = name;
RequiredValues = requiredValues;
}
public string Name { get; }
public IReadOnlyDictionary<string, object> RequiredValues { get; }
internal string DebuggerToString()
{
return $"Name: {Name} - Required values: {string.Join(", ", FormatValues(RequiredValues))}";
IEnumerable<string> FormatValues(IEnumerable<KeyValuePair<string, object>> values)
{
if (values == null)
{
return Array.Empty<string>();
}
return values.Select(
kvp =>
{
var value = "null";
if (kvp.Value != null)
{
value = "\"" + kvp.Value.ToString() + "\"";
}
return kvp.Key + " = " + value;
});
}
}
}
}

View File

@ -137,16 +137,16 @@ namespace Microsoft.AspNetCore.Routing
private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint)
{
var routeNameMetadata = endpoint.Metadata.GetMetadata<IRouteNameMetadata>();
var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
var entry = new OutboundRouteEntry()
{
Handler = NullRouter.Instance,
Order = endpoint.Order,
Precedence = RoutePrecedence.ComputeOutbound(endpoint.RoutePattern),
RequiredLinkValues = new RouteValueDictionary(endpoint.RequiredValues),
RequiredLinkValues = new RouteValueDictionary(routeValuesAddressMetadata?.RequiredValues),
RouteTemplate = new RouteTemplate(endpoint.RoutePattern),
Data = endpoint,
RouteName = routeNameMetadata?.Name,
RouteName = routeValuesAddressMetadata?.Name,
};
entry.Defaults = new RouteValueDictionary(endpoint.RoutePattern.Defaults);
return entry;

View File

@ -3,8 +3,7 @@
namespace Microsoft.AspNetCore.Routing
{
public interface IRouteNameMetadata
public sealed class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata
{
string Name { get; }
}
}
}

View File

@ -149,14 +149,12 @@ namespace Microsoft.AspNetCore.Routing
private MatcherEndpoint CreateEndpoint(
string template,
object defaults = null,
object requiredValues = null,
int order = 0,
string routeName = null)
{
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
new RouteValueDictionary(requiredValues),
order,
EndpointMetadataCollection.Empty,
null);

View File

@ -3,6 +3,8 @@
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing
{
@ -17,18 +19,17 @@ namespace Microsoft.AspNetCore.Routing
string displayName = null,
params object[] metadata)
{
var metadataCollection = EndpointMetadataCollection.Empty;
if (metadata != null)
var d = new List<object>(metadata ?? Array.Empty<object>());
if (requiredValues != null)
{
metadataCollection = new EndpointMetadataCollection(metadata);
d.Add(new RouteValuesAddressMetadata(null, new RouteValueDictionary(requiredValues)));
}
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints),
new RouteValueDictionary(requiredValues),
order,
metadataCollection,
new EndpointMetadataCollection(d),
displayName);
}
}

View File

@ -85,7 +85,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"test");

View File

@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
var endpoint = new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse("a/b/c"),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
"test");

View File

@ -216,7 +216,6 @@ test: /test3", ex.Message);
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template),
new RouteValueDictionary(),
0,
EndpointMetadataCollection.Empty,
$"test: {template}");

View File

@ -860,7 +860,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints)),
new RouteValueDictionary(),
0,
new EndpointMetadataCollection(metadata),
"test");

View File

@ -20,7 +20,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
new RouteValueDictionary(),
order,
metadata ?? EndpointMetadataCollection.Empty,
template);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Metadata;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@ -343,7 +342,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints),
new RouteValueDictionary(),
order,
new EndpointMetadataCollection(metadata),
displayName);

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Routing.Metadata;
using Microsoft.AspNetCore.Routing.Patterns;
using Xunit;
using static Microsoft.AspNetCore.Routing.Matching.HttpMethodMatcherPolicy;
@ -288,7 +287,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template),
new RouteValueDictionary(),
0,
new EndpointMetadataCollection(metadata),
$"test: {template}");

View File

@ -43,7 +43,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints),
new RouteValueDictionary(),
order ?? 0,
EndpointMetadataCollection.Empty,
"endpoint: " + template);

View File

@ -222,7 +222,6 @@ namespace Microsoft.AspNetCore.Routing.Matching
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template),
new RouteValueDictionary(),
order,
new EndpointMetadataCollection(metadata),
"test: " + template);

View File

@ -263,37 +263,29 @@ namespace Microsoft.AspNetCore.Routing
{
if (metadataCollection == null)
{
metadataCollection = EndpointMetadataCollection.Empty;
if (!string.IsNullOrEmpty(routeName))
var metadata = new List<object>();
if (!string.IsNullOrEmpty(routeName) || requiredValues != null)
{
metadataCollection = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
metadata.Add(new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues)));
}
metadataCollection = new EndpointMetadataCollection(metadata);
}
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
RoutePatternFactory.Parse(template, defaults, constraints: null),
new RouteValueDictionary(requiredValues),
order,
metadataCollection,
null);
}
private class RouteNameMetadata : IRouteNameMetadata
{
public RouteNameMetadata(string name)
{
Name = name;
}
public string Name { get; }
}
private class NameMetadata : INameMetadata
{
public NameMetadata(string name)
{
Name = name;
}
public string Name { get; }
}
@ -318,7 +310,5 @@ namespace Microsoft.AspNetCore.Routing
return matches;
}
}
private class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata { }
}
}

View File

@ -0,0 +1,33 @@
// 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;
using System.Text;
using Xunit;
namespace Microsoft.AspNetCore.Routing
{
public class RouteValuesAddressMetadataTests
{
[Fact]
public void DebuggerToString_NoNameAndRequiredValues_ReturnsString()
{
var metadata = new RouteValuesAddressMetadata(null, null);
Assert.Equal("Name: - Required values: ", metadata.DebuggerToString());
}
[Fact]
public void DebuggerToString_HasNameAndRequiredValues_ReturnsString()
{
var metadata = new RouteValuesAddressMetadata("Name!", new Dictionary<string, object>
{
["requiredValue1"] = "One",
["requiredValue2"] = 2,
});
Assert.Equal("Name: Name! - Required values: requiredValue1 = \"One\", requiredValue2 = \"2\"", metadata.DebuggerToString());
}
}
}