如何在ASP.NET Core中获取HttpContext.Current?

HaB*_*aBo 182 c# .net-core asp.net-core

我们目前正在使用ASP.NET Core重写/转换我们的ASP.NET WebForms应用程序.尽量避免重新设计.

我们HttpContext在类库中使用了一个部分来检查当前状态.如何HttpContext.Current在.NET Core 1.0中访问?

 var current = HttpContext.Current;
     if (current == null)
      {
       // do something here
       // string connection = Configuration.GetConnectionString("MyDb");
      }
Run Code Online (Sandbox Code Playgroud)

我需要访问它才能构建当前的应用程序主机.

$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";
Run Code Online (Sandbox Code Playgroud)

Nat*_*ini 231

作为一般规则,将Web窗体或MVC5应用程序转换为ASP.NET Core 将需要大量的重构.

HttpContext.Current已在ASP.NET Core中删除.从单独的类库访问当前HTTP上下文是ASP.NET Core试图避免的混乱体系结构的类型.有几种方法可以在ASP.NET Core中重新构建它.

HttpContext属性

您可以通过HttpContext任何控制器上的属性访问当前HTTP上下文.与原始代码示例最接近的是传递HttpContext给您调用的方法:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        MyMethod(HttpContext);

        // Other code
    }
}

public void MyMethod(Microsoft.AspNetCore.Http.HttpContext context)
{
    var host = $"{context.Request.Scheme}://{context.Request.Host}";

    // Other code
}
Run Code Online (Sandbox Code Playgroud)

中间件中的HttpContext参数

如果您正在为ASP.NET Core管道编写自定义中间件,则当前请求HttpContextInvoke自动传递到您的方法中:

public Task Invoke(HttpContext context)
{
    // Do something with the current HTTP context...
}
Run Code Online (Sandbox Code Playgroud)

HTTP上下文访问器

最后,您可以使用IHttpContextAccessor帮助程序服务来获取由ASP.NET Core依赖注入系统管理的任何类中的HTTP上下文.当您拥有控制器使用的公共服务时,这非常有用.

在构造函数中请求此接口:

public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以以安全的方式访问当前的HTTP上下文:

var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...
Run Code Online (Sandbox Code Playgroud)

IHttpContextAccessor默认情况下并不总是添加到服务容器中,因此请将其注册ConfigureServices为安全:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}
Run Code Online (Sandbox Code Playgroud)

  • 这是单身人士吗?那应该是AddScoped吗? (8认同)
  • 你忘记了`IHttpContextAccessor`,但是因为RC2它没有默认注册,因为它带来了一些请求开销.请参阅公告https://github.com/aspnet/Announcements/issues/190 (4认同)
  • @syclee我也想知道这一点,但根据上面的链接(以及[基本的MVC示例应用程序](https://github.com/aspnet/Mvc/blob/master/test/WebSites/BasicWebSite/Startup. cs#L25)),我的答案中的语法是正确的. (3认同)
  • 我认为accessser可以是单例...它只是允许我们获取上下文的东西......因此访问(上下文)的结果通常是每个请求值. (3认同)
  • @Nate Barbettini您在当前HttpContext中的代码中的任何位置,都没有传递它的好处,我只是出于简单性和功能性,我没有看到它的缺点,传递它只是使我工作更多而没有好处。 (3认同)
  • 是..但问题是我在中间件中这样做...并且该特定类是Injected ..但是上下文尚未设置..但这不是一个大问题..因为中间件传递了上下文正在构建..因此我重新调整了我的代码以使用HtttpContext,以便我可以从任何地方传递它而不是依赖于`IHttpContextAccessor` - 只是其中一个边缘情况.. (2认同)
  • 在.NET Framework HttpContext.Current中没有像这样的简单方法吗?为什么要删除这样的重要功能?现在我需要在所有需要的地方都粘贴它,并且在很多课程中都需要它。 (2认同)
  • @Hove 因为删除它会迫使每个人采用更好的编码实践。显式传递依赖关系更加冗长,但也更易于测试,并促进更好的关注点分离。 (2认同)

Ste*_*ger 48

Necromancing.
是的你可以,这是怎么回事.
那些迁移大的人的秘密提示帆船大块的代码:
下面的方法是一个黑客的邪恶痈,积极参与执行撒旦的快速工作(在.NET Core框架开发人员的眼中),但它的工作原理:

public class Startup

添加一个属性

public IConfigurationRoot Configuration { get; }
Run Code Online (Sandbox Code Playgroud)

然后在ConfigureServices中将单个IHttpContextAccessor添加到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>();
Run Code Online (Sandbox Code Playgroud)

然后在Configure中

    public void Configure(
              IApplicationBuilder app
             ,IHostingEnvironment env
             ,ILoggerFactory loggerFactory
    )
    {
Run Code Online (Sandbox Code Playgroud)

添加DI参数IServiceProvider svp,方法如下:

    public void Configure(
           IApplicationBuilder app
          ,IHostingEnvironment env
          ,ILoggerFactory loggerFactory
          ,IServiceProvider svp)
    {
Run Code Online (Sandbox Code Playgroud)

接下来,为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 


}
Run Code Online (Sandbox Code Playgroud)

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

并将HostingEnvironment.IsHosted设置为true

System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
Run Code Online (Sandbox Code Playgroud)

这基本上是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

    });
Run Code Online (Sandbox Code Playgroud)

就像在ASP.NET Web-Forms中一样,当你尝试访问HttpContext时,你会得到一个NullReference,例如它曾经Application_Start在global.asax中.

我再次强调,这只有在您真正添加时才有效

services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
Run Code Online (Sandbox Code Playgroud)

就像我写的那样.
欢迎使用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;
            }
        }


    }


}
Run Code Online (Sandbox Code Playgroud)

然后在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>()
    );
Run Code Online (Sandbox Code Playgroud)

  • 你谈论的是"垃圾",而不是代码的"块".这是弗洛伊德式的失误吗?:) (14认同)
  • 这是邪恶天才的作品.+1 (5认同)

HLS*_*HLS 10

如果您确实需要对当前上下文的静态访问,则可以使用此解决方案.在Startup.Configure(...)中

app.Use(async (httpContext, next) =>
{
    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    {
        await next();
    }
    finally
    {
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    }
});
Run Code Online (Sandbox Code Playgroud)

当你需要它时,你可以得到它:

HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;
Run Code Online (Sandbox Code Playgroud)

我希望有所帮助.请记住,当您无法选择时,此解决方法.最佳实践是使用de依赖注入.