Changed api of LinkGenerator

This commit is contained in:
Kiran Challa 2018-07-30 15:44:37 -07:00 committed by Kiran Challa
parent 9cea167cfa
commit b177ba5309
5 changed files with 601 additions and 620 deletions

View File

@ -25,11 +25,11 @@ namespace RoutingSample.Web
{
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
});
services.Configure<EndpointOptions>(options =>
{
options.DataSources.Add(new DefaultEndpointDataSource(new[]
{
options.DataSources.Add(new DefaultEndpointDataSource(new[]
{
new MatcherEndpoint((next) => (httpContext) =>
{
var response = httpContext.Response;
@ -83,7 +83,7 @@ namespace RoutingSample.Web
EndpointMetadataCollection.Empty,
"withoptionalconstraints"),
}));
});
});
}
public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app)

View File

@ -1,13 +1,157 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing
{
public abstract class LinkGenerator
{
public abstract bool TryGetLink(LinkGeneratorContext context, out string link);
public string GetLink(object values)
{
return GetLink(httpContext: null, routeName: null, values, options: null);
}
public abstract string GetLink(LinkGeneratorContext context);
public string GetLink(object values, LinkOptions options)
{
return GetLink(httpContext: null, routeName: null, values, options);
}
public bool TryGetLink(object values, out string link)
{
return TryGetLink(httpContext: null, routeName: null, values, options: null, out link);
}
public bool TryGetLink(object values, LinkOptions options, out string link)
{
return TryGetLink(httpContext: null, routeName: null, values, options, out link);
}
public string GetLink(HttpContext httpContext, object values)
{
return GetLink(httpContext, routeName: null, values, options: null);
}
public bool TryGetLink(HttpContext httpContext, object values, out string link)
{
return TryGetLink(httpContext, routeName: null, values, options: null, out link);
}
public string GetLink(HttpContext httpContext, object values, LinkOptions options)
{
return GetLink(httpContext, routeName: null, values, options);
}
public bool TryGetLink(HttpContext httpContext, object values, LinkOptions options, out string link)
{
return TryGetLink(httpContext, routeName: null, values, options, out link);
}
public string GetLink(string routeName, object values)
{
return GetLink(httpContext: null, routeName, values, options: null);
}
public bool TryGetLink(string routeName, object values, out string link)
{
return TryGetLink(httpContext: null, routeName, values, options: null, out link);
}
public string GetLink(string routeName, object values, LinkOptions options)
{
return GetLink(httpContext: null, routeName, values, options);
}
public bool TryGetLink(string routeName, object values, LinkOptions options, out string link)
{
return TryGetLink(httpContext: null, routeName, values, options, out link);
}
public string GetLink(HttpContext httpContext, string routeName, object values)
{
return GetLink(httpContext, routeName, values, options: null);
}
public bool TryGetLink(HttpContext httpContext, string routeName, object values, out string link)
{
return TryGetLink(httpContext, routeName, values, options: null, out link);
}
public string GetLink(HttpContext httpContext, string routeName, object values, LinkOptions options)
{
if (TryGetLink(httpContext, routeName, values, options, out var link))
{
return link;
}
throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
}
public abstract bool TryGetLink(
HttpContext httpContext,
string routeName,
object values,
LinkOptions options,
out string link);
public string GetLinkByAddress<TAddress>(TAddress address, object values)
{
return GetLinkByAddress(address, httpContext: null, values, options: null);
}
public bool TryGetLinkByAddress<TAddress>(TAddress address, object values, out string link)
{
return TryGetLinkByAddress(address, values, options: null, out link);
}
public string GetLinkByAddress<TAddress>(TAddress address, object values, LinkOptions options)
{
return GetLinkByAddress(address, httpContext: null, values, options);
}
public bool TryGetLinkByAddress<TAddress>(
TAddress address,
object values,
LinkOptions options,
out string link)
{
return TryGetLinkByAddress(address, httpContext: null, values, options, out link);
}
public string GetLinkByAddress<TAddress>(TAddress address, HttpContext httpContext, object values)
{
return GetLinkByAddress(address, httpContext, values, options: null);
}
public bool TryGetLinkByAddress<TAddress>(
TAddress address,
HttpContext httpContext,
object values,
out string link)
{
return TryGetLinkByAddress(address, httpContext, values, options: null, out link);
}
public string GetLinkByAddress<TAddress>(
TAddress address,
HttpContext httpContext,
object values,
LinkOptions options)
{
if (TryGetLinkByAddress(address, httpContext, values, options, out var link))
{
return link;
}
throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
}
public abstract bool TryGetLinkByAddress<TAddress>(
TAddress address,
HttpContext httpContext,
object values,
LinkOptions options,
out string link);
}
}

View File

