Akses HttpContext saat ini di ASP.NET Core

132

Saya perlu mengakses arus HttpContextdalam metode statis atau layanan utilitas.

Dengan ASP.NET MVC klasik dan System.Web, saya hanya akan menggunakan HttpContext.Currentuntuk mengakses konteks secara statis. Tetapi bagaimana saya melakukan ini di ASP.NET Core?

maxswitcher
sumber

Jawaban:

149

HttpContext.Currenttidak ada lagi di ASP.NET Core tetapi ada yang baru IHttpContextAccessoryang dapat Anda sisipkan dalam dependensi dan digunakan untuk mengambil yang sekarang 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
sumber
3
Poin bagus! Perlu juga disebutkan bahwa IHttpContextAccessorhanya akan tersedia di tempat-tempat di mana wadah DI sedang menyelesaikan instance.
tugberk
6
@tugberk baik, dalam teori, Anda bisa juga menggunakan CallContextServiceLocatoruntuk menyelesaikan layanan, bahkan dari contoh non-DI-disuntikkan: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). Dalam praktiknya, ini adalah hal yang hebat jika Anda bisa menghindarinya :)
Kévin Chalet
17
Jangan gunakan CallContextServiceLocator
davidfowl
9
@davidfowl kecuali Anda memiliki alasan teknis yang valid (selain dari 'statika itu jahat', tentu saja), saya yakin orang akan menggunakannya jika mereka tidak punya pilihan lain.
Kévin Chalet
7
Tentu, orang jarang memiliki alasan teknis yang valid. Ini lebih seperti lebih mudah menggunakan statis dan siapa yang peduli tentang testability :)
davidfowl
35

Necromancing.
YA ANDA BISA
Tip rahasia untuk mereka yang bermigrasi besarjungpotongan (sigh, Freudian slip) dari kode.
Metode berikut ini adalah serangan jahat dari peretasan yang secara aktif terlibat dalam melaksanakan pekerjaan ekspres setan (di mata pengembang kerangka .NET Core), tetapi berfungsi :

Di public class Startup

tambahkan properti

public IConfigurationRoot Configuration { get; }

Dan kemudian tambahkan IHttpContextAccessor tunggal ke DI di 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>();

Kemudian di Configure

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

tambahkan Parameter DI IServiceProvider svp, sehingga metodenya seperti:

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

Selanjutnya, buat kelas pengganti untuk 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 


}

Sekarang di Konfigurasi, di mana Anda menambahkan IServiceProvider svp, simpan penyedia layanan ini ke variabel statis "ServiceProvider" di System.Web.HttpContext kelas baru saja dibuat (System.Web.HttpContext.ServiceProvider)

dan atur HostingEnvironment.Hosted ke true

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

ini pada dasarnya apa yang dilakukan System.Web, hanya saja Anda tidak pernah melihatnya (saya kira variabel tersebut dinyatakan sebagai internal, bukan publik).

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

    });

Seperti di ASP.NET Web-Forms, Anda akan mendapatkan NullReference ketika Anda mencoba mengakses HttpContext ketika tidak ada, seperti dulu Application_Startdi global.asax.

Saya tekankan lagi, ini hanya berfungsi jika Anda benar-benar menambahkan

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

seperti yang saya tulis Anda harus.
Selamat datang di pola ServiceLocator dalam pola DI;)
Untuk risiko dan efek samping, tanyakan dokter atau apoteker Anda - atau pelajari sumber .NET Core di github.com/aspnet , dan lakukan beberapa pengujian.


Mungkin metode yang lebih bisa dipertahankan adalah menambahkan kelas pembantu ini

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


    }


}

Dan kemudian memanggil HttpContext.Configure di 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>()
    );
Stefan Steiger
sumber
37
INI MURNI JAHAT
Seni
2
Apakah versi dengan metode helper berfungsi dengan baik di setiap skenario. Berpikir multithreading, async dan dengan layanan dalam wadah IoC dengan masa pakai yang berbeda?
Tamas Molnar
7
Saya tahu kita semua harus keluar dari cara kita untuk menunjukkan betapa jahatnya iblis ini ... Tapi jika Anda mengirim proyek besar ke Core, di mana HttpContext.Current digunakan dalam beberapa kelas statis yang sulit dijangkau .. Ini mungkin akan sangat berguna. Di sana, saya mengatakannya.
Brian MacKay
2
Ini adalah kejahatan murni ... dan pantas jika saya akan menerapkannya pada Halloween. Saya suka DI dan IoC ... tapi saya sedang berhadapan dengan aplikasi warisan dengan Kelas Statis jahat dengan variabel Statis jahat, yang perlu kita dorong menggunakan Kestrel dan mencoba menyuntikkan HttpContext hanya akan tidak dapat dihancurkan untuk kita, tanpa merusak semuanya.
House of Dexter
2
Ya, ini jawaban yang tepat untuk MIGRATIONS. ;)
Tom Stickel
23

Hanya untuk menambah jawaban lain ...

Dalam ASP.NET Inti 2.1, ada yang AddHttpContextAccessormetode ekstensi , yang akan mendaftarkan IHttpContextAccessordengan seumur hidup yang benar:

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

    // Other code...
}
khellang
sumber
2
Senang melihat alternatif yang lebih resmi daripada Suncic Carbuncle!
Ken Lyon
@ Ken Lyon:;) khellang: Singleton adalah masa hidup yang benar. Scoped akan salah. Atau paling tidak pada saat penulisan, begitulah. Tapi semua lebih baik jika AddHttpContextAccessor melakukannya dengan benar tanpa kita membutuhkan referensi untuk versi kerangka kerja tertentu.
Stefan Steiger
Bisakah Anda berbagi contoh?
Toolkit
@Toolkit Menambahkan beberapa kode contoh. Namun, tidak yakin nilai apa yang diberikan di atas teks di atas.
khellang
22

Cara paling sah yang saya lakukan adalah dengan menyuntikkan IHttpContextAccessor dalam implementasi statis Anda sebagai berikut:

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

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Kemudian menugaskan IHttpContextAccessor di Konfigurasi Startup harus melakukan pekerjaan.

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

Saya kira Anda juga harus mendaftarkan layanan singleton:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Jan
sumber
Cantik. Apa yang diperintahkan dokter!
ShrapNull
5

Menurut artikel ini: Mengakses HttpContext di luar komponen kerangka kerja di 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;
        }
    }
}

Kemudian:

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

Kemudian:

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

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

Anda bisa menggunakannya seperti ini:

using System.Web;

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

Di Startup

services.AddHttpContextAccessor();

Di Kontroler

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
sumber