web-dev-qa-db-fra.com

Obtention d'URL absolues avec ASP.NET Core

Dans MVC 5, j'avais les méthodes d'extension suivantes pour générer des URL absolues, au lieu de celles relatives:

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

Quel serait l'équivalent dans ASP.NET Core?

  • UrlHelper.RequestContext n'existe plus.
  • Vous ne pouvez pas vous procurer HttpContext car il n’existe plus de propriété statique HttpContext.Current.

Autant que je sache, vous auriez maintenant besoin que les objets HttpContext ou HttpRequest soient également transmis. Ai-je raison? Y a-t-il un moyen de mettre la main sur la demande actuelle?

Si je suis même sur la bonne voie, le domaine devrait-il maintenant être une variable d'environnement, qui est simplement ajoutée à l'URL relative? Serait-ce une meilleure approche?

48

Pour ASP.NET Core 1.0 et versions ultérieures

Vous pouvez utiliser le code ci-dessous ou utiliser le package Boxed.AspNetCore NuGet ou consulter le code dans le référentiel Dotnet-Boxed/Framework GitHub.

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

Astuce Bonus

Vous ne pouvez pas enregistrer directement une IUrlHelper dans le conteneur DI. La résolution d'une instance de IUrlHelper nécessite que vous utilisiez IUrlHelperFactory et IActionContextAccessor. Toutefois, vous pouvez effectuer les opérations suivantes sous forme de raccourci:

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

Après RC2 et 1.0, vous n'avez plus besoin d'injecter une IHttpContextAccessor dans votre classe d'extension. Il est immédiatement disponible dans la IUrlHelper au urlhelper.ActionContext.HttpContext.Request. Vous créeriez alors une classe d'extension en suivant la même idée, mais plus simplement, car aucune injection ne serait impliquée.

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

Laissant les détails sur la façon de le construire en injectant l’accès au cas où ils seraient utiles à quelqu'un. Vous pouvez également vous intéresser à l'URL absolue de la demande en cours, auquel cas, jetez un coup d'œil à la fin de la réponse.


Vous pouvez modifier votre classe d'extension pour utiliser l'interface IHttpContextAccessor afin d'obtenir le HttpContext . Une fois que vous avez le contexte, vous pouvez obtenir l'instance HttpRequest de HttpContext.Request et utiliser ses propriétés Scheme, Host, Protocol etc comme dans:

string scheme = HttpContextAccessor.HttpContext.Request.Scheme;

Par exemple, vous pouvez exiger que votre classe soit configurée avec un 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);
    }

    ....
}

Ce que vous pouvez faire dans votre classe Startup (fichier Startup.cs):

public void Configure(IApplicationBuilder app)
{
    ...

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

    ...
}

Vous pourriez probablement trouver différentes façons d’obtenir la IHttpContextAccessor dans votre classe d’extension, mais si vous souhaitez conserver vos méthodes en tant que méthodes d’extension, vous devrez injecter la IHttpContextAccessor dans votre classe statique. (Sinon, vous aurez besoin de la IHttpContext comme argument à chaque appel)


Juste en train de récupérer l'absolaireUri de la requête en cours

Si vous voulez simplement obtenir l'URI absolu de la requête en cours, vous pouvez utiliser les méthodes d'extension GetDisplayUrl ou GetEncodedUrl de la classe UriHelper . (Ce qui est différent de l'aide urL)

GetDisplayUrl. Renvoie les composants combinés de l'URL de la demande sous une forme entièrement non échappée (à l'exception de QueryString) appropriée uniquement pour l'affichage. Ce format ne doit pas être utilisé dans les en-têtes HTTP ou autres Opérations HTTP.

GetEncodedUrl. Renvoie les composants combinés de l'URL de la demande sous une forme entièrement échappée, utilisable dans les en-têtes HTTP et autres Opérations HTTP.

Pour les utiliser:

  • Incluez l'espace de noms Microsoft.AspNet.Http.Extensions
  • Obtenez l'instance HttpContext. Il est déjà disponible dans certaines classes (comme les vues rasoir), mais dans d'autres, vous devrez peut-être injecter une IHttpContextAccessor comme expliqué ci-dessus. 
  • Ensuite, utilisez-les simplement comme dans this.Context.Request.GetDisplayUrl()

Une alternative à ces méthodes serait de vous fabriquer manuellement l'URI absolu en utilisant les valeurs de l'objet HttpContext.Request (similaire à ce que fait le RequireHttpsAttribute ):

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

Si vous voulez simplement un Uri pour une méthode qui a une annotation de route, les éléments suivants ont fonctionné pour moi.

Pas

Obtenir l'URL relative

En notant le nom de route de l'action cible, obtenez l'URL relative à l'aide de la propriété URL du contrôleur comme suit:

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

Créer une URL absolue

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

Créer un nouvel Uri

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

Exemple

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

Ceci est une variante de la réponse de Muhammad Rehan Saeed , la classe se liant de manière parasite à la classe MVC de noyau .net existante du même nom, de sorte que tout fonctionne correctement.

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

Dans un nouveau projet ASP.Net 5 MVC dans une action de contrôleur, vous pouvez toujours exécuter this.Context et this.Context.Request. Il semble que dans la demande, il n'y a plus de propriété Url, mais que les propriétés enfants (schéma, hôte, etc.) se trouvent toutes dans l'objet de la requête directement. 

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

        return View();
    }

Vous préférez ou non utiliser this.Context ou injecter la propriété est une autre conversation . Injection de dépendance dans ASP.NET vNext

2
ToddB

Vous pouvez obtenir l'URL comme ceci:

Request.Headers["Referer"]

Explication

Le Request.UrlReferer lancera un System.UriFormatException si l'en-tête HTTP du référent est mal formé (ce qui peut arriver car il n'est généralement pas sous votre contrôle).

Quant à l’utilisation de Request.ServerVariables, par MSDN :

Collection Request.ServerVariables

La collection ServerVariables récupère les valeurs de variables d'environnement prédéterminées et les informations d'en-tête de demande.

Propriété Request.Headers

Obtient une collection d'en-têtes HTTP.

Je suppose que je ne comprends pas pourquoi vous préféreriez le Request.ServerVariables au Request.Headers, puisque Request.ServerVariables contient toutes les variables d'environnement ainsi que les en-têtes, où Request.Headers est une liste beaucoup plus courte qui ne contient que les en-têtes.

La meilleure solution consiste donc à utiliser la collection Request.Headers pour lire directement la valeur. Tenez compte des avertissements de Microsoft concernant le codage HTML de la valeur si vous souhaitez l’afficher sur un formulaire.

1
Ricardo Rodriguez

Si vous voulez simplement convertir un chemin relatif avec des paramètres optionnels, j'ai créé une méthode d'extension pour 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);
}

Vous pouvez ensuite appeler la méthode à partir de votre service/vue à l’aide du paramètre injecté IHttpContextAccessor  

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