Abrufen absoluter URLs mit ASP.NET Core

80

In MVC 5 hatte ich die folgenden Erweiterungsmethoden, um absolute URLs anstelle relativer URLs zu generieren:

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

Was wäre das Äquivalent in ASP.NET Core?

  • UrlHelper.RequestContext existiert nicht mehr.
  • Sie können das nicht erreichen, HttpContextda es keine statische HttpContext.CurrentEigenschaft mehr gibt .

Soweit ich sehen kann, müssten Sie jetzt auch die HttpContextoder HttpRequestObjekte übergeben. Habe ich recht? Gibt es eine Möglichkeit, die aktuelle Anfrage zu erhalten?

Bin ich überhaupt auf dem richtigen Weg, sollte die Domain jetzt eine Umgebungsvariable sein, die einfach an die relative URL angehängt wird? Wäre dies ein besserer Ansatz?

Muhammad Rehan Saeed
quelle
1
Die absolute URL bekommen was?
im1dermike
@ im1dermike zBhttp://example.com/controller/action
Muhammad Rehan Saeed

Antworten:

74

Nach RC2 und 1.0 müssen Sie IHttpContextAccessorIhrer Erweiterungsklasse keine Erweiterungsklasse mehr hinzufügen. Es ist sofort in der IUrlHelperdurch dieurlhelper.ActionContext.HttpContext.Request . Sie würden dann eine Erweiterungsklasse erstellen, die der gleichen Idee folgt, jedoch einfacher ist, da keine Injektion erforderlich ist.

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

Lassen Sie die Details darüber, wie man es baut, und injizieren Sie den Zugang, falls sie für jemanden nützlich sind. Möglicherweise interessiert Sie auch nur die absolute URL der aktuellen Anfrage. In diesem Fall sehen Sie sich das Ende der Antwort an.


Sie können Ihre Erweiterungsklasse ändern, um die IHttpContextAccessorSchnittstelle zum Abrufen der zu verwenden HttpContext. Sobald Sie den Kontext haben, dann können Sie die bekommen HttpRequestInstanz aus HttpContext.Requestund verwenden seine Eigenschaften Scheme, Host, Protocolusw. , wie in:

string scheme = HttpContextAccessor.HttpContext.Request.Scheme;

Beispielsweise könnte es erforderlich sein, dass Ihre Klasse mit einem HttpContextAccessor konfiguriert wird:

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

    ....
}

Was können Sie in Ihrer StartupKlasse tun (Startup.cs-Datei):

public void Configure(IApplicationBuilder app)
{
    ...

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

    ...
}

Sie könnten wahrscheinlich verschiedene Möglichkeiten finden, um die IHttpContextAccessorin Ihrer Erweiterungsklasse zu erhalten, aber wenn Sie Ihre Methoden am Ende als Erweiterungsmethoden beibehalten möchten, müssen Sie die IHttpContextAccessorin Ihre statische Klasse einfügen. (Andernfalls benötigen Sie das IHttpContextals Argument bei jedem Aufruf)


Nur das absoluteUri der aktuellen Anfrage erhalten

Wenn Sie nur die absolute URL der aktuellen Anforderung abrufen möchten, können Sie die Erweiterungsmethoden GetDisplayUrloder GetEncodedUrldie UriHelperKlasse verwenden. (Was sich vom Ur L Helper unterscheidet)

GetDisplayUrl . Gibt die kombinierten Komponenten der Anforderungs-URL in einer vollständig nicht maskierten Form (mit Ausnahme des QueryString) zurück, die nur zur Anzeige geeignet ist. Dieses Format sollte nicht in HTTP-Headern oder anderen HTTP-Vorgängen verwendet werden.

GetEncodedUrl . Gibt die kombinierten Komponenten der Anforderungs-URL in einer vollständig maskierten Form zurück, die für die Verwendung in HTTP-Headern und anderen HTTP-Vorgängen geeignet ist.

Um sie zu verwenden:

  • Fügen Sie den Namespace ein Microsoft.AspNet.Http.Extensions.
  • Holen Sie sich die HttpContextInstanz. Es ist bereits in einigen Klassen verfügbar (z. B. in Rasiereransichten), in anderen müssen Sie möglicherweise eine injizieren, IHttpContextAccessorwie oben erläutert.
  • Dann benutze sie einfach wie in this.Context.Request.GetDisplayUrl()

Eine Alternative zu diesen Methoden wäre das manuelle Erstellen des absoluten Uri mithilfe der Werte im HttpContext.RequestObjekt (ähnlich wie beim RequireHttpsAttribute ):

var absoluteUri = string.Concat(
                        request.Scheme,
                        "://",
                        request.Host.ToUriComponent(),
                        request.PathBase.ToUriComponent(),
                        request.Path.ToUriComponent(),
                        request.QueryString.ToUriComponent());
Daniel JG
quelle
Wir sollten jetzt IUrlHelper anstelle von UrlHelper verwenden. Alle Objekte sind in MVC 6 viel stärker voneinander getrennt. Ich denke, Ihre Option ist die beste.
Muhammad Rehan Saeed
Funktioniert nicht mit RC1. View erzeugt einen Laufzeitfehler mit der Erweiterungsmethode. Auch der UriHelperLink ist tot.
Mrchief
1
@ Mrchief Ich habe den Link aktualisiert (die Namespaces für RC2 haben sich geändert, sodass alle Links zum Dev-Zweig tot sind ...). Ich habe jedoch gerade ein RC1-Projekt erstellt, @using Microsoft.AspNet.Http.Extensionsder Ansicht Index.cshtml hinzugefügt und konnte diese Erweiterungen wie in@Context.Request.GetDisplayUrl()
Daniel JG
43

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

Bonus-Tipp

