300 votes

Comment obtenir HttpContext.Current dans ASP.NET Core ?

Nous sommes en train de réécrire/convertir notre application ASP.NET WebForms en utilisant ASP.NET Core. Nous essayons d'éviter autant que possible la réingénierie.

Il y a une section où nous utilisons HttpContext dans une bibliothèque de classes pour vérifier l'état actuel. Comment puis-je accéder à HttpContext.Current dans .NET Core 1.0 ?

 var current = HttpContext.Current;
     if (current == null)
      {
       // do something here
       // string connection = Configuration.GetConnectionString("MyDb");
      }

J'ai besoin d'y accéder afin de construire l'hôte de l'application actuelle.

$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";

3 votes

Demandez-vous d'abord pourquoi vous avez besoin d'y accéder à partir d'une bibliothèque de classes. Dans 80% des cas, il n'y a pas de raison et c'est une mauvaise conception d'essayer de le faire. Dans votre cas, ce n'est pas le cas. Changez la conception de votre bibliothèque de classes, passez les paramètres dont vous avez besoin plutôt que d'y accéder à partir d'elle. Moins de couplage, un code plus facile à maintenir. Regardez dans IOptions<T>

0 votes

@Tseng disons que je veux le passer. Alors qu'est-ce que je suis censé passer ? C'est toute la question ici. Quel est l'espace de nom ou l'objet qui est équivalent à HttpContext dans ASP.NET Core ?

0 votes

Je lis votre question, et je me rends compte que ce n'est pas la question et qu'elle ne dit presque rien. Avec si peu d'informations, il est difficile de fournir des réponses. Ensuite, lisez mon premier commentaire pour trouver la bonne direction.

399voto

Nate Barbettini Points 26922

En règle générale, la conversion d'une application Web Forms ou MVC5 en ASP.NET Core exigera une quantité importante de refactoring.

HttpContext.Current a été supprimé dans ASP.NET Core. Accéder au contexte HTTP actuel à partir d'une bibliothèque de classes distincte est le type d'architecture désordonnée qu'ASP.NET Core tente d'éviter. Il existe plusieurs façons de réorganiser cette architecture dans ASP.NET Core.

Propriété HttpContext

Vous pouvez accéder au contexte HTTP actuel via la fonction HttpContext sur n'importe quel contrôleur. La chose la plus proche de votre exemple de code original serait de passer HttpContext dans la méthode que vous appelez :

public class HomeController : Controller
{
    public IActionResult Index()
    {
        MyMethod(HttpContext);

        // Other code
    }
}

public void MyMethod(Microsoft.AspNetCore.Http.HttpContext context)
{
    var host = $"{context.Request.Scheme}://{context.Request.Host}";

    // Other code
}

Paramètre HttpContext dans le middleware

Si vous écrivez intergiciel personnalisé pour le pipeline ASP.NET Core, l'adresse de la requête courante HttpContext est transmis à votre Invoke automatiquement :

public Task Invoke(HttpContext context)
{
    // Do something with the current HTTP context...
}

Accesseur de contexte HTTP

Enfin, vous pouvez utiliser le IHttpContextAccessor pour obtenir le contexte HTTP dans toute classe gérée par le système d'injection de dépendances d'ASP.NET Core. Ceci est utile lorsque vous avez un service commun qui est utilisé par vos contrôleurs.

Demandez cette interface dans votre constructeur :

public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

Vous pouvez alors accéder au contexte HTTP actuel de manière sûre :

var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...

IHttpContextAccessor n'est pas toujours ajouté par défaut au conteneur de services, il faut donc l'enregistrer dans le fichier ConfigureServices juste pour être sûr :

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}

8 votes

Vous avez oublié IHttpContextAccessor mais depuis la RC2, il n'est pas enregistré par défaut car il entraîne une surcharge par requête. Voir l'annonce github.com/aspnet/Announcements/issues/190

0 votes

@Tseng D'accord, c'est aussi possible avec IHttpContextAccessor . Je vais mettre à jour ma réponse.

10 votes

Est-ce que c'est normal que ce soit Singleton ? Devrait-on plutôt dire AddScoped ?

77voto

Quandary Points 12867

Nécromancie.
OUI, vous pouvez, et voici comment.
Un conseil secret pour les grands migrateurs jonques des morceaux de code :
La méthode suivante est un piratage maléfique qui s'emploie activement à réaliser l'œuvre expresse de Satan (aux yeux des développeurs du cadre .NET Core), mais ça marche :

Sur public class Startup

ajouter une propriété

public IConfigurationRoot Configuration { get; }

