Added initial support for link generation when using Dispatcher
This commit is contained in:
parent
7423bb8702
commit
d3ddc1709a
|
|
@ -35,7 +35,8 @@ namespace Benchmarks
|
|||
values: new { },
|
||||
order: 0,
|
||||
metadata: EndpointMetadataCollection.Empty,
|
||||
displayName: "Plaintext"),
|
||||
displayName: "Plaintext",
|
||||
address: null),
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
new { },
|
||||
0,
|
||||
EndpointMetadataCollection.Empty,
|
||||
template);
|
||||
template,
|
||||
address: null);
|
||||
}
|
||||
|
||||
internal static int[] SampleRequests(int endpointCount, int count)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace DispatcherSample.Web
|
|||
response.ContentLength = payloadLength;
|
||||
return response.Body.WriteAsync(_homePayload, 0, payloadLength);
|
||||
},
|
||||
"/", new { }, 0, EndpointMetadataCollection.Empty, "Home"),
|
||||
"/", new { }, 0, EndpointMetadataCollection.Empty, "Home", address: null),
|
||||
new MatcherEndpoint((next) => (httpContext) =>
|
||||
{
|
||||
var response = httpContext.Response;
|
||||
|
|
@ -41,7 +41,7 @@ namespace DispatcherSample.Web
|
|||
response.ContentLength = payloadLength;
|
||||
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
|
||||
},
|
||||
"/plaintext", new { }, 0, EndpointMetadataCollection.Empty, "Plaintext"),
|
||||
"/plaintext", new { }, 0, EndpointMetadataCollection.Empty, "Plaintext", address: null),
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class Address
|
||||
{
|
||||
public Address()
|
||||
{
|
||||
}
|
||||
|
||||
public Address(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,21 @@ namespace Microsoft.AspNetCore.Routing
|
|||
[DebuggerDisplay("{DisplayName,nq}")]
|
||||
public abstract class Endpoint
|
||||
{
|
||||
protected Endpoint(EndpointMetadataCollection metadata, string displayName)
|
||||
protected Endpoint(
|
||||
EndpointMetadataCollection metadata,
|
||||
string displayName,
|
||||
Address address)
|
||||
{
|
||||
// Both allowed to be null
|
||||
// All are allowed to be null
|
||||
Metadata = metadata ?? EndpointMetadataCollection.Empty;
|
||||
DisplayName = displayName;
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public EndpointMetadataCollection Metadata { get; }
|
||||
|
||||
public Address Address { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public interface IEndpointFinder
|
||||
{
|
||||
IEnumerable<Endpoint> FindEndpoints(Address address);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public interface ILinkGenerator
|
||||
{
|
||||
bool TryGetLink(LinkGeneratorContext context, out string link);
|
||||
|
||||
string GetLink(LinkGeneratorContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class LinkGeneratorContext
|
||||
{
|
||||
public Address Address { get; set; }
|
||||
|
||||
public RouteValueDictionary AmbientValues { get; set; }
|
||||
|
||||
public RouteValueDictionary SuppliedValues { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// 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 Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
internal class DefaultEndpointFinder : IEndpointFinder
|
||||
{
|
||||
private readonly CompositeEndpointDataSource _endpointDatasource;
|
||||
private readonly ILogger<DefaultEndpointFinder> _logger;
|
||||
|
||||
public DefaultEndpointFinder(
|
||||
CompositeEndpointDataSource endpointDataSource,
|
||||
ILogger<DefaultEndpointFinder> logger)
|
||||
{
|
||||
_endpointDatasource = endpointDataSource;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IEnumerable<Endpoint> FindEndpoints(Address lookupAddress)
|
||||
{
|
||||
var allEndpoints = _endpointDatasource.Endpoints;
|
||||
|
||||
if (lookupAddress == null || string.IsNullOrEmpty(lookupAddress.Name))
|
||||
{
|
||||
return allEndpoints;
|
||||
}
|
||||
|
||||
var endpointsWithAddress = allEndpoints.Where(ep => ep.Address != null);
|
||||
if (!endpointsWithAddress.Any())
|
||||
{
|
||||
return allEndpoints;
|
||||
}
|
||||
|
||||
foreach (var endpoint in endpointsWithAddress)
|
||||
{
|
||||
if (string.Equals(lookupAddress.Name, endpoint.Address.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new[] { endpoint };
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
$"Could not find an endpoint having an address with name '{lookupAddress.Name}'.");
|
||||
|
||||
return Enumerable.Empty<Endpoint>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// 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.Routing.Internal;
|
||||
using Microsoft.AspNetCore.Routing.Matchers;
|
||||
using Microsoft.AspNetCore.Routing.Template;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class DefaultLinkGenerator : ILinkGenerator
|
||||
{
|
||||
private readonly IEndpointFinder _endpointFinder;
|
||||
private readonly ObjectPool<UriBuildingContext> _uriBuildingContextPool;
|
||||
private readonly ILogger<DefaultLinkGenerator> _logger;
|
||||
|
||||
public DefaultLinkGenerator(
|
||||
IEndpointFinder endpointFinder,
|
||||
ObjectPool<UriBuildingContext> uriBuildingContextPool,
|
||||
ILogger<DefaultLinkGenerator> logger)
|
||||
{
|
||||
_endpointFinder = endpointFinder;
|
||||
_uriBuildingContextPool = uriBuildingContextPool;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetLink(LinkGeneratorContext context)
|
||||
{
|
||||
if (TryGetLink(context, out var link))
|
||||
{
|
||||
return link;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
|
||||
}
|
||||
|
||||
public bool TryGetLink(LinkGeneratorContext context, out string link)
|
||||
{
|
||||
var address = context.Address;
|
||||
var endpoints = _endpointFinder.FindEndpoints(address);
|
||||
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(endpoint.ParsedTemlate, endpoint.Values, context);
|
||||
if (link != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetLink(
|
||||
RouteTemplate template,
|
||||
IReadOnlyDictionary<string, object> defaultValues,
|
||||
LinkGeneratorContext context)
|
||||
{
|
||||
var defaults = new RouteValueDictionary(defaultValues);
|
||||
var templateBinder = new TemplateBinder(
|
||||
UrlEncoder.Default,
|
||||
_uriBuildingContextPool,
|
||||
template,
|
||||
defaults);
|
||||
|
||||
var values = templateBinder.GetValues(
|
||||
new RouteValueDictionary(context.AmbientValues),
|
||||
new RouteValueDictionary(context.SuppliedValues));
|
||||
if (values == null)
|
||||
{
|
||||
// We're missing one of the required values for this route.
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO: route constraint matching here
|
||||
|
||||
return templateBinder.BindValues(values.AcceptedValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
//
|
||||
services.TryAddSingleton<MatcherFactory, TreeMatcherFactory>();
|
||||
|
||||
// Link generation related services
|
||||
services.TryAddSingleton<IEndpointFinder, DefaultEndpointFinder>();
|
||||
services.TryAddSingleton<ILinkGenerator, DefaultLinkGenerator>();
|
||||
//
|
||||
// Endpoint Selection
|
||||
//
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Template;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matchers
|
||||
{
|
||||
|
|
@ -21,8 +22,9 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
object values,
|
||||
int order,
|
||||
EndpointMetadataCollection metadata,
|
||||
string displayName)
|
||||
: base(metadata, displayName)
|
||||
string displayName,
|
||||
Address address)
|
||||
: base(metadata, displayName, address)
|
||||
{
|
||||
if (invoker == null)
|
||||
{
|
||||
|
|
@ -36,14 +38,42 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
|
||||
Invoker = invoker;
|
||||
Template = template;
|
||||
Values = new RouteValueDictionary(values);
|
||||
ParsedTemlate = TemplateParser.Parse(template);
|
||||
var mergedDefaults = GetDefaults(ParsedTemlate, new RouteValueDictionary(values));
|
||||
Values = mergedDefaults;
|
||||
Order = order;
|
||||
}
|
||||
|
||||
public int Order { get; }
|
||||
public Func<RequestDelegate, RequestDelegate> Invoker { get; }
|
||||
public string Template { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, object> Values { get; }
|
||||
|
||||
// Todo: needs review
|
||||
public RouteTemplate ParsedTemlate { get; }
|
||||
|
||||
private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate, RouteValueDictionary defaults)
|
||||
{
|
||||
var result = defaults == null ? new RouteValueDictionary() : new RouteValueDictionary(defaults);
|
||||
|
||||
foreach (var parameter in parsedTemplate.Parameters)
|
||||
{
|
||||
if (parameter.DefaultValue != null)
|
||||
{
|
||||
if (result.ContainsKey(parameter.Name))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatTemplateRoute_CannotHaveDefaultValueSpecifiedInlineAndExplicitly(
|
||||
parameter.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(parameter.Name, parameter.DefaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
// 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 Microsoft.AspNetCore.Routing.TestObjects;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class DefaultEndpointFinderTest
|
||||
{
|
||||
[Fact]
|
||||
public void FindEndpoints_IgnoresCase_ForRouteNameLookup()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(new Address("home"));
|
||||
var endpoint2 = CreateEndpoint(new Address("admin"));
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(new Address("Admin"));
|
||||
|
||||
// Assert
|
||||
var endpoint = Assert.Single(result);
|
||||
Assert.Same(endpoint2, endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_MultipleEndpointsWithSameName_ReturnsFirstEndpoint_WithMatchingName()
|
||||
{
|
||||
// Arrange
|
||||
var name = "common-tag-for-all-my-section's-routes";
|
||||
var endpoint1 = CreateEndpoint(new Address(name));
|
||||
var endpoint2 = CreateEndpoint(new Address("admin"));
|
||||
var endpoint3 = CreateEndpoint(new Address(name));
|
||||
var endpoint4 = CreateEndpoint(new Address("products"));
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2, endpoint3, endpoint4);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(new Address(name));
|
||||
|
||||
// Assert
|
||||
var endpoint = Assert.Single(result);
|
||||
Assert.Same(endpoint, endpoint1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_ReturnsAllEndpoints_WhenNoEndpointsHaveAddress()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(address: null);
|
||||
var endpoint2 = CreateEndpoint(address: null);
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(new Address("Admin"));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
(ep) => Assert.Same(endpoint1, ep),
|
||||
(ep) => Assert.Same(endpoint2, ep));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_ReturnsAllEndpoints_WhenLookupAddress_IsNull()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(new Address("home"));
|
||||
var endpoint2 = CreateEndpoint(new Address("admin"));
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(lookupAddress: null);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
(ep) => Assert.Same(endpoint1, ep),
|
||||
(ep) => Assert.Same(endpoint2, ep));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_ReturnsAllEndpoints_WhenNoEndpointsHaveAddress_AndLookupAddress_IsNull()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(address: null);
|
||||
var endpoint2 = CreateEndpoint(address: null);
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(lookupAddress: null);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
(ep) => Assert.Same(endpoint1, ep),
|
||||
(ep) => Assert.Same(endpoint2, ep));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_ReturnsAllEndpoints_WhenNoInformationGiven_OnLookupAddress()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(new Address("home"));
|
||||
var endpoint2 = CreateEndpoint(new Address("admin"));
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(new Address(name: null));
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result,
|
||||
(ep) => Assert.Same(endpoint1, ep),
|
||||
(ep) => Assert.Same(endpoint2, ep));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindEndpoints_ReturnsEmpty_WhenNoEndpointFound_WithLookupAddress_Name()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint(new Address("home"));
|
||||
var endpoint2 = CreateEndpoint(new Address("admin"));
|
||||
var endpointFinder = CreateDefaultEndpointFinder(endpoint1, endpoint2);
|
||||
|
||||
// Act
|
||||
var result = endpointFinder.FindEndpoints(new Address("DoesNotExist"));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
private Endpoint CreateEndpoint(Address address)
|
||||
{
|
||||
return new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
displayName: null,
|
||||
address: address);
|
||||
}
|
||||
|
||||
private DefaultEndpointFinder CreateDefaultEndpointFinder(params Endpoint[] endpoints)
|
||||
{
|
||||
return new DefaultEndpointFinder(
|
||||
new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(endpoints) }),
|
||||
NullLogger<DefaultEndpointFinder>.Instance);
|
||||
}
|
||||
|
||||
private class HomeController
|
||||
{
|
||||
public void Index() { }
|
||||
public void Contact() { }
|
||||
}
|
||||
|
||||
private class AdminController
|
||||
{
|
||||
public void Index() { }
|
||||
public void Contact() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,799 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Routing.Internal;
|
||||
using Microsoft.AspNetCore.Routing.Matchers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
public class DefaultLinkGeneratorTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetLink_Success()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(new { controller = "Home" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_Fail_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "Could not find a matching endpoint to generate a link.";
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(new { controller = "Home" });
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => linkGenerator.GetLink(context));
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLink_Fail()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(new { controller = "Home" });
|
||||
|
||||
// Act
|
||||
var canGenerateLink = linkGenerator.TryGetLink(context, out var link);
|
||||
|
||||
// Assert
|
||||
Assert.False(canGenerateLink);
|
||||
Assert.Null(link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_MultipleEndpoints_Success()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint("{controller}/{action}/{id?}");
|
||||
var endpoint2 = CreateEndpoint("{controller}/{action}");
|
||||
var endpoint3 = CreateEndpoint("{controller}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3));
|
||||
var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index", id = "10" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index/10", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_MultipleEndpoints_Success2()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint1 = CreateEndpoint("{controller}/{action}/{id}");
|
||||
var endpoint2 = CreateEndpoint("{controller}/{action}");
|
||||
var endpoint3 = CreateEndpoint("{controller}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint1, endpoint2, endpoint3));
|
||||
var context = CreateLinkGeneratorContext(new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_EncodesValues()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { name = "name with %special #characters" },
|
||||
ambientValues: new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ForListOfStrings()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
new { color = new List<string> { "red", "green", "blue" } },
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index?color=red&color=green&color=blue", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ForListOfInts()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
new { items = new List<int> { 10, 20, 30 } },
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index?items=10&items=20&items=30", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ForList_Empty()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
new { color = new List<string> { } },
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ForList_StringWorkaround()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
new { page = 1, color = new List<string> { "red", "green", "blue" }, message = "textfortest" },
|
||||
new { controller = "Home", action = "Index" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index?page=1&color=red&color=green&color=blue&message=textfortest", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_Success_AmbientValues()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index" },
|
||||
ambientValues: new { controller = "Home" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void RouteGenerationRejectsConstraints()
|
||||
//{
|
||||
// // Arrange
|
||||
// var context = CreateLinkGeneratorContext(new { p1 = "abcd" });
|
||||
|
||||
// var endpoint = CreateEndpoint(
|
||||
// "{p1}/{p2}",
|
||||
// new { p2 = "catchall" },
|
||||
// true,
|
||||
// new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// // Act
|
||||
// var virtualPath = route.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Null(virtualPath);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void RouteGenerationAcceptsConstraints()
|
||||
//{
|
||||
// // Arrange
|
||||
// var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" });
|
||||
|
||||
// var endpoint = CreateEndpoint(
|
||||
// "{p1}/{p2}",
|
||||
// new { p2 = "catchall" },
|
||||
// true,
|
||||
// new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(pathData);
|
||||
// Assert.Equal("/hello/1234", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void RouteWithCatchAllRejectsConstraints()
|
||||
//{
|
||||
// // Arrange
|
||||
// var context = CreateLinkGeneratorContext(new { p1 = "abcd" });
|
||||
|
||||
// var endpoint = CreateEndpoint(
|
||||
// "{p1}/{*p2}",
|
||||
// new { p2 = "catchall" },
|
||||
// true,
|
||||
// new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// // Act
|
||||
// var virtualPath = route.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Null(virtualPath);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void RouteWithCatchAllAcceptsConstraints()
|
||||
//{
|
||||
// // Arrange
|
||||
// var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" });
|
||||
|
||||
// var endpoint = CreateEndpoint(
|
||||
// "{p1}/{*p2}",
|
||||
// new { p2 = "catchall" },
|
||||
// true,
|
||||
// new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(pathData);
|
||||
// Assert.Equal("/hello/1234", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLinkWithNonParameterConstraintReturnsUrlWithoutQueryString()
|
||||
//{
|
||||
// // Arrange
|
||||
// var context = CreateLinkGeneratorContext(new { p1 = "hello", p2 = "1234" });
|
||||
|
||||
// var target = new Mock<IRouteConstraint>();
|
||||
// target
|
||||
// .Setup(
|
||||
// e => e.Match(
|
||||
// It.IsAny<HttpContext>(),
|
||||
// It.IsAny<IRouter>(),
|
||||
// It.IsAny<string>(),
|
||||
// It.IsAny<RouteValueDictionary>(),
|
||||
// It.IsAny<RouteDirection>()))
|
||||
// .Returns(true)
|
||||
// .Verifiable();
|
||||
|
||||
// var endpoint = CreateEndpoint(
|
||||
// "{p1}/{p2}",
|
||||
// new { p2 = "catchall" },
|
||||
// true,
|
||||
// new RouteValueDictionary(new { p2 = target.Object }));
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(pathData);
|
||||
// Assert.Equal("/hello/1234", link);
|
||||
//
|
||||
//
|
||||
|
||||
// target.VerifyAll();
|
||||
//}
|
||||
|
||||
//// Any ambient values from the current request should be visible to constraint, even
|
||||
//// if they have nothing to do with the route generating a link
|
||||
//[Fact]
|
||||
//public void GetLink_ConstraintsSeeAmbientValues()
|
||||
//{
|
||||
// // Arrange
|
||||
// var constraint = new CapturingConstraint();
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "slug/{controller}/{action}",
|
||||
// defaultValues: null,
|
||||
// handleRequest: true,
|
||||
// constraints: new { c = constraint });
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Store" },
|
||||
// ambientValues: new { Controller = "Home", action = "Blog", extra = "42" });
|
||||
|
||||
// var expectedValues = new RouteValueDictionary(
|
||||
// new { controller = "Home", action = "Store", extra = "42" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/slug/Home/Store", link);
|
||||
//
|
||||
//
|
||||
|
||||
// Assert.Equal(expectedValues, constraint.Values);
|
||||
//}
|
||||
|
||||
//// Non-parameter default values from the routing generating a link are not in the 'values'
|
||||
//// collection when constraints are processed.
|
||||
//[Fact]
|
||||
//public void GetLink_ConstraintsDontSeeDefaults_WhenTheyArentParameters()
|
||||
//{
|
||||
// // Arrange
|
||||
// var constraint = new CapturingConstraint();
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "slug/{controller}/{action}",
|
||||
// defaultValues: new { otherthing = "17" },
|
||||
// handleRequest: true,
|
||||
// constraints: new { c = constraint });
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Store" },
|
||||
// ambientValues: new { Controller = "Home", action = "Blog" });
|
||||
|
||||
// var expectedValues = new RouteValueDictionary(
|
||||
// new { controller = "Home", action = "Store" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/slug/Home/Store", link);
|
||||
//
|
||||
//
|
||||
|
||||
// Assert.Equal(expectedValues, constraint.Values);
|
||||
//}
|
||||
|
||||
//// Default values are visible to the constraint when they are used to fill a parameter.
|
||||
//[Fact]
|
||||
//public void GetLink_ConstraintsSeesDefault_WhenThereItsAParamter()
|
||||
//{
|
||||
// // Arrange
|
||||
// var constraint = new CapturingConstraint();
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "slug/{controller}/{action}",
|
||||
// defaultValues: new { action = "Index" },
|
||||
// handleRequest: true,
|
||||
// constraints: new { c = constraint });
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { controller = "Shopping" },
|
||||
// ambientValues: new { Controller = "Home", action = "Blog" });
|
||||
|
||||
// var expectedValues = new RouteValueDictionary(
|
||||
// new { controller = "Shopping", action = "Index" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/slug/Shopping", link);
|
||||
//
|
||||
//
|
||||
|
||||
// Assert.Equal(expectedValues, constraint.Values);
|
||||
//}
|
||||
|
||||
//// Default values from the routing generating a link are in the 'values' collection when
|
||||
//// constraints are processed - IFF they are specified as values or ambient values.
|
||||
//[Fact]
|
||||
//public void GetLink_ConstraintsSeeDefaults_IfTheyAreSpecifiedOrAmbient()
|
||||
//{
|
||||
// // Arrange
|
||||
// var constraint = new CapturingConstraint();
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "slug/{controller}/{action}",
|
||||
// defaultValues: new { otherthing = "17", thirdthing = "13" },
|
||||
// handleRequest: true,
|
||||
// constraints: new { c = constraint });
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Store", thirdthing = "13" },
|
||||
// ambientValues: new { Controller = "Home", action = "Blog", otherthing = "17" });
|
||||
|
||||
// var expectedValues = new RouteValueDictionary(
|
||||
// new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/slug/Home/Store", link);
|
||||
//
|
||||
//
|
||||
|
||||
// Assert.Equal(expectedValues.OrderBy(kvp => kvp.Key), constraint.Values.OrderBy(kvp => kvp.Key));
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_Success()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", id = 4 });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/4", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_NonMatchingvalue()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", id = "asf" });
|
||||
|
||||
// // Act
|
||||
// var path = route.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Null(path);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_OptionalParameter_ValuePresent()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", id = 98 });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/98", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int?}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", id = "sdfd" });
|
||||
|
||||
// // Act
|
||||
// var path = route.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Null(path);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_CompositeInlineConstraint()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("{controller}/{action}/{id:int:range(1,20)}");
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", id = 14 });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/14", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_InlineConstraints_CompositeConstraint_FromConstructor()
|
||||
//{
|
||||
// // Arrange
|
||||
// var constraint = new MaxLengthRouteConstraint(20);
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "{controller}/{action}/{name:alpha}",
|
||||
// defaultValues: null,
|
||||
// handleRequest: true,
|
||||
// constraints: new { name = constraint });
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", name = "products" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/products", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_OptionalParameter_ParameterPresentInValues()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home", name = "products" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index/products", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_OptionalParameter_ParameterNotPresentInValues()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_OptionalParameter_ParameterPresentInValuesAndDefaults()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint(
|
||||
template: "{controller}/{action}/{name?}",
|
||||
defaultValues: new { name = "default-products" });
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home", name = "products" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index/products", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_OptionalParameter_ParameterNotPresentInValues_PresentInDefaults()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint(
|
||||
template: "{controller}/{action}/{name?}",
|
||||
defaultValues: new { name = "products" });
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_ParameterNotPresentInTemplate_PresentInValues()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}/{name}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home", name = "products", format = "json" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index/products?format=json", link);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "{controller}/{action}/.{name?}",
|
||||
// defaultValues: null,
|
||||
// handleRequest: true,
|
||||
// constraints: null);
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home", name = "products" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/.products", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "{controller}/{action}/.{name?}",
|
||||
// defaultValues: null,
|
||||
// handleRequest: true,
|
||||
// constraints: null);
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { action = "Index", controller = "Home" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/Home/Index/", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void GetLink_OptionalParameter_InSimpleSegment()
|
||||
{
|
||||
// Arrange
|
||||
var endpoint = CreateEndpoint("{controller}/{action}/{name?}");
|
||||
var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
var context = CreateLinkGeneratorContext(
|
||||
suppliedValues: new { action = "Index", controller = "Home" });
|
||||
|
||||
// Act
|
||||
var link = linkGenerator.GetLink(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("/Home/Index", link);
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_TwoOptionalParameters_OneValueFromAmbientValues()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("a/{b=15}/{c?}/{d?}");
|
||||
// var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// suppliedValues: new { },
|
||||
// ambientValues: new { c = "17" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/a/15/17", link);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_OptionalParameterAfterDefault_OneValueFromAmbientValues()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint("a/{b=15}/{c?}");
|
||||
// var linkGenerator = CreateLinkGenerator(CreateEndpointFinder(endpoint));
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// suppliedValues: new { },
|
||||
// ambientValues: new { c = "17" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal("/a/15/17", link);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void GetLink_TwoOptionalParametersAfterDefault_LastValueFromAmbientValues()
|
||||
//{
|
||||
// // Arrange
|
||||
// var endpoint = CreateEndpoint(
|
||||
// template: "a/{b=15}/{c?}/{d?}",
|
||||
// defaultValues: null,
|
||||
// handleRequest: true,
|
||||
// constraints: null);
|
||||
|
||||
// var context = CreateLinkGeneratorContext(
|
||||
// values: new { },
|
||||
// ambientValues: new { d = "17" });
|
||||
|
||||
// // Act
|
||||
// var link = linkGenerator.GetLink(context);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(pathData);
|
||||
// Assert.Equal("/a", link);
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
private LinkGeneratorContext CreateLinkGeneratorContext(object suppliedValues, object ambientValues = null)
|
||||
{
|
||||
var context = new LinkGeneratorContext();
|
||||
context.SuppliedValues = new RouteValueDictionary(suppliedValues);
|
||||
context.AmbientValues = new RouteValueDictionary(ambientValues);
|
||||
return context;
|
||||
}
|
||||
|
||||
private MatcherEndpoint CreateEndpoint(string template, object defaultValues = null)
|
||||
{
|
||||
return new MatcherEndpoint(
|
||||
next => (httpContext) => Task.CompletedTask,
|
||||
template,
|
||||
defaultValues,
|
||||
0,
|
||||
EndpointMetadataCollection.Empty,
|
||||
null,
|
||||
new Address("foo"));
|
||||
}
|
||||
|
||||
private ILinkGenerator CreateLinkGenerator(IEndpointFinder endpointFinder)
|
||||
{
|
||||
return new DefaultLinkGenerator(
|
||||
endpointFinder,
|
||||
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
|
||||
Mock.Of<ILogger<DefaultLinkGenerator>>());
|
||||
}
|
||||
|
||||
private DefaultEndpointFinder CreateEndpointFinder(params Endpoint[] endpoints)
|
||||
{
|
||||
return new DefaultEndpointFinder(
|
||||
new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(endpoints) }),
|
||||
NullLogger<DefaultEndpointFinder>.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,11 +24,13 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
// Arrange
|
||||
var defaultEndpoint = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"No constraint endpoint");
|
||||
"No constraint endpoint",
|
||||
address: null);
|
||||
|
||||
var postEndpoint = new TestEndpoint(
|
||||
new EndpointMetadataCollection(new object[] { new HttpMethodEndpointConstraint(new[] { "POST" }) }),
|
||||
"POST constraint endpoint");
|
||||
"POST constraint endpoint",
|
||||
address: null);
|
||||
|
||||
var endpoints = new Endpoint[]
|
||||
{
|
||||
|
|
@ -60,11 +62,13 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
|
||||
var defaultEndpoint1 = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"Ambiguous1");
|
||||
"Ambiguous1",
|
||||
address: null);
|
||||
|
||||
var defaultEndpoint2 = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"Ambiguous2");
|
||||
"Ambiguous2",
|
||||
address: null);
|
||||
|
||||
var endpoints = new Endpoint[]
|
||||
{
|
||||
|
|
@ -96,8 +100,8 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
|
||||
var actions = new Endpoint[]
|
||||
{
|
||||
new TestEndpoint(EndpointMetadataCollection.Empty, "A1"),
|
||||
new TestEndpoint(EndpointMetadataCollection.Empty, "A2"),
|
||||
new TestEndpoint(EndpointMetadataCollection.Empty, "A1", address: null),
|
||||
new TestEndpoint(EndpointMetadataCollection.Empty, "A2", address: null),
|
||||
};
|
||||
var selector = CreateSelector(actions, loggerFactory);
|
||||
|
||||
|
|
@ -120,9 +124,13 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
// Arrange
|
||||
var actionWithConstraints = new TestEndpoint(
|
||||
new EndpointMetadataCollection(new[] { new HttpMethodEndpointConstraint(new string[] { "POST" }) }),
|
||||
"Has constraint");
|
||||
"Has constraint",
|
||||
address: null);
|
||||
|
||||
var actionWithoutConstraints = new TestEndpoint(EndpointMetadataCollection.Empty, "No constraint");
|
||||
var actionWithoutConstraints = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"No constraint",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints };
|
||||
|
||||
|
|
@ -140,9 +148,15 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
public void SelectBestCandidate_ConstraintsRejectAll()
|
||||
{
|
||||
// Arrange
|
||||
var action1 = new TestEndpoint(new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), "action1");
|
||||
var action1 = new TestEndpoint(
|
||||
new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }),
|
||||
"action1",
|
||||
address: null);
|
||||
|
||||
var action2 = new TestEndpoint(new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }), "action2");
|
||||
var action2 = new TestEndpoint(
|
||||
new EndpointMetadataCollection(new[] { new BooleanConstraint() { Pass = false, } }),
|
||||
"action2",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { action1, action2 };
|
||||
|
||||
|
|
@ -164,13 +178,17 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
{
|
||||
new BooleanConstraint() { Pass = false, Order = 0 },
|
||||
new BooleanConstraint() { Pass = true, Order = 1 },
|
||||
}), "action1");
|
||||
}),
|
||||
"action1",
|
||||
address: null);
|
||||
|
||||
var action2 = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraint() { Pass = true, Order = 0 },
|
||||
new BooleanConstraint() { Pass = false, Order = 1 },
|
||||
}), "action2");
|
||||
}),
|
||||
"action2",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { action1, action2 };
|
||||
|
||||
|
|
@ -194,9 +212,14 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
{
|
||||
Constraint = new BooleanConstraint() { Pass = true },
|
||||
},
|
||||
}), "actionWithConstraints");
|
||||
}),
|
||||
"actionWithConstraints",
|
||||
address: null);
|
||||
|
||||
var actionWithoutConstraints = new TestEndpoint(EndpointMetadataCollection.Empty, "actionWithoutConstraints");
|
||||
var actionWithoutConstraints = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"actionWithoutConstraints",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints };
|
||||
|
||||
|
|
@ -214,7 +237,7 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
public void SelectBestCandidate_MultipleCallsNoConstraint_ReturnsEndpoint()
|
||||
{
|
||||
// Arrange
|
||||
var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint");
|
||||
var noConstraint = new TestEndpoint(EndpointMetadataCollection.Empty, "noConstraint", address: null);
|
||||
|
||||
var actions = new Endpoint[] { noConstraint };
|
||||
|
||||
|
|
@ -237,7 +260,9 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
var noConstraint = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new object(),
|
||||
}), "noConstraint");
|
||||
}),
|
||||
"noConstraint",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { noConstraint };
|
||||
|
||||
|
|
@ -260,7 +285,9 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
var nullConstraint = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new ConstraintFactory(),
|
||||
}), "nullConstraint");
|
||||
}),
|
||||
"nullConstraint",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { nullConstraint };
|
||||
|
||||
|
|
@ -284,9 +311,14 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
var actionWithConstraints = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraintMarker() { Pass = true },
|
||||
}), "actionWithConstraints");
|
||||
}),
|
||||
"actionWithConstraints",
|
||||
address: null);
|
||||
|
||||
var actionWithoutConstraints = new TestEndpoint(EndpointMetadataCollection.Empty, "actionWithoutConstraints");
|
||||
var actionWithoutConstraints = new TestEndpoint(
|
||||
EndpointMetadataCollection.Empty,
|
||||
"actionWithoutConstraints",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { actionWithConstraints, actionWithoutConstraints, };
|
||||
|
||||
|
|
@ -308,12 +340,16 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
var best = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraint() { Pass = true, Order = 0, },
|
||||
}), "best");
|
||||
}),
|
||||
"best",
|
||||
address: null);
|
||||
|
||||
var worst = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraint() { Pass = true, Order = 1, },
|
||||
}), "worst");
|
||||
}),
|
||||
"worst",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { best, worst };
|
||||
|
||||
|
|
@ -337,14 +373,18 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
new BooleanConstraint() { Pass = true, Order = 0, },
|
||||
new BooleanConstraint() { Pass = true, Order = 1, },
|
||||
new BooleanConstraint() { Pass = true, Order = 2, },
|
||||
}), "best");
|
||||
}),
|
||||
"best",
|
||||
address: null);
|
||||
|
||||
var worst = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraint() { Pass = true, Order = 0, },
|
||||
new BooleanConstraint() { Pass = true, Order = 1, },
|
||||
new BooleanConstraint() { Pass = true, Order = 3, },
|
||||
}), "worst");
|
||||
}),
|
||||
"worst",
|
||||
address: null);
|
||||
|
||||
var actions = new Endpoint[] { best, worst };
|
||||
|
||||
|
|
@ -367,16 +407,20 @@ namespace Microsoft.AspNetCore.Routing.EndpointConstraints
|
|||
new BooleanConstraint() { Pass = true, Order = 0, },
|
||||
new BooleanConstraint() { Pass = true, Order = 1, },
|
||||
new BooleanConstraint() { Pass = false, Order = 2, },
|
||||
}), "nomatch1");
|
||||
}),
|
||||
"nomatch1",
|
||||
address: null);
|
||||
|
||||
var nomatch2 = new TestEndpoint(new EndpointMetadataCollection(new[]
|
||||
{
|
||||
new BooleanConstraint() { Pass = true, Order = 0, },
|
||||
new BooleanConstraint() { Pass = true, Order = 1, },
|
||||
new BooleanConstraint() { Pass = false, Order = 3, },
|
||||
}), "nomatch2");
|
||||
}),
|
||||
"nomatch2",
|
||||
address: null);
|
||||
|
||||
var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best");
|
||||
var best = new TestEndpoint(EndpointMetadataCollection.Empty, "best", address: null);
|
||||
|
||||
var actions = new Endpoint[] { best, nomatch1, nomatch2 };
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
{
|
||||
var context = new EndpointConstraintContext();
|
||||
|
||||
var endpointSelectorCandidate = new EndpointSelectorCandidate(new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty), new List<IEndpointConstraint> { constraint });
|
||||
var endpointSelectorCandidate = new EndpointSelectorCandidate(
|
||||
new TestEndpoint(EndpointMetadataCollection.Empty, string.Empty, address: null),
|
||||
new List<IEndpointConstraint> { constraint });
|
||||
|
||||
context.Candidates = new List<EndpointSelectorCandidate> { endpointSelectorCandidate };
|
||||
context.CurrentCandidate = context.Candidates[0];
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
template,
|
||||
null,
|
||||
0,
|
||||
EndpointMetadataCollection.Empty, "endpoint: " + template);
|
||||
EndpointMetadataCollection.Empty,
|
||||
"endpoint: " + template,
|
||||
address: null);
|
||||
}
|
||||
|
||||
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
{
|
||||
private MatcherEndpoint CreateEndpoint(string template, int order, object values = null, EndpointMetadataCollection metadata = null)
|
||||
{
|
||||
return new MatcherEndpoint((next) => null, template, values, order, metadata ?? EndpointMetadataCollection.Empty, template);
|
||||
return new MatcherEndpoint((next) => null, template, values, order, metadata ?? EndpointMetadataCollection.Empty, template, address: null);
|
||||
}
|
||||
|
||||
private TreeMatcher CreateTreeMatcher(EndpointDataSource endpointDataSource)
|
||||
|
|
@ -88,6 +88,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
|||
|
||||
// Assert
|
||||
Assert.Equal(endpointWithConstraint, endpointFeature.Endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
|
|||
{
|
||||
internal class TestEndpoint : Endpoint
|
||||
{
|
||||
public TestEndpoint(EndpointMetadataCollection metadata, string displayName) : base(metadata, displayName)
|
||||
public TestEndpoint(EndpointMetadataCollection metadata, string displayName, Address address)
|
||||
: base(metadata, displayName, address)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Routing.TestObjects
|
|||
{
|
||||
if (_isHandled)
|
||||
{
|
||||
feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint");
|
||||
feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint", address: null);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
|||
Loading…
Reference in New Issue