Modify UrlHelper to use a single StringBuilder
This commit is contained in:
parent
aa5a4d4af2
commit
e078259547
|
|
@ -19,9 +19,7 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
/// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and
|
/// Initializes a new instance of the <see cref="UrlHelper"/> class using the specified action context and
|
||||||
/// action selector.
|
/// action selector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actionContext">
|
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current request.</param>
|
||||||
/// The <see cref="Mvc.ActionContext"/> for the current request.
|
|
||||||
/// </param>
|
|
||||||
public UrlHelper(ActionContext actionContext)
|
public UrlHelper(ActionContext actionContext)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
if (actionContext == null)
|
||||||
|
|
@ -79,13 +77,8 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
valuesDictionary["controller"] = actionContext.Controller;
|
valuesDictionary["controller"] = actionContext.Controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = GeneratePathFromRoute(routeName: null, values: valuesDictionary);
|
var virtualPathData = GetVirtualPathData(routeName: null, values: valuesDictionary);
|
||||||
if (path == null)
|
return GenerateUrl(actionContext.Protocol, actionContext.Host, virtualPathData, actionContext.Fragment);
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateUrl(actionContext.Protocol, actionContext.Host, path, actionContext.Fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -110,61 +103,53 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
var valuesDictionary = new RouteValueDictionary(routeContext.Values);
|
var valuesDictionary = new RouteValueDictionary(routeContext.Values);
|
||||||
|
var virtualPathData = GetVirtualPathData(routeContext.RouteName, valuesDictionary);
|
||||||
var path = GeneratePathFromRoute(routeContext.RouteName, valuesDictionary);
|
return GenerateUrl(routeContext.Protocol, routeContext.Host, virtualPathData, routeContext.Fragment);
|
||||||
if (path == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateUrl(routeContext.Protocol, routeContext.Host, path, routeContext.Fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the absolute path of the url for the specified route values by
|
/// Gets the <see cref="VirtualPathData"/> for the specified route values by using the specified route name.
|
||||||
/// using the specified route name.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="routeName">The name of the route that is used to generate the URL.</param>
|
/// <param name="routeName">The name of the route that is used to generate the <see cref="VirtualPathData"/>.
|
||||||
|
/// </param>
|
||||||
/// <param name="values">A dictionary that contains the parameters for a route.</param>
|
/// <param name="values">A dictionary that contains the parameters for a route.</param>
|
||||||
/// <returns>The absolute path of the URL.</returns>
|
/// <returns>The <see cref="VirtualPathData"/>.</returns>
|
||||||
protected virtual string GeneratePathFromRoute(string routeName, RouteValueDictionary values)
|
protected virtual VirtualPathData GetVirtualPathData(string routeName, RouteValueDictionary values)
|
||||||
{
|
{
|
||||||
var context = new VirtualPathContext(HttpContext, AmbientValues, values, routeName);
|
var context = new VirtualPathContext(HttpContext, AmbientValues, values, routeName);
|
||||||
var pathData = Router.GetVirtualPath(context);
|
return Router.GetVirtualPath(context);
|
||||||
if (pathData == null)
|
}
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VirtualPathData.VirtualPath returns string.Empty for null.
|
// Internal for unit testing.
|
||||||
Debug.Assert(pathData.VirtualPath != null);
|
internal void AppendPathAndFragment(StringBuilder builder, VirtualPathData pathData, string fragment)
|
||||||
|
{
|
||||||
var pathBase = HttpContext.Request.PathBase;
|
var pathBase = HttpContext.Request.PathBase;
|
||||||
|
|
||||||
if (!pathBase.HasValue)
|
if (!pathBase.HasValue)
|
||||||
{
|
{
|
||||||
if (pathData.VirtualPath.Length == 0)
|
if (pathData.VirtualPath.Length == 0)
|
||||||
{
|
{
|
||||||
return "/";
|
builder.Append("/");
|
||||||
}
|
|
||||||
else if (!pathData.VirtualPath.StartsWith("/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return "/" + pathData.VirtualPath;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return pathData.VirtualPath;
|
if (!pathData.VirtualPath.StartsWith("/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
builder.Append("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(pathData.VirtualPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (pathData.VirtualPath.Length == 0)
|
if (pathData.VirtualPath.Length == 0)
|
||||||
{
|
{
|
||||||
return pathBase;
|
builder.Append(pathBase.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder(
|
builder.Append(pathBase.Value);
|
||||||
pathBase.Value,
|
|
||||||
pathBase.Value.Length + pathData.VirtualPath.Length);
|
|
||||||
|
|
||||||
if (pathBase.Value.EndsWith("/", StringComparison.Ordinal))
|
if (pathBase.Value.EndsWith("/", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
|
@ -177,10 +162,13 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Append(pathData.VirtualPath);
|
builder.Append(pathData.VirtualPath);
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(fragment))
|
||||||
|
{
|
||||||
|
builder.Append("#").Append(fragment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -213,34 +201,47 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateUrl(string protocol, string host, string path, string fragment)
|
/// <summary>
|
||||||
|
/// Generates the URL using the specified components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="protocol">The protocol.</param>
|
||||||
|
/// <param name="host">The host.</param>
|
||||||
|
/// <param name="pathData">The <see cref="VirtualPathData"/>.</param>
|
||||||
|
/// <param name="fragment">The URL fragment.</param>
|
||||||
|
/// <returns>The generated URL.</returns>
|
||||||
|
protected virtual string GenerateUrl(string protocol, string host, VirtualPathData pathData, string fragment)
|
||||||
{
|
{
|
||||||
Debug.Assert(path != null);
|
if (pathData == null)
|
||||||
var url = path;
|
|
||||||
if (!string.IsNullOrEmpty(fragment))
|
|
||||||
{
|
{
|
||||||
url += "#" + fragment;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VirtualPathData.VirtualPath returns string.Empty instead of null.
|
||||||
|
Debug.Assert(pathData.VirtualPath != null);
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
|
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
|
||||||
{
|
{
|
||||||
// We're returning a partial url (just path + query + fragment), but we still want it
|
AppendPathAndFragment(builder, pathData, fragment);
|
||||||
// to be rooted.
|
// We're returning a partial URL (just path + query + fragment), but we still want it to be rooted.
|
||||||
if (!url.StartsWith("/", StringComparison.Ordinal))
|
if (builder.Length == 0 || builder[0] != '/')
|
||||||
{
|
{
|
||||||
url = "/" + url;
|
builder.Insert(0, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
|
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
|
||||||
host = string.IsNullOrEmpty(host) ? HttpContext.Request.Host.Value : host;
|
builder.Append(protocol);
|
||||||
|
|
||||||
url = protocol + "://" + host + url;
|
builder.Append("://");
|
||||||
return url;
|
|
||||||
|
host = string.IsNullOrEmpty(host) ? HttpContext.Request.Host.Value : host;
|
||||||
|
builder.Append(host);
|
||||||
|
AppendPathAndFragment(builder, pathData, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
|
|
@ -892,25 +893,50 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(GeneratePathFromRoute_HandlesLeadingAndTrailingSlashesData))]
|
[MemberData(nameof(GeneratePathFromRoute_HandlesLeadingAndTrailingSlashesData))]
|
||||||
public void GeneratePathFromRoute_HandlesLeadingAndTrailingSlashes(
|
public void AppendPathAndFragment_HandlesLeadingAndTrailingSlashes(
|
||||||
string appBase,
|
string appBase,
|
||||||
string virtualPath,
|
string virtualPath,
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
// Arrage
|
// Arrage
|
||||||
var router = new Mock<IRouter>();
|
var router = Mock.Of<IRouter>();
|
||||||
router.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
var pathData = new VirtualPathData(router, virtualPath)
|
||||||
.Returns(new VirtualPathData(router.Object, virtualPath)
|
{
|
||||||
{
|
VirtualPath = virtualPath
|
||||||
VirtualPath = virtualPath
|
};
|
||||||
});
|
var urlHelper = CreateUrlHelper(appBase, router);
|
||||||
var urlHelper = CreateUrlHelper(appBase, router.Object);
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = urlHelper.GeneratePathFromRoutePublic("some-name", new RouteValueDictionary());
|
urlHelper.AppendPathAndFragment(builder, pathData, string.Empty);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, result);
|
Assert.Equal(expected, builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(GeneratePathFromRoute_HandlesLeadingAndTrailingSlashesData))]
|
||||||
|
public void AppendPathAndFragment_AppendsFragments(
|
||||||
|
string appBase,
|
||||||
|
string virtualPath,
|
||||||
|
string expected)
|
||||||
|
{
|
||||||
|
// Arrage
|
||||||
|
var fragmentValue = "fragment-value";
|
||||||
|
expected += $"#{fragmentValue}";
|
||||||
|
var router = Mock.Of<IRouter>();
|
||||||
|
var pathData = new VirtualPathData(router, virtualPath)
|
||||||
|
{
|
||||||
|
VirtualPath = virtualPath
|
||||||
|
};
|
||||||
|
var urlHelper = CreateUrlHelper(appBase, router);
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
urlHelper.AppendPathAndFragment(builder, pathData, fragmentValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, builder.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpContext CreateHttpContext(
|
private static HttpContext CreateHttpContext(
|
||||||
|
|
@ -939,13 +965,13 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
return new ActionContext(context, routeData, new ActionDescriptor());
|
return new ActionContext(context, routeData, new ActionDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestableUrlHelper CreateUrlHelper()
|
private static UrlHelper CreateUrlHelper()
|
||||||
{
|
{
|
||||||
var services = CreateServices();
|
var services = CreateServices();
|
||||||
var context = CreateHttpContext(services, string.Empty);
|
var context = CreateHttpContext(services, string.Empty);
|
||||||
var actionContext = CreateActionContext(context);
|
var actionContext = CreateActionContext(context);
|
||||||
|
|
||||||
return new TestableUrlHelper(actionContext);
|
return new UrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UrlHelper CreateUrlHelper(ActionContext context)
|
private static UrlHelper CreateUrlHelper(ActionContext context)
|
||||||
|
|
@ -964,7 +990,7 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
return new UrlHelper(actionContext);
|
return new UrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestableUrlHelper CreateUrlHelper(string host, string protocol, IRouter router)
|
private static UrlHelper CreateUrlHelper(string host, string protocol, IRouter router)
|
||||||
{
|
{
|
||||||
var services = CreateServices();
|
var services = CreateServices();
|
||||||
var context = CreateHttpContext(services, string.Empty);
|
var context = CreateHttpContext(services, string.Empty);
|
||||||
|
|
@ -973,19 +999,19 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
|
|
||||||
var actionContext = CreateActionContext(context, router);
|
var actionContext = CreateActionContext(context, router);
|
||||||
|
|
||||||
return new TestableUrlHelper(actionContext);
|
return new UrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestableUrlHelper CreateUrlHelper(string appBase, IRouter router)
|
private static UrlHelper CreateUrlHelper(string appBase, IRouter router)
|
||||||
{
|
{
|
||||||
var services = CreateServices();
|
var services = CreateServices();
|
||||||
var context = CreateHttpContext(services, appBase);
|
var context = CreateHttpContext(services, appBase);
|
||||||
var actionContext = CreateActionContext(context, router);
|
var actionContext = CreateActionContext(context, router);
|
||||||
|
|
||||||
return new TestableUrlHelper(actionContext);
|
return new UrlHelper(actionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TestableUrlHelper CreateUrlHelperWithRouteCollection(
|
private static UrlHelper CreateUrlHelperWithRouteCollection(
|
||||||
IServiceProvider services,
|
IServiceProvider services,
|
||||||
string appPrefix)
|
string appPrefix)
|
||||||
{
|
{
|
||||||
|
|
@ -1066,16 +1092,5 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestableUrlHelper : UrlHelper
|
|
||||||
{
|
|
||||||
public TestableUrlHelper(ActionContext context)
|
|
||||||
: base(context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GeneratePathFromRoutePublic(string routeName, RouteValueDictionary values) =>
|
|
||||||
GeneratePathFromRoute(routeName, values);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue