使用 ASP.NET Core 获取绝对 URL

Getting absolute URLs using ASP.NET Core

在 MVC 5 中,我有以下扩展方法来生成绝对 URLs,而不是相对的:

public static class UrlHelperExtensions
{
    public static string AbsoluteAction(
        this UrlHelper url,
        string actionName, 
        string controllerName, 
        object routeValues = null)
    {
        string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;
        return url.Action(actionName, controllerName, routeValues, scheme);
    }

    public static string AbsoluteContent(
        this UrlHelper url,
        string contentPath)
    {
        return new Uri(url.RequestContext.HttpContext.Request.Url, url.Content(contentPath)).ToString();
    }

    public static string AbsoluteRouteUrl(
        this UrlHelper url,
        string routeName,
        object routeValues = null)
    {
        string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;
        return url.RouteUrl(routeName, routeValues, scheme);
    }
}

ASP.NET Core 中的等价物是什么?

据我所知,您现在还需要传入 HttpContextHttpRequest 对象。我对吗?有什么方法可以获取当前请求吗?

我的方向是否正确,域现在应该是一个环境变量,它简单地附加到相对 URL 上吗?这是更好的方法吗?

在控制器操作中的新 ASP.Net 5 MVC 项目中,您仍然可以执行 this.Contextthis.Context.Request 看起来在请求中不再有 Url 属性 但子属性(模式、主机等)都直接在请求对象上。

 public IActionResult About()
    {
        ViewBag.Message = "Your application description page.";
        var schema = this.Context.Request.Scheme;

        return View();
    }

你是否想使用 this.Context 或注入 属性 是另一个对话。 Dependency Injection in ASP.NET vNext

在 RC2 和 1.0 之后,您不再需要向扩展 class 注入 IHttpContextAccessor。它在 IUrlHelperurlhelper.ActionContext.HttpContext.Request 中立即可用。然后,您将按照相同的想法创建一个扩展 class,但更简单,因为不会涉及注入。

public static string AbsoluteAction(
    this IUrlHelper url,
    string actionName, 
    string controllerName, 
    object routeValues = null)
{
    string scheme = url.ActionContext.HttpContext.Request.Scheme;
    return url.Action(actionName, controllerName, routeValues, scheme);
}

留下有关如何构建它的详细信息,以防它们对某人有用。您可能还只对当前请求的绝对 url 感兴趣,在这种情况下请查看答案的末尾。


您可以修改您的扩展 class 以使用 HttpContext.Request 中的 IHttpContextAccessor interface to get the HttpContext. Once you have the context, then you can get the HttpRequest 实例并使用其属性 SchemeHostProtocol 等,如:

string scheme = HttpContextAccessor.HttpContext.Request.Scheme;

例如,您可能需要为 class 配置 HttpContextAccessor:

public static class UrlHelperExtensions
{        
    private static IHttpContextAccessor HttpContextAccessor;
    public static void Configure(IHttpContextAccessor httpContextAccessor)
    {           
        HttpContextAccessor = httpContextAccessor;  
    }

    public static string AbsoluteAction(
        this IUrlHelper url,
        string actionName, 
        string controllerName, 
        object routeValues = null)
    {
        string scheme = HttpContextAccessor.HttpContext.Request.Scheme;
        return url.Action(actionName, controllerName, routeValues, scheme);
    }

    ....
}

您可以在 Startup class(Startup.cs 文件)上执行以下操作:

public void Configure(IApplicationBuilder app)
{
    ...

    var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    UrlHelperExtensions.Configure(httpContextAccessor);

    ...
}

您可能会想出不同的方法在您的扩展 class 中获取 IHttpContextAccessor,但是如果您希望最终将您的方法保留为扩展方法,您将需要注入IHttpContextAccessor 变成你的静态 class。 (否则您将需要 IHttpContext 作为每次调用的参数)


只是获取当前请求的absoluteUri

如果只想获取当前请求的绝对uri,可以使用UriHelperclass中的扩展方法GetDisplayUrlGetEncodedUrl。 (与 UrLHelper 不同)

GetDisplayUrl. Returns the combined components of the request URL in a fully un-escaped form (except for the QueryString) suitable only for display. This format should not be used in HTTP headers or other HTTP operations.

GetEncodedUrl. Returns the combined components of the request URL in a fully escaped form suitable for use in HTTP headers and other HTTP operations.

为了使用它们:

  • 包括命名空间 Microsoft.AspNet.Http.Extensions
  • 获取HttpContext实例。它已经在一些 classes 中可用(如剃刀视图),但在其他人中你可能需要注入一个 IHttpContextAccessor 如上所述。
  • 然后像 this.Context.Request.GetDisplayUrl()
  • 一样使用它们

