在ASP.NET Core中访问当前的HttpContext


132

我需要使用HttpContext静态方法或实用程序服务来访问当前数据。

使用经典的ASP.NET MVC和System.Web,我将只用于HttpContext.Current静态访问上下文。但是如何在ASP.NET Core中做到这一点?

Answers:


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容器解析实例的地方可用。
tugberk

6
@tugberk很好,从理论上讲,您也可以使用CallContextServiceLocator来解析服务,即使来自非DI注入的实例也是如此:CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>()。在实践中,这是一个伟大的事情,如果你能避免它:)
凯文木屋

17
不要使用CallContextServiceLocator
davidfowl

9
@davidfowl,除非您有充分的技术理由(当然,除了“静电是邪恶的”),否则我敢打赌,如果人们别无选择,他们会使用它。
凯文木屋

7
当然,人们很少有有效的技术理由。它更像是使用静态方法更容易,并且更关心可测试性:)
davidfowl

35

死灵法师。
是的,您可以
为那些大规模迁移的人提供秘诀垃圾代码块(叹气,弗洛伊德式的滑动)。
以下方法是骇客的邪恶手段,它正积极地执行satan的快速工作(在.NET Core框架开发人员看来),但它可以起作用

public class Startup

添加一个属性

public IConfigurationRoot Configuration { get; }

然后将单例IHttpContextAccessor添加到ConfigureServices中的DI。

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


}

现在在Configure中,在其中添加IServiceProvider svp,将服务提供者保存到刚创建的虚拟类System.Web.HttpContext(System.Web.HttpContext.ServiceProvider)中的静态变量“ ServiceProvider”中

并将HostingEnvironment.IsHosted设置为true

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窗体中一样,当您尝试访问没有HttpContext的HttpContext时(例如,它曾经Application_Start在global.asax中),您将获得NullReference 。

我再次强调,这只有在您实际添加

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

就像我写的那样。
欢迎使用DI模式中的ServiceLocator模式;)
有关风险和副作用,请咨询您的住院医生或药剂师-或在github.com/aspnet上研究.NET Core的来源,并进行一些测试。


也许更可维护的方法是添加此帮助程序类

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


    }


}

然后在Startup-> Configure中调用HttpContext.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
这是纯粹的邪恶
艺术

2
使用helper方法的版本在每种情况下是否都能正常工作。在考虑在IoC容器中使用不同寿命的多线程,异步和服务?
塔玛斯·莫纳

7
我知道我们所有人都必须竭尽全力指出这是多么恶魔般的...但是,如果您要将一个巨大的项目移植到Core,HttpContext.Current在一些难以访问的静态类中使用。这可能会很有用。在那里,我说了。
Brian MacKay

2
这是纯粹的邪恶……我打算在万圣节实施它。我喜欢DI和IoC ...但是我正在处理一个带有邪恶静态类和邪恶静态变量的遗留应用程序,我们需要使用Kestrel进行推送并尝试注入HttpContext对于我们来说是无法撤消的,而不会破坏所有内容。
德克斯特府18'Oct

2
是的,这是迁移的正确答案。;)
汤姆·斯蒂克

23

只是为了增加其他答案...

在ASP.NET 2.1的核心,还有AddHttpContextAccessor扩展方法,将注册IHttpContextAccessor与正确的生命周期:

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

    // Other code...
}

2
很高兴看到撒旦ic的更正式替代品!
肯·里昂

@Ken Lyon:;)khellang:单身是正确的一生。范围将是错误的。或者至少在撰写本文时是如此。但是,如果AddHttpContextAccessor正确执行此操作而无需我们为特定框架版本提供参考,那就更好了。
Stefan Steiger,

您能举个例子吗?
工具包

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

美丽。正是医生命令的!
ShrapNull19年

5

根据本文的介绍:在ASP.NET Core的框架组件外部访问HttpContext

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();
        }
   }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.