Доступ до поточного HttpContext в ASP.NET Core


132

Мені потрібно отримати доступ до струму HttpContext статичним методом або службовою службою.

З класичним ASP.NET MVC і System.Web, я б просто використовувати HttpContext.Currentдля статичного доступу до контексту. Але як це зробити в ASP.NET Core?

Відповіді:


149

HttpContext.Currentбільше не існує в ASP.NET Core, але є нове, IHttpContextAccessorяке ви можете ввести у свої залежності і використовувати для отримання поточного 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
Гарна думка! Варто також згадати, що це IHttpContextAccessorбуло б доступне лише в місцях, де контейнер DI вирішує екземпляр.
тугберк

6
@tugberk добре, в теорії, можна також використовувати CallContextServiceLocatorдля вирішення служби, навіть з не-DI-закачуваної наприклад: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). На практиці це чудова річ, якщо ви зможете цього уникнути :)
Шале Кевін

17
Не використовуйте CallContextServiceLocator
davidfowl

9
@davidfowl, якщо у вас немає поважних технічних причин (крім, звичайно, "статики є злими"), я думаю, що люди будуть використовувати це, якщо у них немає іншого вибору.
Шале Kévin

7
Звичайно, люди рідко мають вагомі технічні причини. Це більше схоже на те, що простіше використовувати статику, і хто дбає про
заповітність

35

Некромантування.
ТАК, ТИ МОЖЕШ
Секретну пораду для великих мігрантівджонкишматки (зітхання, фрейдівське ковзання) коду.
Наступний метод - злий карбункул хака, який активно бере участь у експрес-роботі сатани (в очах розробників .NET Core Framework), але це працює :

В public class Startup

додати властивість

public IConfigurationRoot Configuration { get; }

А потім додайте одноразовий IHttpContextAccessor в 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>();

Потім у Налаштувати

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

додайте параметр DI IServiceProvider svp, щоб метод виглядав так:

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

Далі створіть клас заміни для 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 svp, збережіть цього постачальника послуг у статичну змінну "ServiceProvider" у щойно створеному манекені класу System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)

і встановіть HostingEn Environmentment. Підтверджено до істини

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

це по суті те, що зробив System.Web, тільки що ви його ніколи не бачили (я думаю, що змінна була оголошена як внутрішня, а не публічна).

// 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-Forms, ви отримаєте NullReference, коли намагаєтесь отримати доступ до HttpContext, коли його немає, як, наприклад, Application_Startу global.asax.

Ще раз наголошую, це працює лише в тому випадку, якщо ви насправді додали

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

як я написав, що слід.
Ласкаво просимо до схеми ServiceLocator у межах схеми DI;)
Про ризики та побічні ефекти зверніться до лікаря-резидента чи фармацевта - або вивчіть джерела .NET Core на веб- сайті github.com/aspnet та зробіть тестування.


Можливо, більш доцільним методом буде додавання цього класу помічників

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


    }


}

А потім зателефонувавши HttpContext.Configure в 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>()
    );

37
ЦЕ PURE ЗЛО
Art

2
Чи правильно в кожному сценарії працює версія з хелперним методом. Думаєте про багатопоточність, асинхронізацію та послуги в контейнері IoC з різним терміном експлуатації?
Тамас Молнар

7
Я знаю, що всі ми повинні піти з шляху, щоб вказати, наскільки це дивно диявольське ... Але якщо ви переносите величезний проект до Core, де HttpContext.Current використовувався в деяких важкодоступних статичних класах .. Це, мабуть, було б дуже корисно. Там я це сказав.
Брайан Маккей

2
Це чисте зло ... і доречно, що я збираюся його здійснити на Хелловін. Я люблю DI та IoC ... але я маю справу зі застарілим додатком із злими Статичними класами із злими Статичними змінними, які нам потрібно натиснути за допомогою Kestrel і намагатися ввести HttpContext було б для нас просто невідступним, не порушуючи нічого.
Дім Декстера

2
Так, це правильна відповідь для МІГРАЦІЙ. ;)
Том

23

Просто додати до інших відповідей ...

У ASP.NET 2.1 Ядра, є метод розширення , який буде зареєструвати з правильної життям:AddHttpContextAccessorIHttpContextAccessor

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

    // Other code...
}

2
Радий побачити офіційну альтернативу сатанинській карбункулу!
Кен Ліон

@Ken Lyon:;) khellang: Singleton - це правильне життя. Охоплювати було б неправильно. Або принаймні на момент написання, це було так. Але ще краще, якщо AddHttpContextAccessor робить це правильно, не потребуючи посилання на конкретну рамкову версію.
Стефан Штайгер

Чи можете ви поділитися прикладом?
Інструментарій

@Toolkit Додано приклад коду. Не впевнений, яке значення він надає над текстом вище.
khellang

22

Найбільш законним способом, який я придумав, було введення IHttpContextAccessor у вашу статичну реалізацію наступним чином:

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

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Потім присвоєння IHttpContextAccessor у налаштуваннях запуску повинно виконати цю роботу.

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

Я думаю, вам також потрібно зареєструвати послугу одиночного:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Гарний. Тільки те, що призначив лікар!
ShrapNull

5

Відповідно до цієї статті: Доступ до HttpContext поза компонентами рамки в 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;
        }
    }
}

Тоді:

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

Тоді:

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

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

Ви можете використовувати його так:

using System.Web;

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

2

У запуску

services.AddHttpContextAccessor();

У контролері

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();
        }
   }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.