Sie können eine nicht direkt IUrlHelperim DI-Container registrieren . IUrlHelperZum Auflösen einer Instanz von müssen Sie das IUrlHelperFactoryund verwenden IActionContextAccessor. Als Verknüpfung können Sie jedoch Folgendes tun:

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

ASP.NET Core Backlog

UPDATE : Dadurch wird ASP.NET Core 5 nicht erstellt

Es gibt Hinweise darauf, dass Sie LinkGeneratorabsolute URLs erstellen können, ohne eine angeben zu müssen HttpContext(Dies war der größte Nachteil LinkGeneratorund warum IUrlHelperdie Einrichtung mit der folgenden Lösung komplexer war, obwohl sie einfacher zu verwenden war). Siehe "Vereinfachen Sie die Konfiguration." ein Host / Schema für absolute URLs mit LinkGenerator " .

Muhammad Rehan Saeed
quelle
1
Würde das auch das tun, was ich brauche? Siehe stackoverflow.com/q/37928214/153923
jp2code
3
Das ist in Ordnung, aber es scheint mir ein Overkill zu sein, zu viel Code für etwas Einfaches. Könnten wir einfach bei bleibenstring url = string.Concat(this.Request.Scheme, "://", this.Request.Host, this.Request.Path, this.Request.QueryString);
Junior Mayhé
19

Sie müssen hierfür keine Erweiterungsmethode erstellen

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

  • Action - Name der Aktion
  • Controller - Name des Controllers
  • values - Objekt mit Routenwerten: auch bekannt als GET-Parameter

Es gibt auch viele andere Überladungen, mit denenUrl.Action Sie Links generieren können.

Kelly Elton
quelle
1
Vielen Dank! Das war genau das, was ich brauchte, aber ich muss nicht verstehen, was es ist this.Context.Request.Scheme. Erhält das nur die Protokoll- und Domänenteile der URL?
Lukas
this.Context.Request.SchemaGibt das Protokoll zurück, das für die Anforderung verwendet wurde. Es wird httpoder sein https. Hier sind die Dokumente, aber sie erklären nicht wirklich, was Schema bedeutet.
Kelly Elton
13

Wenn Sie einfach einen Uri für eine Methode mit einer Routenanmerkung benötigen, hat das Folgende für mich funktioniert.

Schritte

Relative URL abrufen

Wenn Sie den Routennamen der Zielaktion notieren, rufen Sie die relative URL mithilfe der URL- Eigenschaft des Controllers wie folgt ab:

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

Erstellen Sie eine absolute URL

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

Erstellen Sie einen neuen Uri

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

Beispiel

[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);
    }
}
Jon
quelle
9

Dies ist eine Variation der Antwort von Muhammad Rehan Saeed , wobei die Klasse parasitär an die vorhandene gleichnamige .net-Kern-MVC-Klasse gebunden wird, sodass alles einfach funktioniert.

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);
        }
    }
}
Josiah
quelle
3

In einem neuen Projekt MVC ASP.Net 5 in einer Controller - Aktion können Sie noch tun , this.Contextund this.Context.Requestes sieht aus wie auf dem Antrag gibt es nicht mehr eine Eigenschaft Url , aber die untergeordneten Eigenschaften (Schema, Host, etc.) sind alle auf dem Request - Objekt direkt.

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

        return View();
    }

Eher oder nicht möchten Sie dies verwenden. Kontext oder Injizieren der Eigenschaft ist eine weitere Konversation. Abhängigkeitsinjektion in ASP.NET vNext

ToddB
quelle
3

Wenn Sie nur einen relativen Pfad mit optionalen Parametern konvertieren möchten, habe ich eine Erweiterungsmethode für IHttpContextAccessor erstellt

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

Sie können die Methode dann von Ihrem Dienst / Ihrer Ansicht aus mit dem injizierten IHttpContextAccessor aufrufen

var callbackUrl = _httpContextAccessor.AbsoluteUrl("/Identity/Account/ConfirmEmail", new { userId = applicationUser.Id, code });
RadarBug
quelle
3

Ich habe gerade festgestellt, dass Sie dies mit diesem Aufruf tun können:

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

Dadurch werden das Schema, der Host, der Port und alles beibehalten.

pavlindrom
quelle
2

Sie können die URL wie folgt erhalten:

Request.Headers["Referer"]

Erläuterung

Das Request.UrlRefererwird einen System.UriFormatExceptionauslösen, wenn der HTTP-Header des Referers fehlerhaft ist (was passieren kann, da er normalerweise nicht unter Ihrer Kontrolle steht).

Wie für die Verwendung Request.ServerVariables, pro MSDN :

Request.ServerVariables-Sammlung

Die ServerVariables-Auflistung ruft die Werte vorgegebener Umgebungsvariablen ab und fordert Headerinformationen an.

Request.Headers-Eigenschaft

Ruft eine Sammlung von HTTP-Headern ab.

Ich glaube , ich verstehe nicht , warum Sie würden die lieber Request.ServerVariablesüber Request.Headers, daRequest.ServerVariables alle Umgebungsvariablen enthält sowie die Header, wo Request.Headers ist eine viel kürzere Liste , dass nur die Header enthält.

Die beste Lösung besteht also darin, die Request.HeadersSammlung zu verwenden, um den Wert direkt zu lesen. Beachten Sie jedoch die Warnungen von Microsoft bezüglich der HTML-Codierung des Werts, wenn Sie ihn in einem Formular anzeigen möchten.

Ricardo Rodriguez
quelle
Referer ist nicht zuverlässig, Browser sind nicht gezwungen, es zu senden. Mit anderen Worten, Benutzer können ihre Browser so konfigurieren, dass keine Verweise gesendet werden, z. B. als Sicherheitsmaßnahme.
Mikiqex