Greifen Sie in ASP.NET Core auf den aktuellen HttpContext zu

132

Ich muss auf den Strom HttpContextin einer statischen Methode oder einem Dienstprogramm zugreifen .

Mit klassischem ASP.NET MVC und System.Webwürde ich nur verwenden HttpContext.Current, um statisch auf den Kontext zuzugreifen. Aber wie mache ich das in ASP.NET Core?

Maxswitcher
quelle

Antworten:

149

HttpContext.Currentexistiert nicht mehr in ASP.NET Core, aber es gibt eine neue IHttpContextAccessor, die Sie in Ihre Abhängigkeiten einfügen und zum Abrufen der aktuellen verwenden können HttpContext:

public class MyComponent : IMyComponent
{
    private readonly IHttpContextAccessor _contextAccessor;

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

    public string GetDataFromSession()
    {
        return _contextAccessor.HttpContext.Session.GetString(*KEY*);
    }
}
Kévin Chalet
quelle
3
Guter Punkt! Erwähnenswert ist auch, dass IHttpContextAccessordies nur an Stellen verfügbar ist, an denen der DI-Container die Instanz auflöst.
Tugberk
6
@tugberk Nun, theoretisch könnten Sie das auch verwenden CallContextServiceLocator, um einen Dienst aufzulösen, selbst von einer Instanz ohne DI-Injektion : CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). In der Praxis ist es eine großartige Sache, wenn Sie es vermeiden können :)
Kévin Chalet
17
Verwenden Sie nicht CallContextServiceLocator
Davidfowl
9
@davidfowl es sei denn, Sie haben einen gültigen technischen Grund (abgesehen von "Statik ist böse" natürlich), ich wette, die Leute werden es verwenden, wenn sie keine andere Wahl haben.
Kévin Chalet
7
Sicher, Menschen haben selten einen gültigen technischen Grund. Es ist eher so, als ob es einfacher ist, eine Statik zu verwenden und wer sich um die Testbarkeit kümmert :)
Davidfowl
35

Nekromantie.
JA, SIE KÖNNEN
Geheimtipp für diejenigen, die groß migrierenJunksBrocken (Seufzer, Freudscher Ausrutscher) des Codes.
Die folgende Methode ist ein böser Karbunkel eines Hacks, der aktiv an der Ausführung der Expressarbeit von Satan beteiligt ist (in den Augen von .NET Core-Framework-Entwicklern), aber sie funktioniert :

Im public class Startup

Fügen Sie eine Eigenschaft hinzu

public IConfigurationRoot Configuration { get; }

Fügen Sie dann DI in ConfigureServices einen Singleton-IHttpContextAccessor hinzu.

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

Dann in Konfigurieren

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

Fügen Sie den DI-Parameter hinzu IServiceProvider svp, damit die Methode wie folgt aussieht:

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

Erstellen Sie als Nächstes eine Ersatzklasse für 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 


}

IServiceProvider svpSpeichern Sie nun in Configure, wo Sie den hinzugefügt haben , diesen Dienstanbieter in der statischen Variablen "ServiceProvider" in der gerade erstellten Dummy-Klasse System.Web.HttpContext (System.Web.HttpContext.ServiceProvider).

und setzen Sie HostingEnvironment.IsHosted auf true

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

Dies ist im Wesentlichen das, was System.Web getan hat, nur dass Sie es nie gesehen haben (ich denke, die Variable wurde als intern statt öffentlich deklariert).

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

    });

Wie in ASP.NET Web-Forms erhalten Sie eine NullReference, wenn Sie versuchen, auf einen HttpContext zuzugreifen, wenn keiner vorhanden ist, wie er früher Application_Startin global.asax enthalten war.

Ich betone noch einmal, das funktioniert nur, wenn Sie tatsächlich hinzugefügt haben

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

wie ich dir geschrieben habe solltest du.
Willkommen beim ServiceLocator-Muster innerhalb des DI-Musters;)
Fragen Sie Ihren Arzt oder Apotheker nach Risiken und Nebenwirkungen - oder untersuchen Sie die Quellen von .NET Core unter github.com/aspnet und führen Sie einige Tests durch.


Vielleicht wäre eine wartbarere Methode das Hinzufügen dieser Hilfsklasse

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


    }


}

Rufen Sie dann HttpContext.Configure unter Startup-> Configure auf

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>()
    );
Stefan Steiger
quelle
37
DAS IST REINES BÖSE
Art
2
Funktioniert die Version mit der Hilfsmethode in jedem Szenario ordnungsgemäß? Denken Sie an Multithreading, Async und Services in IoC-Containern mit unterschiedlicher Lebensdauer?
Tamas Molnar
7
Ich weiß, wir müssen uns alle Mühe geben, um darauf hinzuweisen, wie teuflisch teuflisch das ist ... Aber wenn Sie ein riesiges Projekt auf Core portieren, wo HttpContext.Current in einigen schwer erreichbaren statischen Klassen verwendet wurde. Dies wäre wahrscheinlich sehr nützlich. Dort habe ich es gesagt.
Brian MacKay
2
Das ist rein böse ... und angemessen, dass ich es an Halloween umsetzen werde. Ich liebe DI und IoC ... aber ich habe es mit einer Legacy-App mit bösen statischen Klassen mit bösen statischen Variablen zu tun, die wir mit Kestrel vorantreiben müssen, und der Versuch, HttpContext zu injizieren, wäre für uns einfach nicht rückgängig zu machen, ohne alles zu beschädigen.
House of Dexter
2
Ja, das ist die richtige Antwort für MIGRATIONEN. ;)
Tom Stickel
23

Nur um die anderen Antworten zu ergänzen ...

In ASP.NET Core 2.1 gibt es die AddHttpContextAccessorErweiterungsmethode , IHttpContextAccessormit der die mit der richtigen Lebensdauer registriert wird :

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

    // Other code...
}
Khellang
quelle
2
Ich bin froh, eine offiziellere Alternative zum satanischen Karbunkel zu sehen!
Ken Lyon
@ Ken Lyon :;) khellang: Singleton ist die richtige Lebensdauer. Scoped wäre falsch. Oder zumindest zum Zeitpunkt des Schreibens war das so. Umso besser, wenn AddHttpContextAccessor dies korrekt ausführt, ohne dass wir eine Referenz für die jeweilige Framework-Version benötigen.
Stefan Steiger
Können Sie bitte ein Beispiel nennen?
Toolkit
@Toolkit Beispielcode hinzugefügt. Ich bin mir jedoch nicht sicher, welchen Wert es über dem obigen Text bietet.
Khellang
22

Der legitimste Weg, den ich gefunden habe, war das Einfügen von IHttpContextAccessor in Ihre statische Implementierung wie folgt:

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

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Das Zuweisen des IHttpContextAccessor in der Startkonfiguration sollte dann den Job erledigen.

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

Ich denke, Sie sollten auch den Dienst Singleton registrieren müssen:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Jan.
quelle
Wunderschönen. Genau das, was der Arzt bestellt hat!
ShrapNull
5

Gemäß diesem Artikel: Zugriff auf HttpContext außerhalb von Framework-Komponenten 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;
        }
    }
}

Dann:

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

Dann:

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

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

Sie können es so verwenden:

using System.Web;

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

Im Start

services.AddHttpContextAccessor();

Im 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();
        }
   }
Diana Tereshko
quelle