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

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