Merge pull request #887 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
James Newton-King 2018-10-23 11:33:13 +13:00 committed by GitHub
commit 74336f10c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 123 additions and 648 deletions

View File

@ -1,126 +0,0 @@
// 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.Http;
namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Defines a contract to generate a URL from a template.
/// </summary>
/// <remarks>
/// A <see cref="LinkGenerationTemplate"/> can be created from <see cref="LinkGenerator.GetTemplateByAddress{TAddress}(TAddress, LinkGenerationTemplateOptions)"/>
/// by supplying an address value which has matching endpoints. The returned <see cref="LinkGenerationTemplate"/>
/// will be bound to the endpoints matching the address that was originally provided.
/// </remarks>
public abstract class LinkGenerationTemplate
{
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <param name="values">The route values. Used to expand parameters in the route template. Optional.</param>
/// <param name="pathBase">
/// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of <see cref="HttpRequest.PathBase"/> will be used.
/// </param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
public abstract string GetPath(
HttpContext httpContext,
object values,
PathString? pathBase = default,
FragmentString fragment = default,
LinkOptions options = default);
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
/// <param name="values">The route values. Used to expand parameters in the route template. Optional.</param>
/// <param name="pathBase">An optional URI path base. Prepended to the path in the resulting URI.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
public abstract string GetPath(
object values,
PathString pathBase = default,
FragmentString fragment = default,
LinkOptions options = default);
/// <summary>
/// Generates an absolute URI based on the provided values.
/// </summary>
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <param name="values">The route values. Used to expand parameters in the route template. Optional.</param>
/// <param name="scheme">
/// The URI scheme, applied to the resulting URI. Optional. If not provided, the value of <see cref="HttpRequest.Scheme"/> will be used.
/// </param>
/// <param name="host">
/// The URI host/authority, applied to the resulting URI. Optional. If not provided, the value <see cref="HttpRequest.Host"/> will be used.
/// See the remarks section for details about the security implications of the <paramref name="host"/>.
/// </param>
/// <param name="pathBase">
/// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of <see cref="HttpRequest.PathBase"/> will be used.
/// </param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>A URI with an absolute path, or <c>null</c>.</returns>
/// <remarks>
/// <para>
/// The value of <paramref name="host" /> should be a trusted value. Relying on the value of the current request
/// can allow untrusted input to influence the resulting URI unless the <c>Host</c> header has been validated.
/// See the deployment documentation for instructions on how to properly validate the <c>Host</c> header in
/// your deployment environment.
/// </para>
/// </remarks>
public abstract string GetUri(
HttpContext httpContext,
object values,
string scheme = default,
HostString? host = default,
PathString? pathBase = default,
FragmentString fragment = default,
LinkOptions options = default);
/// <summary>
/// Generates an absolute URI based on the provided values.
/// </summary>
/// <param name="values">The route values. Used to expand parameters in the route template. Optional.</param>
/// <param name="scheme">The URI scheme, applied to the resulting URI.</param>
/// <param name="host">
/// The URI host/authority, applied to the resulting URI.
/// See the remarks section for details about the security implications of the <paramref name="host"/>.
/// </param>
/// <param name="pathBase">An optional URI path base. Prepended to the path in the resulting URI.</param>
/// <param name="fragment">An optional URI fragment. Appended to the resulting URI.</param>
/// <param name="options">
/// An optional <see cref="LinkOptions"/>. Settings on provided object override the settings with matching
/// names from <c>RouteOptions</c>.
/// </param>
/// <returns>An absolute URI, or <c>null</c>.</returns>
/// <remarks>
/// <para>
/// The value of <paramref name="host" /> should be a trusted value. Relying on the value of the current request
/// can allow untrusted input to influence the resulting URI unless the <c>Host</c> header has been validated.
/// See the deployment documentation for instructions on how to properly validate the <c>Host</c> header in
/// your deployment environment.
/// </para>
/// </remarks>
public abstract string GetUri(
object values,
string scheme,
HostString host,
PathString pathBase = default,
FragmentString fragment = default,
LinkOptions options = default);
}
}

View File

@ -1,17 +0,0 @@
// 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
{
/// <summary>
/// Contains options for creating a <see cref="LinkGenerationTemplate" />.
/// </summary>
public class LinkGenerationTemplateOptions
{
/// <summary>
/// Gets or sets a value indicating whether the template will use route values from the current request
/// when generating a URI.
/// </summary>
public bool UseAmbientValues { get; set; }
}
}

View File

@ -147,16 +147,5 @@ namespace Microsoft.AspNetCore.Routing
PathString pathBase = default,
FragmentString fragment = default,
LinkOptions options = default);
/// <summary>
/// Gets a <see cref="LinkGenerationTemplate"/> based on the provided <paramref name="address"/>.
/// </summary>
/// <typeparam name="TAddress">The address type.</typeparam>
/// <param name="address">The address value. Used to resolve endpoints.</param>
/// <param name="options">Options for the created <see cref="LinkGenerationTemplate"/>.</param>
/// <returns>
/// A <see cref="LinkGenerationTemplate"/> if one or more endpoints matching the address can be found, otherwise <c>null</c>.
/// </returns>
public abstract LinkGenerationTemplate GetTemplateByAddress<TAddress>(TAddress address, LinkGenerationTemplateOptions options = null);
}
}

View File

@ -14,15 +14,20 @@ namespace Microsoft.AspNetCore.Routing
/// </summary>
public sealed class DefaultEndpointDataSource : EndpointDataSource
{
private readonly List<Endpoint> _endpoints;
private readonly IReadOnlyList<Endpoint> _endpoints;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultEndpointDataSource" /> class.
/// </summary>
/// <param name="endpoints">The <see cref="Endpoint"/> instances that the data source will return.</param>
public DefaultEndpointDataSource(params Endpoint[] endpoints)
: this((IEnumerable<Endpoint>) endpoints)
{
if (endpoints == null)
{
throw new ArgumentNullException(nameof(endpoints));
}
_endpoints = (Endpoint[])endpoints.Clone();
}
/// <summary>
@ -36,8 +41,7 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(endpoints));
}
_endpoints = new List<Endpoint>();
_endpoints.AddRange(endpoints);
_endpoints = new List<Endpoint>(endpoints);
}
/// <summary>

View File

@ -1,120 +0,0 @@
// 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.Http;
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Routing
{
internal sealed class DefaultLinkGenerationTemplate : LinkGenerationTemplate
{
public DefaultLinkGenerationTemplate(DefaultLinkGenerator linkGenerator, List<RouteEndpoint> endpoints, LinkGenerationTemplateOptions options)
{
LinkGenerator = linkGenerator;
Endpoints = endpoints;
Options = options;
}
public DefaultLinkGenerator LinkGenerator { get; }
public List<RouteEndpoint> Endpoints { get; }
public LinkGenerationTemplateOptions Options { get; }
public override string GetPath(
HttpContext httpContext,
object values,
PathString? pathBase = default,
FragmentString fragment = default,
LinkOptions options = default)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
return LinkGenerator.GetPathByEndpoints(
Endpoints,
new RouteValueDictionary(values),
GetAmbientValues(httpContext),
pathBase ?? httpContext.Request.PathBase,
fragment,
options);
}
public override string GetPath(
object values,
PathString pathBase = default,
FragmentString fragment = default,
LinkOptions options = default)
{
return LinkGenerator.GetPathByEndpoints(
Endpoints,
new RouteValueDictionary(values),
ambientValues: null,
pathBase: pathBase,
fragment: fragment,
options: options);
}
public override string GetUri(
HttpContext httpContext,
object values,
string scheme = default,
HostString? host = default,
PathString? pathBase = default,
FragmentString fragment = default,
LinkOptions options = default)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
return LinkGenerator.GetUriByEndpoints(
Endpoints,
new RouteValueDictionary(values),
GetAmbientValues(httpContext),
scheme ?? httpContext.Request.Scheme,
host ?? httpContext.Request.Host,
pathBase ?? httpContext.Request.PathBase,
fragment,
options);
}
public override string GetUri(
object values,
string scheme,
HostString host,
PathString pathBase = default,
FragmentString fragment = default,
LinkOptions options = default)
{
if (string.IsNullOrEmpty(scheme))
{
throw new ArgumentException("A scheme must be provided.", nameof(scheme));
}
if (!host.HasValue)
{
throw new ArgumentException("A host must be provided.", nameof(host));
}
return LinkGenerator.GetUriByEndpoints(
Endpoints,
new RouteValueDictionary(values),
ambientValues: null,
scheme: scheme,
host: host,
pathBase: pathBase,
fragment: fragment,
options: options);
}
private RouteValueDictionary GetAmbientValues(HttpContext httpContext)
{
return (Options?.UseAmbientValues ?? false) ? DefaultLinkGenerator.GetAmbientValues(httpContext) : null;
}
}
}

