Доступ до поточного 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.