133 lines
4.5 KiB
C#
133 lines
4.5 KiB
C#
// 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.Linq;
|
|
using System.Text.Encodings.Web;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Routing.Internal;
|
|
using Microsoft.AspNetCore.Routing.Matchers;
|
|
using Microsoft.AspNetCore.Routing.Template;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.ObjectPool;
|
|
|
|
namespace Microsoft.AspNetCore.Routing
|
|
{
|
|
internal class DefaultLinkGenerator : LinkGenerator
|
|
{
|
|
private readonly MatchProcessorFactory _matchProcessorFactory;
|
|
private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool;
|
|
private readonly ILogger<DefaultLinkGenerator> _logger;
|
|
|
|
public DefaultLinkGenerator(
|
|
MatchProcessorFactory matchProcessorFactory,
|
|
ObjectPool<UriBuildingContext> uriBuildingContextPool,
|
|
ILogger<DefaultLinkGenerator> logger)
|
|
{
|
|
_matchProcessorFactory = matchProcessorFactory;
|
|
_uriBuildingContextPool = uriBuildingContextPool;
|
|
_logger = logger;
|
|
}
|
|
|
|
public override string GetLink(
|
|
HttpContext httpContext,
|
|
IEnumerable<Endpoint> endpoints,
|
|
RouteValueDictionary explicitValues,
|
|
RouteValueDictionary ambientValues)
|
|
{
|
|
if (TryGetLink(httpContext, endpoints, explicitValues, ambientValues, out var link))
|
|
{
|
|
return link;
|
|
}
|
|
|
|
throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
|
|
}
|
|
|
|
public override bool TryGetLink(
|
|
HttpContext httpContext,
|
|
IEnumerable<Endpoint> endpoints,
|
|
RouteValueDictionary explicitValues,
|
|
RouteValueDictionary ambientValues,
|
|
out string link)
|
|
{
|
|
link = null;
|
|
|
|
if (endpoints == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var matcherEndpoints = endpoints.OfType<MatcherEndpoint>();
|
|
if (!matcherEndpoints.Any())
|
|
{
|
|
//todo:log here
|
|
return false;
|
|
}
|
|
|
|
foreach (var endpoint in matcherEndpoints)
|
|
{
|
|
link = GetLink(httpContext, endpoint, explicitValues, ambientValues);
|
|
if (link != null)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private string GetLink(
|
|
HttpContext httpContext,
|
|
MatcherEndpoint endpoint,
|
|
RouteValueDictionary explicitValues,
|
|
RouteValueDictionary ambientValues)
|
|
{
|
|
var templateBinder = new TemplateBinder(
|
|
UrlEncoder.Default,
|
|
_uriBuildingContextPool,
|
|
new RouteTemplate(endpoint.RoutePattern),
|
|
new RouteValueDictionary(endpoint.RoutePattern.Defaults));
|
|
|
|
var templateValuesResult = templateBinder.GetValues(ambientValues, explicitValues);
|
|
if (templateValuesResult == null)
|
|
{
|
|
// We're missing one of the required values for this route.
|
|
return null;
|
|
}
|
|
|
|
if (!Match(httpContext, endpoint, templateValuesResult.CombinedValues))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return templateBinder.BindValues(templateValuesResult.AcceptedValues);
|
|
}
|
|
|
|
private bool Match(HttpContext httpContext, MatcherEndpoint endpoint, RouteValueDictionary routeValues)
|
|
{
|
|
if (routeValues == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(routeValues));
|
|
}
|
|
|
|
foreach (var kvp in endpoint.RoutePattern.Constraints)
|
|
{
|
|
var parameter = endpoint.RoutePattern.GetParameter(kvp.Key); // may be null, that's ok
|
|
var constraintReferences = kvp.Value;
|
|
for (var i = 0; i < constraintReferences.Count; i++)
|
|
{
|
|
var constraintReference = constraintReferences[i];
|
|
var matchProcessor = _matchProcessorFactory.Create(parameter, constraintReference);
|
|
if (!matchProcessor.ProcessOutbound(httpContext, routeValues))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|