Получение абсолютных URL-адресов с помощью ASP.NET Core

82

В MVC 5 у меня были следующие методы расширения для генерации абсолютных URL-адресов вместо относительных:

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?

  • UrlHelper.RequestContext более не существует.
  • Вы не можете получить объект, так HttpContextкак больше не существует статического HttpContext.Currentсвойства.

Насколько я понимаю, теперь вам также потребуется передать объекты HttpContextили HttpRequest. Я прав? Есть ли способ получить текущий запрос?

Я даже на правильном пути, должен ли домен теперь быть переменной среды, которая просто добавляется к относительному URL-адресу? Было бы это лучше?

Мухаммад Рехан Саид
источник
1
Получение абсолютного URL-адреса что?
im1dermike
@ im1dermike eghttp://example.com/controller/action
Мухаммад Рехан Саид

Ответы:

74

После RC2 и 1.0 вам больше не нужно вводить IHttpContextAccessorкласс расширения. Он сразу же доступен в IUrlHelperформате urlhelper.ActionContext.HttpContext.Request. Затем вы должны создать класс расширения, следуя той же идее, но более простой, поскольку не будет задействована инъекция.

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-адрес текущего запроса, и в этом случае посмотрите в конец ответа.


Вы можете изменить свой класс расширения, чтобы использовать IHttpContextAccessorинтерфейс для получения HttpContext. Если у вас есть контекст, то вы можете получить HttpRequestэкземпляр из HttpContext.Requestи использовать его свойство Scheme, Host, и Protocolт.д. , как в:

string scheme = HttpContextAccessor.HttpContext.Request.Scheme;

Например, вы можете потребовать, чтобы ваш класс был настроен с 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классе (файл Startup.cs):

public void Configure(IApplicationBuilder app)
{
    ...

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

    ...
}

Вероятно, вы могли бы придумать разные способы получения IHttpContextAccessorв своем классе расширения, но если вы хотите сохранить свои методы как методы расширения, в конце вам нужно будет ввести IHttpContextAccessorв свой статический класс. (В противном случае вам понадобится в IHttpContextкачестве аргумента при каждом вызове)


Просто получаю absoluteUri текущего запроса

Если вы просто хотите получить абсолютный uri текущего запроса, вы можете использовать методы расширения GetDisplayUrlили GetEncodedUrlиз UriHelperкласса. (Который отличается от помощника Ur L )

GetDisplayUrl . Возвращает объединенные компоненты URL-адреса запроса в полностью неэкранированной форме (за исключением QueryString), подходящей только для отображения. Этот формат не следует использовать в заголовках HTTP или других операциях HTTP.

GetEncodedUrl . Возвращает объединенные компоненты URL-адреса запроса в полностью экранированной форме, подходящей для использования в заголовках HTTP и других операциях HTTP.

Чтобы их использовать:

  • Включите пространство имен Microsoft.AspNet.Http.Extensions.
  • Получите HttpContextэкземпляр. Он уже доступен в некоторых классах (например, в представлениях бритвы), но в других вам может потребоваться ввести, IHttpContextAccessorкак описано выше.
  • Тогда просто используйте их как в this.Context.Request.GetDisplayUrl()

Альтернативой этим методам было бы вручную создать абсолютный uri, используя значения в HttpContext.Requestобъекте (аналогично тому, что делает RequireHttpsAttribute ):

var absoluteUri = string.Concat(
                        request.Scheme,
                        "://",
                        request.Host.ToUriComponent(),
                        request.PathBase.ToUriComponent(),
                        request.Path.ToUriComponent(),
                        request.QueryString.ToUriComponent());
Дэниел Дж.
источник
Теперь мы должны использовать IUrlHelper, а не UrlHelper. В MVC 6 все объекты намного более разъединены. Я думаю, что ваш вариант - лучший.
Мухаммад Рехан Саид
Не работает с RC1. Просмотр вызывает ошибку времени выполнения с методом расширения. Также UriHelperссылка мертва.
Mrchief
2
@Mrchief Я обновил ссылку (пространства имен изменились для RC2, поэтому все ссылки на ветку dev мертвы ...). Однако я только что создал проект RC1, добавил @using Microsoft.AspNet.Http.Extensionsв представление Index.cshtml и смог использовать эти расширения, как в@Context.Request.GetDisplayUrl()
Daniel JG
44

Для ASP.NET Core 1.0 и более поздних версий

/// <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);
    }
}

Бонусный совет

Вы не можете напрямую зарегистрировать объект IUrlHelperв контейнере DI. Для разрешения экземпляра IUrlHelperтребуется использовать клавиши IUrlHelperFactoryи IActionContextAccessor. Однако вы можете сделать следующее в качестве ярлыка:

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

Журнал невыполненных работ ASP.NET Core

ОБНОВЛЕНИЕ : это не сделает ASP.NET Core 5

Есть признаки того, что вы сможете использовать LinkGeneratorдля создания абсолютных URL без необходимости указывать HttpContext(это был самый большой недостаток LinkGeneratorи почему, IUrlHelperхотя более сложную настройку с использованием решения ниже было проще использовать) См. "Упростить настройку хост / схема для абсолютных URL с LinkGenerator " .

Мухаммад Рехан Саид
источник
1
Будет ли это делать то, что мне нужно? См stackoverflow.com/q/37928214/153923
jp2code
4
Это нормально, но мне кажется излишним, слишком много кода для чего-то простого. Можем ли мы просто придерживатьсяstring url = string.Concat(this.Request.Scheme, "://", this.Request.Host, this.Request.Path, this.Request.QueryString);
Джуниор Мэйхе
19

Для этого не нужно создавать метод расширения

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

  • Action - Название акции
  • Controller - Имя контроллера
  • values - Объект, содержащий значения маршрута: параметры GET

Есть также множество других перегрузок, которыеUrl.Action вы можете использовать для создания ссылок.

Келли Элтон
источник
1
Благодаря! Это было именно то, что мне нужно, но я не понимаю, что это такое this.Context.Request.Scheme. Это только часть протокола и домена URL-адреса?
Лукас
this.Context.Request.Schemaвозвращает протокол, который использовался для запроса. Будет httpили https. Вот документы, но на самом деле они не объясняют, что означает схема.
Келли Элтон
14

Если вам просто нужен Uri для метода с аннотацией маршрута, у меня сработало следующее.

Шаги

Получить относительный URL

Отметив имя маршрута целевого действия, получите относительный 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);
    }
}
Джон
источник
9

Это вариант ответа Мухаммада Рехана Саида , в котором класс паразитически присоединяется к существующему основному классу MVC .net с тем же именем, так что все просто работает.

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);
        }
    }
}
Иосия
источник
5

Я только что обнаружил, что это можно сделать с помощью этого вызова:

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

Это будет поддерживать схему, хост, порт, все.

павлиндром
источник
3

В новом проекте ASP.Net 5 MVC в действии контроллера вы все еще можете выполнять, this.Contextи this.Context.Requestпохоже, что в запросе больше нет свойства Url, но все дочерние свойства (схема, хост и т.д.) находятся непосредственно в объекте запроса.

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

        return View();
    }

Скорее всего, вы хотите использовать this.Context или внедрить свойство - это еще один разговор. Внедрение зависимостей в ASP.NET vNext

ToddB
источник
3

Если вы просто хотите преобразовать относительный путь с дополнительными параметрами, я создал метод расширения для 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

var callbackUrl = _httpContextAccessor.AbsoluteUrl("/Identity/Account/ConfirmEmail", new { userId = applicationUser.Id, code });
RadarBug
источник
2

Вы можете получить такой URL-адрес:

Request.Headers["Referer"]

Объяснение

Request.UrlRefererБудет бросать , System.UriFormatExceptionесли реферер HTTP заголовок неправильный формат (что может произойти , так как это обычно не под вашим контролем).

Что касается использования Request.ServerVariables, согласно MSDN :

Коллекция Request.ServerVariables

Коллекция ServerVariables извлекает значения предопределенных переменных среды и информацию заголовка запроса.

Request.Headers Свойство

Получает коллекцию заголовков HTTP.

Думаю, я не понимаю, почему вы предпочли бы Request.ServerVariablesover Request.Headers, поскольку он Request.ServerVariablesсодержит все переменные среды, а также заголовки, где Request.Headers - гораздо более короткий список, который содержит только заголовки.

Поэтому лучшее решение - использовать Request.Headersколлекцию для непосредственного чтения значения. Обратите внимание на предупреждения Microsoft о кодировке значения HTML, если вы собираетесь отображать его в форме.

Рикардо Родригес
источник
Referer ненадежен, браузеры не обязаны его отправлять. Другими словами, пользователи могут настроить свои браузеры так, чтобы они не отправляли ссылку, например, в качестве меры безопасности.
mikiqex