Et ensuite ajouter un singleton IHttpContextAccessor à DI dans ConfigureServices.

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

Ensuite, dans Configurer

    public void Configure(
              IApplicationBuilder app
             ,IHostingEnvironment env
             ,ILoggerFactory loggerFactory
    )
    {

ajouter le paramètre DI IServiceProvider svp La méthode ressemble donc à ceci :

    public void Configure(
           IApplicationBuilder app
          ,IHostingEnvironment env
          ,ILoggerFactory loggerFactory
          ,IServiceProvider svp)
    {

Ensuite, créez une classe de remplacement pour System.Web :

namespace System.Web
{

    namespace Hosting
    {
        public static class HostingEnvironment 
        {
            public static bool m_IsHosted;

            static HostingEnvironment()
            {
                m_IsHosted = false;
            }

            public static bool IsHosted
            {
                get
                {
                    return m_IsHosted;
                }
            }
        }
    }

    public static class HttpContext
    {
        public static IServiceProvider ServiceProvider;

        static HttpContext()
        { }

        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                // var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
                object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));

                // Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
                Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
                // context.Response.WriteAsync("Test");

                return context;
            }
        }

    } // End Class HttpContext 

}

Maintenant, dans Configure, où vous avez ajouté le IServiceProvider svp enregistrez ce fournisseur de services dans la variable statique "ServiceProvider" de la classe fictive System.Web.HttpContext (System.Web.HttpContext.ServiceProvider) que vous venez de créer.

et définissez HostingEnvironment.IsHosted comme étant vrai.

System.Web.Hosting.HostingEnvironment.m_IsHosted = true;

c'est essentiellement ce que System.Web a fait, mais vous ne l'avez jamais vu (je suppose que la variable a été déclarée comme interne au lieu de publique).

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    ServiceProvider = svp;
    System.Web.HttpContext.ServiceProvider = svp;
    System.Web.Hosting.HostingEnvironment.m_IsHosted = true;

    app.UseCookieAuthentication(new CookieAuthenticationOptions()
    {
        AuthenticationScheme = "MyCookieMiddlewareInstance",
        LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
        AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest

       , CookieHttpOnly=false

    });

Comme dans ASP.NET Web-Forms, vous obtiendrez une NullReference lorsque vous essayez d'accéder à un HttpContext alors qu'il n'y en a pas, comme c'était le cas auparavant dans Application_Start dans global.asax.

J'insiste encore une fois, cela ne fonctionne que si vous avez réellement ajouté

services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

comme je l'ai écrit, tu devrais.
Bienvenue au modèle ServiceLocator dans le modèle DI ;)
Pour connaître les risques et les effets secondaires, consultez votre médecin traitant ou votre pharmacien - ou étudiez les sources de .NET Core à l'adresse suivante github.com/aspnet et faire quelques tests.


Peut-être qu'une méthode plus facile à maintenir serait d'ajouter cette classe auxiliaire

namespace System.Web
{

    public static class HttpContext
    {
        private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;

        public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
        {
            m_httpContextAccessor = httpContextAccessor;
        }

        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                return m_httpContextAccessor.HttpContext;
            }
        }

    }

}

Et ensuite appeler HttpContext.Configure dans Startup->Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    System.Web.HttpContext.Configure(app.ApplicationServices.
        GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
    );

22 votes

Vous parlez de "junks" plutôt que de "chunks" de code. Est-ce un lapsus freudien ? :)

1 votes

@John : Haha, elle est bonne. Oui, je suppose que oui ;)

11 votes

C'est le travail d'un génie du mal. +1

15voto

HLS Points 176

Il existe une solution à ce problème si vous avez vraiment besoin d'un accès statique au contexte actuel. Dans Startup.Configure( .)

app.Use(async (httpContext, next) =>
{
    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    {
        await next();
    }
    finally
    {
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    }
});

Et quand vous en avez besoin, vous pouvez l'obtenir avec :

HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;

J'espère que cela vous aidera. Gardez à l'esprit que cette solution de contournement s'applique lorsque vous n'avez pas le choix. La meilleure pratique consiste à utiliser l'injection de dépendances.

0 votes

Cela ne fonctionne pas pour moi, devrais-je être en mesure d'utiliser cette ligne dans une bibliothèque de classe référencée ? HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext ; context est juste mis à null quand je fais

0 votes

Vous pouvez l'utiliser dans une bibliothèque. Mais je pense que vous avez utilisé la classe System.Web.HttpContext, ce n'est pas la même classe que le contexte http d'aspnet core (Microsoft.AspNetCore.Http.HttpContext).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X