View File

@ -189,17 +189,6 @@ namespace Microsoft.AspNetCore.Routing
options: options);
}
public override LinkGenerationTemplate GetTemplateByAddress<TAddress>(TAddress address, LinkGenerationTemplateOptions options = default)
{
var endpoints = GetEndpoints(address);
if (endpoints.Count == 0)
{
return null;
}
return new DefaultLinkGenerationTemplate(this, endpoints, options);
}
private List<RouteEndpoint> GetEndpoints<TAddress>(TAddress address)
{
var addressingScheme = _serviceProvider.GetRequiredService<IEndpointAddressScheme<TAddress>>();
@ -217,7 +206,6 @@ namespace Microsoft.AspNetCore.Routing
return endpoints;
}
// Also called from DefaultLinkGenerationTemplate
public string GetPathByEndpoints(
List<RouteEndpoint> endpoints,
RouteValueDictionary values,

View File

@ -242,18 +242,29 @@ namespace Microsoft.AspNetCore.Routing.Internal
// Used by TemplateBinder.TryBindValues - the new code path of LinkGenerator
public PathString ToPathString()
{
if (_path.Length > 0 && _path[0] != '/')
PathString pathString;
if (_path.Length > 0)
{
// Normalize generated paths so that they always contain a leading slash.
_path.Insert(0, '/');
if (_path[0] != '/')
{
// Normalize generated paths so that they always contain a leading slash.
_path.Insert(0, '/');
}
if (AppendTrailingSlash && _path[_path.Length - 1] != '/')
{
_path.Append('/');
}
pathString = new PathString(_path.ToString());
}
else
{
pathString = PathString.Empty;
}
if (AppendTrailingSlash && _path.Length > 0 && _path[_path.Length - 1] != '/')
{
_path.Append('/');
}
return new PathString(_path.ToString());
return pathString;
}
// Used by TemplateBinder.TryBindValues - the new code path of LinkGenerator

View File

@ -11,11 +11,6 @@ namespace Microsoft.AspNetCore.Routing
/// </summary>
public static class LinkGeneratorEndpointNameAddressExtensions
{
private static readonly LinkGenerationTemplateOptions _templateOptions = new LinkGenerationTemplateOptions()
{
UseAmbientValues = false,
};
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
@ -227,28 +222,5 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetUriByAddress<string>(endpointName, new RouteValueDictionary(values), scheme, host, pathBase, fragment, options);
}
/// <summary>
/// Gets a <see cref="LinkGenerationTemplate"/> based on the provided <paramref name="endpointName"/>.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="endpointName">The endpoint name. Used to resolve endpoints. Optional.</param>
/// <returns>
/// A <see cref="LinkGenerationTemplate"/> if one or more endpoints matching the address can be found, otherwise <c>null</c>.
/// </returns>
public static LinkGenerationTemplate GetTemplateByName(this LinkGenerator generator, string endpointName)
{
if (generator == null)
{
throw new ArgumentNullException(nameof(generator));
}
if (endpointName == null)
{
throw new ArgumentNullException(nameof(endpointName));
}
return generator.GetTemplateByAddress<string>(endpointName, _templateOptions);
}
}
}

View File

