【问题标题】:Access the current HttpContext in ASP.NET Core在 ASP.NET Core 中访问当前的 HttpContext
【发布时间】:2015-09-23 10:27:35
【问题描述】:

我需要在静态方法或实用服务中访问当前的HttpContext

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

【问题讨论】:

    标签: c# asp.net-core


    【解决方案1】:

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

    【讨论】:

    • 好点!还值得一提的是,IHttpContextAccessor 仅在 DI 容器解析实例的地方可用。
    • @tugberk 好吧,理论上,您也可以使用CallContextServiceLocator 来解析服务,即使来自非 DI 注入的实例:CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>()。在实践中,如果你能避免它,那就太好了:)
    • 不要使用 CallContextServiceLocator
    • @davidfowl 除非你有正当的技术原因(当然,除了'静态是邪恶的'),我敢打赌,如果人们别无选择,他们会使用它。
    • 当然,人们很少有正当的技术原因。它更像是使用静态更容易并且谁关心可测试性:)
    【解决方案2】:

    死灵术。
    是的,你可以
    给那些迁移大量 junks 代码块(叹气,弗洛伊德的失误)的人的秘密提示。
    以下方法是一个 hack 的邪恶痈肿,它正在积极从事撒旦的快速工作(在 .NET Core 框架开发人员的眼中),但它确实有效

    public class Startup

    添加属性

    public IConfigurationRoot Configuration { get; }
    

    然后在ConfigureServices中给DI添加一个单例IHttpContextAccessor。

        // 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,将此服务提供者保存到刚刚创建的虚拟类 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-Forms 中一样,当您尝试访问没有 HttpContext 时,您将获得 NullReference,例如它曾经位于 global.asax 中的 Application_Start 中。

    我再次强调,这只有在你实际添加时才有效

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

    【讨论】:

    • 这是纯粹的邪恶
    • 带有辅助方法的版本是否在每个场景中都能正常工作。考虑不同生命周期的 IoC 容器中的多线程、异步和服务?
    • 我知道我们都必须不遗余力地指出这是多么可怕……但是如果你将一个巨大的项目移植到 Core,其中 HttpContext.Current 被用于一些困难的地方-to-reach 静态类...这可能非常有用。在那里,我说过了。
    • 这纯粹是邪恶的......而且我将在万圣节实施它是合适的。我喜欢 DI 和 IoC ...但我正在处理一个带有邪恶静态类和邪恶静态变量的遗留应用程序,我们需要使用 Kestrel 推送它并尝试注入 HttpContext 对我们来说只是不可撤销的,而不会破坏一切。
    • 是的,这是迁移的正确答案。 ;)
    【解决方案3】:

    我想出的最合法的方法是在静态实现中注入 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>();
    

    【讨论】:

    • 我猜你可以直接在 Startup.cs 的 ConfigureServices 函数中使用services.AddHttpContextAccessor();,而不需要services.AddSingleton
    • 这是最正确的答案,而不是其他人的捷径。
    【解决方案4】:

    只是添加到其他答案...

    在 ASP.NET Core 2.1 中,有 the AddHttpContextAccessor extension method,它将使用正确的生命周期注册 IHttpContextAccessor

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

    【讨论】:

    • 很高兴看到撒旦痈的更正式的替代品!
    • @Ken Lyon: ;) khillang: Singleton 是正确的生命周期。 Scoped 是错误的。或者至少在撰写本文时是这样。但是,如果 AddHttpContextAccessor 能正确执行,我们不需要特定框架版本的参考,那就更好了。
    • 你能分享一个例子吗?
    • @Toolkit 添加了一些示例代码。不过,不确定它在上面的文本中提供了什么价值。
    • 没有足够的代码来实现这个建议。
    【解决方案5】:

    根据这篇文章:Accessing HttpContext outside of framework components in 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
       }
    }
    

    【讨论】:

      【解决方案6】:

      在启动中

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

      【讨论】:

      • 这里没有足够的信息来知道下一步该做什么
      【解决方案7】:

      要从一个类访问会话对象而不在类构造函数中显式使用依赖注入,请按照以下步骤操作:

      1. 在 Startup.cs (ConfigureServices) 上添加一个 Singleton 实例:

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        
      2. 在你的目标类中声明一个 HttpContextAccessor 的实例:

        IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
        
      3. 访问会话对象:

        string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");
        

      示例

      Startup.cs

      public void ConfigureServices(IServiceCollection services)
          {
              services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
      
          }
      

      YourClass.cs

      public class YourClass {
            
             public string yourProperty {
                   get{
                       IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
                       return _httpContextAccessor.HttpContext.Session.GetString("_YourSessionVar");         
                      }
              }
        }
      

      享受:)

      【讨论】:

      • 是的,但是如何设置呢?
      • 在上面的例子中,我们假设之前设置了 Session 对象。一般来说,如果你必须访问 HttpContext 你只需要添加单例实例,然后声明一个 IHttpContextAccessor 对象来访问会话对象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-17
      • 1970-01-01
      相关资源
      最近更新 更多