@ -1,21 +1,10 @@
// 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;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing
{
public class LinkGeneratorContext
public class LinkOptions
{
public HttpContext HttpContext { get; set; }
public IEnumerable<Endpoint> Endpoints { get; set; }
public RouteValueDictionary ExplicitValues { get; set; }
public RouteValueDictionary AmbientValues { get; set; }
/// <summary>
/// Gets or sets a value indicating whether all generated paths URLs are lower-case.
/// Use <see cref="LowercaseQueryStrings" /> to configure the behavior for query strings.

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
@ -19,60 +20,106 @@ namespace Microsoft.AspNetCore.Routing
private readonly static char[] UrlQueryDelimiters = new char[] { '?', '#' };
private readonly MatchProcessorFactory _matchProcessorFactory;
private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool;
private readonly RouteOptions _options;
private readonly ILogger<DefaultLinkGenerator> _logger;
private readonly IServiceProvider _serviceProvider;
private readonly RouteOptions _options;
public DefaultLinkGenerator(
MatchProcessorFactory matchProcessorFactory,
ObjectPool<UriBuildingContext> uriBuildingContextPool,
IOptions<RouteOptions> routeOptions,
ILogger<DefaultLinkGenerator> logger)
ILogger<DefaultLinkGenerator> logger,
IServiceProvider serviceProvider)
{
_matchProcessorFactory = matchProcessorFactory;
_uriBuildingContextPool = uriBuildingContextPool;
_options = routeOptions.Value;
_logger = logger;
_serviceProvider = serviceProvider;
}
public override string GetLink(LinkGeneratorContext context)
public override bool TryGetLink(
HttpContext httpContext,
string routeName,
object values,
LinkOptions options,
out string link)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (TryGetLink(context, out var link))
{
return link;
}
throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
return TryGetLinkByRouteValues(
httpContext,
routeName,
values,
options,
out link);
}
public override bool TryGetLink(LinkGeneratorContext context, out string link)
public override bool TryGetLinkByAddress<TAddress>(
TAddress address,
HttpContext httpContext,
object values,
LinkOptions options,
out string link)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return TryGetLinkByAddressInternal(
address,
httpContext,
explicitValues: values,
ambientValues: GetAmbientValues(httpContext),
options,
out link);
}
private bool TryGetLinkByRouteValues(
HttpContext httpContext,
string routeName,
object values,
LinkOptions options,
out string link)
{
var ambientValues = GetAmbientValues(httpContext);
var address = new RouteValuesAddress
{
RouteName = routeName,
ExplicitValues = new RouteValueDictionary(values),
AmbientValues = ambientValues
};
return TryGetLinkByAddressInternal(
address,
httpContext,
explicitValues: values,
ambientValues: ambientValues,
options,
out link);
}
private bool TryGetLinkByAddressInternal<TAddress>(
TAddress address,
HttpContext httpContext,
object explicitValues,
RouteValueDictionary ambientValues,
LinkOptions options,
out string link)
{
link = null;
if (context.Endpoints == null)
var endpointFinder = _serviceProvider.GetRequiredService<IEndpointFinder<TAddress>>();
var endpoints = endpointFinder.FindEndpoints(address);
if (endpoints == null)
{
return false;
}
var matcherEndpoints = context.Endpoints.OfType<MatcherEndpoint>();
var matcherEndpoints = endpoints.OfType<MatcherEndpoint>();
if (!matcherEndpoints.Any())
{
//todo:log here
return false;
}
foreach (var endpoint in matcherEndpoints)
{
link = GetLink(endpoint, context);
link = GetLink(endpoint);
if (link != null)
{
return true;
@ -80,34 +127,33 @@ namespace Microsoft.AspNetCore.Routing
}
return false;
}
private string GetLink(MatcherEndpoint endpoint, LinkGeneratorContext context)
{
var templateBinder = new TemplateBinder(
UrlEncoder.Default,
_uriBuildingContextPool,
new RouteTemplate(endpoint.RoutePattern),
new RouteValueDictionary(endpoint.RoutePattern.Defaults));
var templateValuesResult = templateBinder.GetValues(
ambientValues: context.AmbientValues,
explicitValues: context.ExplicitValues,
endpoint.RequiredValues.Keys);
if (templateValuesResult == null)
string GetLink(MatcherEndpoint endpoint)
{
// We're missing one of the required values for this route.
return null;
}
var templateBinder = new TemplateBinder(
UrlEncoder.Default,
_uriBuildingContextPool,
new RouteTemplate(endpoint.RoutePattern),
new RouteValueDictionary(endpoint.RoutePattern.Defaults));
if (!MatchesConstraints(context.HttpContext, endpoint, templateValuesResult.CombinedValues))
{
return null;
}
var templateValuesResult = templateBinder.GetValues(
ambientValues: ambientValues,
explicitValues: new RouteValueDictionary(explicitValues),
requiredKeys: endpoint.RequiredValues.Keys);
if (templateValuesResult == null)
{
// We're missing one of the required values for this route.
return null;
}
var url = templateBinder.BindValues(templateValuesResult.AcceptedValues);
return Normalize(context, url);
if (!MatchesConstraints(httpContext, endpoint, templateValuesResult.CombinedValues))
{
return null;
}
var url = templateBinder.BindValues(templateValuesResult.AcceptedValues);
return Normalize(url, options);
}
}
private bool MatchesConstraints(
@ -138,13 +184,11 @@ namespace Microsoft.AspNetCore.Routing
return true;
}
private string Normalize(LinkGeneratorContext context, string url)
private string Normalize(string url, LinkOptions options)
{
var lowercaseUrls = context.LowercaseUrls.HasValue ? context.LowercaseUrls.Value : _options.LowercaseUrls;
var lowercaseQueryStrings = context.LowercaseQueryStrings.HasValue ?
context.LowercaseQueryStrings.Value : _options.LowercaseQueryStrings;
var appendTrailingSlash = context.AppendTrailingSlash.HasValue ?
context.AppendTrailingSlash.Value : _options.AppendTrailingSlash;
var lowercaseUrls = options?.LowercaseUrls ?? _options.LowercaseUrls;
var lowercaseQueryStrings = options?.LowercaseQueryStrings ?? _options.LowercaseQueryStrings;
var appendTrailingSlash = options?.AppendTrailingSlash ?? _options.AppendTrailingSlash;
if (!string.IsNullOrEmpty(url) && (lowercaseUrls || appendTrailingSlash))
{
@ -179,5 +223,18 @@ namespace Microsoft.AspNetCore.Routing
return url;
}
private RouteValueDictionary GetAmbientValues(HttpContext httpContext)
{
if (httpContext != null)
{
var feature = httpContext.Features.Get<IEndpointFeature>();
if (feature != null)
{
return feature.Values;
}
}
return null;
}
}
}