这些方法的替代方法是使用 HttpContext.Request 对象中的值手动制作绝对 uri(与 RequireHttpsAttribute 所做的类似):

var absoluteUri = string.Concat(
                        request.Scheme,
                        "://",
                        request.Host.ToUriComponent(),
                        request.PathBase.ToUriComponent(),
                        request.Path.ToUriComponent(),
                        request.QueryString.ToUriComponent());

如果您只是想要一个具有路由注释的方法的 Uri,以下方法对我有用。

步骤

获取相对 URL

注意目标动作的Route名称,使用controller的URL属性获取相对URL如下:

var routeUrl = Url.RouteUrl("*Route Name Here*", new { *Route parameters here* });

创建绝对URL

var absUrl = string.Format("{0}://{1}{2}", Request.Scheme,
            Request.Host, routeUrl);

创建一个新的 Uri

var uri = new Uri(absUrl, UriKind.Absolute)

示例

[Produces("application/json")]
[Route("api/Children")]
public class ChildrenController : Controller
{
    private readonly ApplicationDbContext _context;

    public ChildrenController(ApplicationDbContext context)
    {
        _context = context;
    }

    // GET: api/Children
    [HttpGet]
    public IEnumerable<Child> GetChild()
    {
        return _context.Child;
    }

    [HttpGet("uris")]
    public IEnumerable<Uri> GetChildUris()
    {
        return from c in _context.Child
               select
                   new Uri(
                       $"{Request.Scheme}://{Request.Host}{Url.RouteUrl("GetChildRoute", new { id = c.ChildId })}",
                       UriKind.Absolute);
    }


    // GET: api/Children/5
    [HttpGet("{id}", Name = "GetChildRoute")]
    public IActionResult GetChild([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return HttpBadRequest(ModelState);
        }

        Child child = _context.Child.Single(m => m.ChildId == id);

        if (child == null)
        {
            return HttpNotFound();
        }

        return Ok(child);
    }
}

你可以这样得到url:

Request.Headers["Referer"]

说明

如果引用 HTTP header 格式错误(这可能发生,因为它通常不受您的控制),Request.UrlReferer 将抛出 System.UriFormatException

至于使用Request.ServerVariables,per MSDN:

Request.ServerVariables Collection

The ServerVariables collection retrieves the values of predetermined environment variables and request header information.

Request.Headers 属性

获取 collection 的 HTTP headers。

我想我不明白为什么您更喜欢 Request.ServerVariables 而不是 Request.Headers,因为 Request.ServerVariables 包含所有环境变量以及 header s,其中 Request.Headers 是一个更短的列表,仅包含 headers.

所以最好的解决办法是使用Request.Headers collection直接读取值。不过,如果要在表单上显示值,请注意 Microsoft 关于 HTML 对值进行编码的警告。

对于 ASP.NET Core 1.0 Onwards

/// <summary>
/// <see cref="IUrlHelper"/> extension methods.
/// </summary>
public static class UrlHelperExtensions
{
    /// <summary>
    /// Generates a fully qualified URL to an action method by using the specified action name, controller name and
    /// route values.
    /// </summary>
    /// <param name="url">The URL helper.</param>
    /// <param name="actionName">The name of the action method.</param>
    /// <param name="controllerName">The name of the controller.</param>
    /// <param name="routeValues">The route values.</param>
    /// <returns>The absolute URL.</returns>
    public static string AbsoluteAction(
        this IUrlHelper url,
        string actionName,
        string controllerName,
        object routeValues = null)
    {
        return url.Action(actionName, controllerName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
    }

    /// <summary>
    /// Generates a fully qualified URL to the specified content by using the specified content path. Converts a
    /// virtual (relative) path to an application absolute path.
    /// </summary>
    /// <param name="url">The URL helper.</param>
    /// <param name="contentPath">The content path.</param>
    /// <returns>The absolute URL.</returns>
    public static string AbsoluteContent(
        this IUrlHelper url,
        string contentPath)
    {
        HttpRequest request = url.ActionContext.HttpContext.Request;
        return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString();
    }