@ -11,11 +11,6 @@ namespace Microsoft.AspNetCore.Routing
/// </summary>
public static class LinkGeneratorRouteValuesAddressExtensions
{
private static readonly LinkGenerationTemplateOptions _templateOptions = new LinkGenerationTemplateOptions()
{
UseAmbientValues = true,
};
/// <summary>
/// Generates a URI with an absolute path based on the provided values.
/// </summary>
@ -202,29 +197,6 @@ namespace Microsoft.AspNetCore.Routing
return generator.GetUriByAddress<RouteValuesAddress>(address, address.ExplicitValues, scheme, host, pathBase, fragment, options);
}
/// <summary>
/// Gets a <see cref="LinkGenerationTemplate"/> based on the provided <paramref name="routeName"/> and <paramref name="values"/>.
/// </summary>
/// <param name="generator">The <see cref="LinkGenerator"/>.</param>
/// <param name="routeName">The route name. Used to resolve endpoints. Optional.</param>
/// <param name="values">The route values. Used to resolve endpoints and expand parameters in the route template. Optional.</param>
/// <returns>
/// A <see cref="LinkGenerationTemplate"/> if one or more endpoints matching the address can be found, otherwise <c>null</c>.
/// </returns>
public static LinkGenerationTemplate GetTemplateByRouteValues(
this LinkGenerator generator,
string routeName,
object values)
{
if (generator == null)
{
throw new ArgumentNullException(nameof(generator));
}
var address = CreateAddress(httpContext: null, routeName, values);
return generator.GetTemplateByAddress<RouteValuesAddress>(address, _templateOptions);
}
private static RouteValuesAddress CreateAddress(HttpContext httpContext, string routeName, object values)
{
return new RouteValuesAddress()

View File

@ -75,16 +75,20 @@ namespace Microsoft.AspNetCore.Routing.Matching
int? foundScore = null;
for (var i = 0; i < candidateSet.Count; i++)
{
if (!candidateSet.IsValidCandidate(i))
{
continue;
}
ref var state = ref candidateSet[i];
var isValid = candidateSet.IsValidCandidate(i);
if (isValid && foundScore == null)
if (foundScore == null)
{
// This is the first match we've seen - speculatively assign it.
endpoint = state.Endpoint;
values = state.Values;
foundScore = state.Score;
}
else if (isValid && foundScore < state.Score)
else if (foundScore < state.Score)
{
// This candidate is lower priority than the one we've seen
// so far, we can stop.
@ -92,7 +96,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Don't worry about the 'null < state.Score' case, it returns false.
break;
}
else if (isValid && foundScore == state.Score)
else if (foundScore == state.Score)
{
// This is the second match we've found of the same score, so there
// must be an ambiguity.

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Http;
using Xunit;
@ -26,6 +25,34 @@ namespace Microsoft.AspNetCore.Routing
endpoint => Assert.Equal("2", endpoint.DisplayName));
}
[Fact]
public void Constructor_Params_ShouldMakeCopyOfEndpoints()
{
// Arrange
var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "1");
var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "2");
var endpoints = new[] { endpoint1, endpoint2 };
// Act
var dataSource = new DefaultEndpointDataSource(endpoints);
Array.Resize(ref endpoints, 1);
endpoints[0] = null;
// Assert
Assert.Equal(2, dataSource.Endpoints.Count);
Assert.Contains(endpoint1, dataSource.Endpoints);
Assert.Contains(endpoint2, dataSource.Endpoints);
}
[Fact]
public void Constructor_Params_ShouldThrowArgumentNullExceptionWhenEndpointsIsNull()
{
Endpoint[] endpoints = null;
var actual = Assert.Throws<ArgumentNullException>(() => new DefaultEndpointDataSource(endpoints));
Assert.Equal("endpoints", actual.ParamName);
}
[Fact]
public void Constructor_Enumerable_EndpointsInitialized()
{
@ -41,5 +68,33 @@ namespace Microsoft.AspNetCore.Routing
endpoint => Assert.Equal("1", endpoint.DisplayName),
endpoint => Assert.Equal("2", endpoint.DisplayName));
}
[Fact]
public void Constructor_Enumerable_ShouldMakeCopyOfEndpoints()
{
// Arrange
var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "1");
var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "2");
var endpoints = new List<Endpoint> { endpoint1, endpoint2 };
// Act
var dataSource = new DefaultEndpointDataSource((IEnumerable<Endpoint>)endpoints);
endpoints.RemoveAt(0);
endpoints[0] = null;
// Assert
Assert.Equal(2, dataSource.Endpoints.Count);
Assert.Contains(endpoint1, dataSource.Endpoints);
Assert.Contains(endpoint2, dataSource.Endpoints);
}
[Fact]
public void Constructor_Enumerable_ShouldThrowArgumentNullExceptionWhenEndpointsIsNull()
{
IEnumerable<Endpoint> endpoints = null;
var actual = Assert.Throws<ArgumentNullException>(() => new DefaultEndpointDataSource(endpoints));
Assert.Equal("endpoints", actual.ParamName);
}
}
}

View File

@ -1,207 +0,0 @@
// 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;
using Xunit;
namespace Microsoft.AspNetCore.Routing
{
// Tests DefaultLinkGenerationTemplate functionality - these are pretty light since most of the functionality
// is a direct subset of DefaultLinkGenerator
//
// Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests
// and DefaultLinkGeneratorProcessTemplateTest
public class DefaultLinkGenerationTemplateTest : LinkGeneratorTestBase
{
[Fact]
public void GetPath_WithoutHttpContext_WithPathBaseAndFragment()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: null);
// Act
var path = template.GetPath(
values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }),
new PathString("/Foo/Bar?encodeme?"),
new FragmentString("#Fragment?"),
new LinkOptions() { AppendTrailingSlash = true, });
// Assert
Assert.Equal("/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex/?query=some%3Fquery#Fragment?", path);
}
[Fact]
public void GetPath_WithHttpContext_WithPathBaseAndFragment()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: null);
var httpContext = CreateHttpContext();
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
// Act
var path = template.GetPath(
httpContext,
values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }),
fragment: new FragmentString("#Fragment?"),
options: new LinkOptions() { AppendTrailingSlash = true, });
// Assert
Assert.Equal("/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex/?query=some%3Fquery#Fragment?", path);
}
[Fact]
public void GetUri_WithoutHttpContext_WithPathBaseAndFragment()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: null);
// Act
var path = template.GetUri(
values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }),
"http",
new HostString("example.com"),
new PathString("/Foo/Bar?encodeme?"),
new FragmentString("#Fragment?"),
new LinkOptions() { AppendTrailingSlash = true, });
// Assert
Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex/?query=some%3Fquery#Fragment?", path);
}
[Fact]
public void GetUri_WithHttpContext_WithPathBaseAndFragment()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: null);
var httpContext = CreateHttpContext();
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("example.com");
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
// Act
var uri = template.GetUri(
httpContext,
values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }),
fragment: new FragmentString("#Fragment?"),
options: new LinkOptions() { AppendTrailingSlash = true, });
// Assert
Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex/?query=some%3Fquery#Fragment?", uri);
}
[Fact]
public void GetPath_WithHttpContext_IncludesAmbientValues_WhenUseAmbientValuesIsTrue()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: new LinkGenerationTemplateOptions()
{
UseAmbientValues = true,
});
var httpContext = CreateHttpContext(new { controller = "Home", });
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("example.com");
// Act
var uri = template.GetPath(httpContext, values: new RouteValueDictionary(new { action = "Index", }));
// Assert
Assert.Equal("/Home/Index", uri);
}
[Fact]
public void GetPath_WithHttpContext_ExcludesAmbientValues_WhenUseAmbientValuesIsFalse()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: new LinkGenerationTemplateOptions()
{
UseAmbientValues = false,
});
var httpContext = CreateHttpContext(new { controller = "Home", });
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("example.com");
// Act
var uri = template.GetPath(httpContext, values: new RouteValueDictionary(new { action = "Index", }));
// Assert
Assert.Null(uri);
}
[Fact]
public void GetUri_WithHttpContext_IncludesAmbientValues_WhenUseAmbientValuesIsTrue()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: new LinkGenerationTemplateOptions()
{
UseAmbientValues = true,
});
var httpContext = CreateHttpContext(new { controller = "Home", });
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("example.com");
// Act
var uri = template.GetUri(httpContext, values: new { action = "Index", });
// Assert
Assert.Equal("http://example.com/Home/Index", uri);
}
[Fact]
public void GetUri_WithHttpContext_ExcludesAmbientValues_WhenUseAmbientValuesIsFalse()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}");
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}");
var linkGenerator = CreateLinkGenerator();
var template = new DefaultLinkGenerationTemplate(linkGenerator, new List<RouteEndpoint>() { endpoint1, endpoint2, }, options: new LinkGenerationTemplateOptions()
{
UseAmbientValues = false,
});
var httpContext = CreateHttpContext(new { controller = "Home", });
httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("example.com");
// Act
var uri = template.GetUri(httpContext, values: new { action = "Index", });
// Assert
Assert.Null(uri);
}
}
}

View File

