ASP.NET Core'da geçerli HttpContext'e erişin


132

Akıma erişmem gerekiyor HttpContextStatik bir yöntem veya bir yardımcı program hizmetindeki erişmem .

Klasik ASP.NET MVC ile ve içeriğe statik olarak erişmek için System.Webkullanırım HttpContext.Current. Ama bunu ASP.NET Core'da nasıl yaparım?

Yanıtlar:


149

HttpContext.Currentartık ASP.NET Core'da mevcut değil, ancak IHttpContextAccessorbağımlılıklarınıza enjekte edebileceğiniz ve geçerli olanı almak için kullanabileceğiniz yeni bir şey var 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
İyi bir nokta! Ayrıca, IHttpContextAccessoryalnızca DI kapsayıcısının örneği çözdüğü yerlerde kullanılabileceğini belirtmekte fayda var .
tugberk

6
@tugberk de, teoride, ayrıca kullanabilirsiniz CallContextServiceLocatordahi olmayan bir DI-enjekte örneğinden, bir hizmetin çözümlenmesinde: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). Pratikte, bundan kaçınmanız harika bir şey :)
Kévin Chalet

17
CallContextServiceLocator'ı kullanma
davidfowl

9
@davidfowl, geçerli bir teknik nedeniniz yoksa (tabii ki 'statik kötüdür' dışında), bahse girerim insanlar başka seçenekleri yoksa kullanırlar.
Kévin Chalet

7
Elbette, insanların nadiren geçerli bir teknik nedeni vardır. Statik kullanmak daha kolay gibi ve test edilebilirliği kimin umurunda :)
davidfowl

35

Necromancing.
EVET YAPABİLİRSİN
büyük göçmenler için gizli ipucujunkskod parçacıkları (iç çekiş, Freudcu kayma).
Aşağıdaki yöntem aktif (NET Çekirdek çerçeve geliştiricileri gözünde) şeytanın ekspres çalışmalarını yürüten yapan bir hack bir kötülük kan çıbanı olduğunu ama çalışıyor :

İçinde public class Startup

mülk ekle

public IConfigurationRoot Configuration { get; }

Ve sonra ConfigureServices'da DI'ye tek bir IHttpContextAccessor ekleyin.

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

Sonra Yapılandırda

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

DI Parametresini ekleyin IServiceProvider svp, böylece yöntem şöyle görünür:

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

Ardından, System.Web için bir değiştirme sınıfı oluşturun:

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 


}

Şimdi IServiceProvider svp, bu hizmet sağlayıcıyı eklediğiniz Configure'da, yeni oluşturulan kukla sınıf System.Web.HttpContext (System.Web.HttpContext.ServiceProvider) içindeki "ServiceProvider" statik değişkenine kaydedin.

ve HostingEnvironment.IsHosted değerini true olarak ayarlayın

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

System.Web'in yaptığı şey buydu, siz onu hiç görmediniz (sanırım değişken public yerine dahili olarak ilan edildi).

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

    });

ASP.NET Web Formlarında olduğu gibi Application_Start, global.asax içinde olduğu gibi, hiçbiri olmadığında bir HttpContext'e erişmeye çalıştığınızda bir NullReference alırsınız .

Tekrar vurguluyorum, bu yalnızca gerçekten eklerseniz işe yarar

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

yazdığım gibi yapmalısın.
DI modeli içindeki ServiceLocator modeline hoş geldiniz;)
Riskler ve yan etkiler için, doktorunuza veya eczacınıza danışın - veya github.com/aspnet adresinde .NET Core kaynaklarını inceleyin ve bazı testler yapın.


Belki de daha sürdürülebilir bir yöntem bu yardımcı sınıfı eklemek olabilir

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


    }


}

Ve sonra Başlangıç-> Yapılandır'da HttpContext.Configure çağrısı

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
BU SAF KÖTÜ
Sanat

2
Yardımcı yönteme sahip sürüm her senaryoda düzgün çalışıyor mu? Çok iş parçacıklı, zaman uyumsuz ve farklı kullanım sürelerine sahip IoC konteynerindeki hizmetleri mi düşünüyorsunuz?
Tamas Molnar

7
Bunun ne kadar şeytanca bir şey olduğunu belirtmek için hepimizin yolumuzdan çekilmesi gerektiğini biliyorum ... Ama eğer büyük bir projeyi Core'a taşıyorsanız, HttpContext.Current bazı ulaşılması zor statik sınıflarda kullanılmıştır .. Bu muhtemelen oldukça faydalı olacaktır. Orada söyledim.
Brian MacKay

2
Bu saf bir kötülük ... ve bunu Cadılar Bayramı'nda uygulayacağım uygun. DI ve IoC'yi seviyorum ... ama kötü Statik değişkenlere sahip kötü Statik Sınıflar içeren eski bir uygulamayla uğraşıyorum, Kestrel kullanarak zorlamamız ve HttpContext'i enjekte etmeye çalışmamız, bizim için her şeyi bozmadan geri alınamaz.
House of Dexter

2
Ya, bu GÖÇLER için doğru cevap. ;)
Tom Stickel

23

Sadece diğer cevaplara eklemek için ...

ASP.NET Çekirdek 2.1, orada uzatma yöntemi olduğunu kaydedecektir, doğru ömrü ile:AddHttpContextAccessorIHttpContextAccessor

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

    // Other code...
}

2
Satanic carbuncle'a daha resmi bir alternatif görmekten memnunum!
Ken Lyon

@Ken Lyon:;) khellang: Singleton doğru yaşam süresidir. Kapsamlı olmak yanlış olur. Ya da en azından yazı yazarken böyleydi. Ancak AddHttpContextAccessor, belirli çerçeve sürümü için bir referansa ihtiyaç duymadan bunu doğru bir şekilde yaparsa daha iyi olur.
Stefan Steiger

Lütfen bir örnek paylaşır mısınız?
Araç Seti

@Toolkit Bazı örnek kodlar eklendi. Yine de yukarıdaki metnin üzerinde ne gibi bir değer sağladığından emin değilim.
khellang

22

Bulduğum en yasal yol, statik uygulamanıza IHttpContextAccessor'ı aşağıdaki gibi enjekte etmekti:

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

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Ardından, Başlangıç ​​Yapılandırmasında IHttpContextAccessor atamak işi yapmalıdır.

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

Sanırım ayrıca hizmet tekilini kaydetmeniz gerekiyor:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Güzel. Tam da doktorun emrettiği!
ShrapNull

5

Bu makaleye göre: ASP.NET Core'da HttpContext'e çerçeve bileşenleri dışında erişim

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

Sonra:

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

Sonra:

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

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

Bunu şu şekilde kullanabilirsiniz:

using System.Web;

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

2

Başlangıçta

services.AddHttpContextAccessor();

Denetleyicide

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();
        }
   }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.