aspnetcore/src/Microsoft.AspNet.Mvc.Core/UrlHelper.cs

171 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Routing;
namespace Microsoft.AspNet.Mvc
{
public class UrlHelper : IUrlHelper
{
private readonly HttpContext _httpContext;
private readonly IRouter _router;
private readonly IDictionary<string, object> _ambientValues;
private readonly IActionSelector _actionSelector;
public UrlHelper(IContextAccessor<ActionContext> contextAccessor, IActionSelector actionSelector)
{
_httpContext = contextAccessor.Value.HttpContext;
_router = contextAccessor.Value.Router;
_ambientValues = contextAccessor.Value.RouteValues;
_actionSelector = actionSelector;
}
public string Action(string action, string controller, object values, string protocol, string host, string fragment)
{
var valuesDictionary = TypeHelper.ObjectToDictionary(values);
if (action != null)
{
valuesDictionary["action"] = action;
}
if (controller != null)
{
valuesDictionary["controller"] = controller;
}
var context = new VirtualPathContext(_httpContext, _ambientValues, valuesDictionary);
var actions = _actionSelector.GetCandidateActions(context);
var actionCandidate = actions.FirstOrDefault();
if (actionCandidate == null)
{
return null;
}
foreach (var constraint in actionCandidate.RouteConstraints)
{
if (constraint.KeyHandling == RouteKeyHandling.DenyKey &&
_ambientValues.ContainsKey(constraint.RouteKey))
{
valuesDictionary[constraint.RouteKey] = null;
}
}
var path = GeneratePathFromRoute(valuesDictionary);
if (path == null)
{
return null;
}
return GenerateUrl(protocol, host, path, fragment);
}
public bool IsLocalUrl(string url)
{
return
!string.IsNullOrEmpty(url) &&
// Allows "/" or "/foo" but not "//" or "/\".
((url[0] == '/' && (url.Length == 1 || (url[1] != '/' && url[1] != '\\'))) ||
// Allows "~/" or "~/foo".
(url.Length > 1 && url[0] == '~' && url[1] == '/'));
}
public string RouteUrl(string routeName, object values, string protocol, string host, string fragment)
{
var valuesDictionary = TypeHelper.ObjectToDictionary(values);
var path = GeneratePathFromRoute(routeName, valuesDictionary);
if (path == null)
{
return null;
}
return GenerateUrl(protocol, host, path, fragment);
}
private string GeneratePathFromRoute(IDictionary<string, object> values)
{
return GeneratePathFromRoute(routeName: null, values: values);
}
private string GeneratePathFromRoute(string routeName, IDictionary<string, object> values)
{
var context = new VirtualPathContext(_httpContext, _ambientValues, values, routeName);
var path = _router.GetVirtualPath(context);
if (path == null)
{
return null;
}
// See Routing Issue#31
if (path.Length > 0 && path[0] != '/')
{
path = "/" + path;
}
var fullPath = _httpContext.Request.PathBase.Add(new PathString(path)).Value;
if (fullPath.Length == 0)
{
return "/";
}
else
{
return fullPath;
}
}
public string Content([NotNull] string contentPath)
{
return GenerateClientUrl(_httpContext.Request.PathBase, contentPath);
}
private static string GenerateClientUrl([NotNull] PathString applicationPath,
[NotNull] string path)
{
if (path.StartsWith("~/", StringComparison.Ordinal))
{
var segment = new PathString(path.Substring(1));
return applicationPath.Add(segment).Value;
}
return path;
}
private string GenerateUrl(string protocol, string host, string path, string fragment)
{
// We should have a robust and centrallized version of this code. See HttpAbstractions#28
Contract.Assert(path != null);
var url = path;
if (!string.IsNullOrEmpty(fragment))
{
url += "#" + fragment;
}
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
{
// We're returning a partial url (just path + query + fragment), but we still want it
// to be rooted.
if (!url.StartsWith("/", StringComparison.Ordinal))
{
url = "/" + url;
}
return url;
}
else
{
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
host = string.IsNullOrEmpty(host) ? _httpContext.Request.Host.Value : host;
url = protocol + "://" + host + url;
return url;
}
}
}
}