@ -524,42 +524,6 @@ namespace Microsoft.AspNetCore.Routing
Assert.Equal("ftp://example.com:5000/Home/Index", uri);
}
[Fact]
public void GetTemplateByAddress_WithNoMatch_ReturnsNull()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), });
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), });
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
// Act
var template = linkGenerator.GetTemplateByAddress(address: 0);
// Assert
Assert.Null(template);
}
[Fact]
public void GetTemplateByAddress_WithMatch_ReturnsTemplate()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), });
var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), });
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
// Act
var template = linkGenerator.GetTemplateByAddress(address: 1);
// Assert
Assert.NotNull(template);
Assert.Collection(
Assert.IsType<DefaultLinkGenerationTemplate>(template).Endpoints,
e => Assert.Same(endpoint1, e),
e => Assert.Same(endpoint2, e));
}
[Fact]
public void GetTemplateBinder_CanCache()
{

View File

@ -65,5 +65,36 @@ namespace Microsoft.AspNetCore.Routing.Internal
// Assert
Assert.Equal(expected, uriBuilldingContext.ToString());
}
[Theory]
[InlineData("/Author", false, false, "/UrlEncode[[Author]]")]
[InlineData("/Author", false, true, "/UrlEncode[[Author]]")]
[InlineData("/Author", true, false, "/UrlEncode[[Author]]/")]
[InlineData("/Author", true, true, "/UrlEncode[[Author]]/")]
[InlineData("/Author/", false, false, "/UrlEncode[[Author]]/")]
[InlineData("/Author/", false, true, "/UrlEncode[[Author/]]")]
[InlineData("/Author/", true, false, "/UrlEncode[[Author]]/")]
[InlineData("/Author/", true, true, "/UrlEncode[[Author/]]/")]
[InlineData("Author", false, false, "/UrlEncode[[Author]]")]
[InlineData("Author", false, true, "/UrlEncode[[Author]]")]
[InlineData("Author", true, false, "/UrlEncode[[Author]]/")]
[InlineData("Author", true, true, "/UrlEncode[[Author]]/")]
[InlineData("", false, false, "")]
[InlineData("", false, true, "")]
[InlineData("", true, false, "")]
[InlineData("", true, true, "")]
public void ToPathString(string url, bool appendTrailingSlash, bool encodeSlashes, string expected)
{
// Arrange
var urlTestEncoder = new UrlTestEncoder();
var uriBuilldingContext = new UriBuildingContext(urlTestEncoder);
uriBuilldingContext.AppendTrailingSlash = appendTrailingSlash;
// Act
uriBuilldingContext.Accept(url, encodeSlashes);
// Assert
Assert.Equal(expected, uriBuilldingContext.ToPathString().Value);
}
}
}

View File

@ -149,24 +149,5 @@ namespace Microsoft.AspNetCore.Routing
// Assert
Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/some%23-other-endpoint/In%3Fdex/?query=some%3Fquery#Fragment?", uri);
}
[Fact]
public void GetTemplateByName_CreatesTemplate()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint("some-endpoint/{p}", metadata: new[] { new EndpointNameMetadata("name1"), });
var endpoint2 = EndpointFactory.CreateRouteEndpoint("some#-other-endpoint/{p}", metadata: new[] { new EndpointNameMetadata("name2"), });
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
// Act
var template = linkGenerator.GetTemplateByName(endpointName: "name2");
// Assert
Assert.NotNull(template);
Assert.Collection(
Assert.IsType<DefaultLinkGenerationTemplate>(template).Endpoints.Cast<RouteEndpoint>().OrderBy(e => e.RoutePattern.RawText),
e => Assert.Same(endpoint2, e));
}
}
}

View File

@ -201,31 +201,5 @@ namespace Microsoft.AspNetCore.Routing
// Assert
Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/Home/Index/?query=some%3Fquery#Fragment?", uri);
}
[Fact]
public void GetTemplateByRouteValues_CreatesTemplate()
{
// Arrange
var endpoint1 = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id}",
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "In?dex", })) });
var endpoint2 = EndpointFactory.CreateRouteEndpoint(
"{controller}/{action}/{id?}",
metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "In?dex", })) });
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
// Act
var template = linkGenerator.GetTemplateByRouteValues(
routeName: null,
values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }));
// Assert
Assert.NotNull(template);
Assert.Collection(
Assert.IsType<DefaultLinkGenerationTemplate>(template).Endpoints.Cast<RouteEndpoint>().OrderBy(e => e.RoutePattern.RawText),
e => Assert.Same(endpoint2, e),
e => Assert.Same(endpoint1, e));
}
}
}