    /// <summary>
    /// Generates a fully qualified URL to the specified route by using the route name and route values.
    /// </summary>
    /// <param name="url">The URL helper.</param>
    /// <param name="routeName">Name of the route.</param>
    /// <param name="routeValues">The route values.</param>
    /// <returns>The absolute URL.</returns>
    public static string AbsoluteRouteUrl(
        this IUrlHelper url,
        string routeName,
        object routeValues = null)
    {
        return url.RouteUrl(routeName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
    }
}

奖金提示

您不能直接在 DI 容器中注册一个 IUrlHelper。解析 IUrlHelper 的实例需要您使用 IUrlHelperFactoryIActionContextAccessor。但是,您可以执行以下操作作为快捷方式:

services
    .AddSingleton<IActionContextAccessor, ActionContextAccessor>()
    .AddScoped<IUrlHelper>(x => x
        .GetRequiredService<IUrlHelperFactory>()
        .GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));

ASP.NET 核心积压

更新:这不会使 ASP.NET Core 5

有迹象表明,您可以使用 LinkGenerator 创建绝对 URL,而无需提供 HttpContext(这是 LinkGenerator 的最大缺点,为什么 IUrlHelper 尽管使用下面的解决方案设置起来更复杂但更易于使用)参见 "Make it easy to configure a host/scheme for absolute URLs with LinkGenerator"

这是 Muhammad Rehan Saeed 的 anwser 的变体,class 寄生地附加到同名的现有 .net 核心 MVC class,因此一切都只是有效。

namespace Microsoft.AspNetCore.Mvc
{
    /// <summary>
    /// <see cref="IUrlHelper"/> extension methods.
    /// </summary>
    public static partial class UrlHelperExtensions
    {
        /// <summary>
        /// Generates a fully qualified URL to an action method by using the specified action name, controller name and
        /// route values.
        /// </summary>
        /// <param name="url">The URL helper.</param>
        /// <param name="actionName">The name of the action method.</param>
        /// <param name="controllerName">The name of the controller.</param>
        /// <param name="routeValues">The route values.</param>
        /// <returns>The absolute URL.</returns>
        public static string AbsoluteAction(
            this IUrlHelper url,
            string actionName,
            string controllerName,
            object routeValues = null)
        {
            return url.Action(actionName, controllerName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
        }

        /// <summary>
        /// Generates a fully qualified URL to the specified content by using the specified content path. Converts a
        /// virtual (relative) path to an application absolute path.
        /// </summary>
        /// <param name="url">The URL helper.</param>
        /// <param name="contentPath">The content path.</param>
        /// <returns>The absolute URL.</returns>
        public static string AbsoluteContent(
            this IUrlHelper url,
            string contentPath)
        {
            HttpRequest request = url.ActionContext.HttpContext.Request;
            return new Uri(new Uri(request.Scheme + "://" + request.Host.Value), url.Content(contentPath)).ToString();
        }

        /// <summary>
        /// Generates a fully qualified URL to the specified route by using the route name and route values.
        /// </summary>
        /// <param name="url">The URL helper.</param>
        /// <param name="routeName">Name of the route.</param>
        /// <param name="routeValues">The route values.</param>
        /// <returns>The absolute URL.</returns>
        public static string AbsoluteRouteUrl(
            this IUrlHelper url,
            string routeName,
            object routeValues = null)
        {
            return url.RouteUrl(routeName, routeValues, url.ActionContext.HttpContext.Request.Scheme);
        }
    }
}

如果您只想转换带有可选参数的相对路径,我为 IHttpContextAccessor

创建了一个扩展方法
public static string AbsoluteUrl(this IHttpContextAccessor httpContextAccessor, string relativeUrl, object parameters = null)
{
    var request = httpContextAccessor.HttpContext.Request;

    var url = new Uri(new Uri($"{request.Scheme}://{request.Host.Value}"), relativeUrl).ToString();

    if (parameters != null)
    {
        url = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, ToDictionary(parameters));
    }

    return url;
}


private static Dictionary<string, string> ToDictionary(object obj)
{
    var json = JsonConvert.SerializeObject(obj);
    return JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}

然后您可以使用注入的 IHttpContextAccessor

从您的 service/view 调用方法
var callbackUrl = _httpContextAccessor.AbsoluteUrl("/Identity/Account/ConfirmEmail", new { userId = applicationUser.Id, code });

您不需要为此创建扩展方法

@Url.Action("Action", "Controller", values: null);

  • Action - 动作名称
  • Controller - 控制器名称
  • values - 包含路由值的对象:又名 GET 参数

还有 lots of other overloads to Url.Action 可以用来生成链接。

我刚刚发现你可以通过这个电话来做到这一点:

Url.Action(new UrlActionContext
{
    Protocol = Request.Scheme,
    Host = Request.Host.Value,
    Action = "Action"
})

这将维护方案、主机、端口等所有内容。

ASP.NET Core 3.0 及更高版本已经为 IUrlHelper 提供了 ActionLink and PageLink 扩展方法,其目的是分别为操作和页面生成绝对 URL。

在控制器操作或页面中,可以通过 Url 属性:

访问 IUrlHelper 实例
public IActionResult Index()
{
    string absoluteActionUrl = Url.ActionLink("ActionName", "ControllerName");
    string absolutePageUrl = Url.PageLink("/Page");
    ...
}