Accedi all'attuale HttpContext in ASP.NET Core


132

Ho bisogno di accedere alla corrente HttpContextin un metodo statico o un servizio di utilità.

Con ASP.NET classico MVC e System.Web, vorrei solo usare HttpContext.Currentper accedere al contesto staticamente. Ma come posso farlo in ASP.NET Core?

Risposte:


149

HttpContext.Currentnon esiste più in ASP.NET Core ma c'è un nuovo IHttpContextAccessorche puoi iniettare nelle tue dipendenze e usare per recuperare l'attuale HttpContext:

public class MyComponent : IMyComponent
{
    private readonly IHttpContextAccessor _contextAccessor;

    public MyComponent(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public string GetDataFromSession()
    {
        return _contextAccessor.HttpContext.Session.GetString(*KEY*);
    }
}

3
Buon punto! Vale anche la pena ricordare che IHttpContextAccessorsarebbe disponibile solo in luoghi in cui il contenitore DI sta risolvendo l'istanza.
Tugberk,

6
@tugberk bene, in teoria, si potrebbe anche utilizzare il CallContextServiceLocatorper risolvere un servizio, anche da un'istanza non-DI-iniettato: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). In pratica, è una cosa grandiosa se puoi evitarlo :)
Kévin Chalet

17
Non usare CallContextServiceLocator
davidfowl

9
@davidfowl a meno che tu non abbia un valido motivo tecnico (a parte "statics are evil", ovviamente), scommetto che le persone lo useranno se non hanno altra scelta.
Kévin Chalet

7
Certo, le persone raramente hanno un valido motivo tecnico. È più facile usare una statica e chi se ne frega della testabilità :)
davidfowl

35

Necromancing.
SÌ PUOI Consiglio
segreto per coloro che migrano in grandegiunchepezzi (sospiro, scivolo freudiano) di codice.
Il seguente metodo è un brutto carbonchio di un hack che è attivamente impegnato nello svolgimento del lavoro espresso di satana (agli occhi degli sviluppatori del framework .NET Core), ma funziona :

Nel public class Startup

aggiungi una proprietà

public IConfigurationRoot Configuration { get; }

E quindi aggiungere un singolo IHttpContextAccessor a DI in 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>();

Quindi in Configura

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

aggiungi il parametro DI IServiceProvider svp, quindi il metodo è simile al seguente:

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

Quindi, creare una classe di sostituzione per 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 


}

Ora in Configura, dove è stato aggiunto IServiceProvider svp, salvare questo fornitore di servizi nella variabile statica "ServiceProvider" nella classe fittizia appena creata System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)

e imposta HostingEnvironment.IsHosted su true

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

questo è essenzialmente ciò che ha fatto System.Web, solo che non l'hai mai visto (suppongo che la variabile sia stata dichiarata come interna anziché pubblica).

// 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

    });

Come nei moduli Web ASP.NET, otterrai un NullReference quando stai tentando di accedere a un HttpContext quando non ce n'è, come in passato era presente Application_Startin global.asax.

Sottolineo di nuovo, questo funziona solo se hai effettivamente aggiunto

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

come ho scritto dovresti.
Benvenuto nel modello ServiceLocator all'interno del modello DI;)
Per rischi ed effetti collaterali, chiedi al tuo medico o farmacista residente o studia le fonti di .NET Core su github.com/aspnet e fai dei test.


Forse un metodo più mantenibile sarebbe l'aggiunta di questa classe di supporto

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


    }


}

E quindi chiamare HttpContext.Configure in Avvio-> Configura

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

37
QUESTO È PURO MALE
Art

2
La versione con il metodo helper funziona correttamente in ogni scenario? Stai pensando a multithreading, asincrono e con servizi in container IoC con vita diversa?
Tamas Molnar,

7
So che tutti dobbiamo fare del nostro meglio per sottolineare quanto diabolicamente diabolico sia ... Ma se stavi portando un enorme progetto su Core, dove HttpContext. Current era usato in alcune classi statiche difficili da raggiungere. Questo sarebbe probabilmente abbastanza utile. Ecco, l'ho detto.
Brian MacKay,

2
Questo è puro male ... e appropriato che lo implementerò ad Halloween. Adoro DI e IoC ... ma ho a che fare con un'app legacy con Classi statiche malvagie con variabili statiche malvagie, che dobbiamo spingere usando Kestrel e cercare di iniettare HttpContext sarebbe semplicemente annullabile per noi, senza rompere tutto.
House of Dexter,

2
Ya, questa è la risposta corretta per MIGRAZIONI. ;)
Tom Stickel

23

Solo per aggiungere alle altre risposte ...

In ASP.NET Core 2.1, c'è il AddHttpContextAccessormetodo di estensione , che registrerà IHttpContextAccessorcon la durata corretta:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();

    // Other code...
}

2
Sono contento di vedere un'alternativa più ufficiale al carbonchio satanico!
Ken Lyon,

@Ken Lyon:;) khellang: Singleton è la vita corretta. Scoped sarebbe sbagliato. O almeno al momento della stesura, era così. Ma tanto meglio se AddHttpContextAccessor lo fa correttamente senza che abbiamo bisogno di un riferimento per la specifica versione del framework.
Stefan Steiger,

Puoi per favore condividere un esempio?
Toolkit

@Toolkit Aggiunto un codice di esempio. Non sono sicuro del valore che fornisce sul testo sopra, però.
Khellang

22

Il modo più legittimo che mi è venuto in mente è stato iniettando IHttpContextAccessor nella tua implementazione statica come segue:

public static class HttpHelper
{
     private static IHttpContextAccessor _accessor;
     public static void Configure(IHttpContextAccessor httpContextAccessor)
     {
          _accessor = httpContextAccessor;
     }

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Quindi l'assegnazione di IHttpContextAccessor nella configurazione di avvio dovrebbe fare il lavoro.

HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());

Suppongo che dovresti anche registrare il servizio singleton:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Bellissimo. Proprio quello che il medico ha ordinato!
ShrapNull,

5

Secondo questo articolo: Accesso a HttpContext al di fuori dei componenti del framework in ASP.NET Core

namespace System.Web
{
    public static class HttpContext
    {
        private static IHttpContextAccessor _contextAccessor;

        public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;

        internal static void Configure(IHttpContextAccessor contextAccessor)
        {
            _contextAccessor = contextAccessor;
        }
    }
}

Poi:

public static class StaticHttpContextExtensions
{
    public static void AddHttpContextAccessor(this IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
    {
        var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
        System.Web.HttpContext.Configure(httpContextAccessor);
        return app;
    }
}

Poi:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseStaticHttpContext();
        app.UseMvc();
    }
}

Puoi usarlo in questo modo:

using System.Web;

public class MyService
{
   public void DoWork()
   {
    var context = HttpContext.Current;
    // continue with context instance
   }
}

2

All'avvio

services.AddHttpContextAccessor();

Nel controller

public class HomeController : Controller
    {
        private readonly IHttpContextAccessor _context;

        public HomeController(IHttpContextAccessor context)
        {
            _context = context; 
        }
        public IActionResult Index()
        {
           var context = _context.HttpContext.Request.Headers.ToList();
           return View();
        }
   }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.