Changed api of LinkGenerator
This commit is contained in:
parent
9cea167cfa
commit
b177ba5309
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue