Отримання абсолютних 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, напр.http://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аргумент для кожного виклику)


Просто отримуємо абсолютний Uri поточного запиту

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

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, тому всі ці посилання на гілку розробника мертві ...). Однак я щойно створив проект 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

ОНОВЛЕННЯ : Це не зробить 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);
Junior Mayhé

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

Це різновид анзауера Мухаммеда Рехана Саїда , при цьому клас паразитно приєднується до існуючого однойменного класу .net основного MVC, так що все просто працює.

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

Скоріше чи ні ви хочете використовувати це. Контекст або ін'єкція властивості - це інша розмова. Ін’єкція залежності в ASP.NET vNext


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

2

Ви можете отримати URL-адресу таким чином:

Request.Headers["Referer"]

Пояснення

Request.UrlRefererКидатиме , System.UriFormatExceptionякщо реферер HTTP заголовок неправильний формат (що може статися , так як це зазвичай не під вашим контролем).

Що стосується використання Request.ServerVariables, відповідно до MSDN :

Request.ServerVariables Collection

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

Запит. Власність керівників

Отримує колекцію заголовків HTTP.

Здається, я не розумію, чому ти віддав би перевагу Request.ServerVariablesнад Request.Headers, оскількиRequest.ServerVariables містить усі змінні середовища, а також заголовки, де Request.Headers - це набагато коротший список, який містить лише заголовки.

Тож найкращим рішенням є використання Request.Headersколекції для безпосереднього зчитування значення. Однак дотримуйтесь попереджень Microsoft щодо кодування HTML значення, якщо ви збираєтеся відображати його у формі.


Referer не є надійним, браузери не змушені його надсилати. Іншими словами, користувачі можуть налаштувати свої браузери не надсилати реферера, наприклад, як засіб безпеки.
mikiqex
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.