Various improvments to route values link APIs

- Add docs
- Add support for templates
- Remove undesirable constructor parameter
- misc cleanup
This commit is contained in:
Ryan Nowak 2018-09-06 19:18:14 -07:00
parent c683844817
commit 57cc8aea96
4 changed files with 88 additions and 31 deletions

View File

@ -60,8 +60,7 @@ namespace RoutingSample.Web
return new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup(startupType)
.ConfigureLogging(l => l.AddConsole().SetMinimumLevel(LogLevel.Debug));
.UseStartup(startupType);
}
}
}

View File

@ -11,6 +11,19 @@ namespace Microsoft.AspNetCore.Routing
/// </summary>
public static class LinkGeneratorRouteValuesAddressExtensions
{
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
public static string GetPathByRouteValues(
this LinkGenerator generator,
HttpContext httpContext,
@ -28,6 +41,19 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetPathByAddress<RouteValuesAddress>(httpContext, address, address.ExplicitValues, fragment, options);
}
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <param name="pathBase">An optional URI path base. Prepended to the path in the resulting URI.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
public static string GetPathByRouteValues(
this LinkGenerator generator,
string routeName,
@ -45,6 +71,19 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetPathByAddress<RouteValuesAddress>(address, address.ExplicitValues, pathBase, fragment, options);
}
/// <summary>
/// Generates an absolute URI based on the provided values.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
public static string GetUriByRouteValues(
this LinkGenerator generator,
HttpContext httpContext,
@ -62,6 +101,21 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetUriByAddress<RouteValuesAddress>(httpContext, address, address.ExplicitValues, fragment, options);
}
/// <summary>
/// Generates an absolute URI based on the provided values.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <param name="scheme">The URI scheme, applied to the resulting URI.</param>
/// <param name="host">The URI host/authority, applied to the resulting URI.</param>
/// <param name="pathBase">An optional URI path base. Prepended to the path in the resulting URI.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>An absolute URI, or <c>null</c>.</returns>
public static string GetUriByRouteValues(
this LinkGenerator generator,
string routeName,
@ -81,7 +135,29 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetUriByAddress<RouteValuesAddress>(address, address.ExplicitValues, scheme, host, pathBase, fragment, options);
}
/// <summary>
/// Gets a <see cref="LinkGenerationTemplate"/> based on the provided <paramref name="routeName"/> and <paramref name="values"/>.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <returns>
/// A <see cref="LinkGenerationTemplate"/> if one or more endpoints matching the address can be found, otherwise <c>null</c>.
/// </returns>
public static LinkGenerationTemplate GetTemplateByRouteValues(
this LinkGenerator generator,
string routeName,
object values)
{
if (generator == null)
{
throw new ArgumentNullException(nameof(generator));
}
var address = CreateAddress(httpContext: null, routeName, values);
return generator.GetTemplateByAddress<RouteValuesAddress>(address);
}
private static RouteValuesAddress CreateAddress(HttpContext httpContext, string routeName, object values)
{
return new RouteValuesAddress()

View File

@ -6,33 +6,27 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.ObjectPool;
namespace Microsoft.AspNetCore.Routing
{
internal class RouteValuesBasedEndpointFinder : IEndpointFinder<RouteValuesAddress>
{
private readonly CompositeEndpointDataSource _endpointDataSource;
private readonly ObjectPool<UriBuildingContext> _objectPool;
private readonly CompositeEndpointDataSource _dataSource;
private LinkGenerationDecisionTree _allMatchesLinkGenerationTree;
private IDictionary<string, List<OutboundMatchResult>> _namedMatchResults;
public RouteValuesBasedEndpointFinder(
CompositeEndpointDataSource endpointDataSource,
ObjectPool<UriBuildingContext> objectPool)
public RouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource)
{
_endpointDataSource = endpointDataSource;
_objectPool = objectPool;
_dataSource = dataSource;
// Build initial matches
BuildOutboundMatches();
// Register for changes in endpoints
Extensions.Primitives.ChangeToken.OnChange(
_endpointDataSource.GetChangeToken,
_dataSource.GetChangeToken,
HandleChange);
}
@ -68,7 +62,7 @@ namespace Microsoft.AspNetCore.Routing
// re-register the callback as the change token is one time use only and a new change token
// is produced every time
Extensions.Primitives.ChangeToken.OnChange(
_endpointDataSource.GetChangeToken,
_dataSource.GetChangeToken,
HandleChange);
}
@ -104,7 +98,7 @@ namespace Microsoft.AspNetCore.Routing
var namedOutboundMatchResults = new Dictionary<string, List<OutboundMatchResult>>(
StringComparer.OrdinalIgnoreCase);
var endpoints = _endpointDataSource.Endpoints.OfType<RouteEndpoint>();
var endpoints = _dataSource.Endpoints.OfType<RouteEndpoint>();
foreach (var endpoint in endpoints)
{
// Do not consider an endpoint for link generation if the following marker metadata is on it

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.ObjectPool;
using Xunit;
namespace Microsoft.AspNetCore.Routing
@ -83,13 +82,9 @@ namespace Microsoft.AspNetCore.Routing
// Arrange 1
var endpoint1 = CreateEndpoint("/a");
var dynamicDataSource = new DynamicEndpointDataSource(new[] { endpoint1 });
var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPool = objectPoolProvider.Create(new UriBuilderContextPooledObjectPolicy());
// Act 1
var finder = new CustomRouteValuesBasedEndpointFinder(
new CompositeEndpointDataSource(new[] { dynamicDataSource }),
objectPool);
var finder = new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(new[] { dynamicDataSource }));
// Assert 1
Assert.NotNull(finder.AllMatches);
@ -218,14 +213,9 @@ namespace Microsoft.AspNetCore.Routing
return CreateEndpointFinder(new DefaultEndpointDataSource(endpoints));
}
private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params EndpointDataSource[] endpointDataSources)
private CustomRouteValuesBasedEndpointFinder CreateEndpointFinder(params EndpointDataSource[] dataSources)
{
var objectPoolProvider = new DefaultObjectPoolProvider();
var objectPool = objectPoolProvider.Create(new UriBuilderContextPooledObjectPolicy());
return new CustomRouteValuesBasedEndpointFinder(
new CompositeEndpointDataSource(endpointDataSources),
objectPool);
return new CustomRouteValuesBasedEndpointFinder(new CompositeEndpointDataSource(dataSources));
}
private RouteEndpoint CreateEndpoint(
@ -256,10 +246,8 @@ namespace Microsoft.AspNetCore.Routing
private class CustomRouteValuesBasedEndpointFinder : RouteValuesBasedEndpointFinder
{
public CustomRouteValuesBasedEndpointFinder(
CompositeEndpointDataSource endpointDataSource,
ObjectPool<UriBuildingContext> objectPool)
: base(endpointDataSource, objectPool)
public CustomRouteValuesBasedEndpointFinder(CompositeEndpointDataSource dataSource)
: base(dataSource)
{
}