如何在 ASP.NET Core 中获取 HttpContext.Current? [复制]

Posted

技术标签:

【中文标题】如何在 ASP.NET Core 中获取 HttpContext.Current? [复制]【英文标题】:How to get HttpContext.Current in ASP.NET Core? [duplicate] 【发布时间】:2016-11-28 23:44:31 【问题描述】:

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

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

 var current = HttpContext.Current;
     if (current == null)
      
       // do something here
       // string connection = Configuration.GetConnectionString("MyDb");
      

我需要访问它以构建当前的应用程序主机。

$"current.Request.Url.Scheme://current.Request.Url.Host(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)";

【问题讨论】:

首先问问自己,为什么需要从类库中访问它。 80% 的情况下,没有理由,只是糟糕的设计甚至尝试。在您上面的情况下,您没有。更改类库的设计,传入您需要的参数,而不是从中访问。更少的耦合,更好的可维护代码。查看IOptions<T> @Tseng 可以说我想通过它。那我应该通过什么?这就是这里的全部问题。 ASP.NET Core中相当于HttpContext的命名空间或对象是什么 阅读您的问题,并意识到这不是问题,它几乎没有说什么。信息如此之少,很难提供。其次,请阅读我的第一条评论以了解正确的方向 @Tseng 同时它不应该像一个不理解问题并且他们投反对票一样。你对这个问题发表了评论,如果你理解我的回答,你的评论与问题无关,它说明你根本不理解这个问题。所以像你这样的人一直在投票,这也阻止了用户使用这个平台。该平台适用于高质量的问题和高质量的答案,而不仅仅是投反对票和投赞成票。 @Tseng:在您的评论中,您没有说明为什么您认为这是一个不好的问题。这是关于为什么这个人不应该尝试做他想做的事情。人们在 ASP.NET 中的任何地方都使用HttpContext.Current,不管他们是否应该。询问 ASP.NET Core 中的等价物是什么,我认为没有太大的危害。答案可能是没有,或者有不同/更好的方法。 【参考方案1】:

作为一般规则,将 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

中间件中的HttpContext参数

如果您正在为 ASP.NET Core 管道编写 custom middleware,则当前请求的 HttpContext 会自动传递到您的 Invoke 方法中:

public Task Invoke(HttpContext context)

    // Do something with the current HTTP context...

HTTP 上下文访问器

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

在你的构造函数中请求这个接口:

public MyMiddleware(IHttpContextAccessor httpContextAccessor)

    _httpContextAccessor = httpContextAccessor;

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

var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...

IHttpContextAccessor 并不总是默认添加到服务容器中,所以为了安全起见,将其注册到ConfigureServices

public void ConfigureServices(IServiceCollection services)

    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...

【讨论】:

您忘记了IHttpContextAccessor,但是由于 RC2,它默认没有注册,因为它会带来一些请求开销。见公告github.com/aspnet/Announcements/issues/190 单例可以吗?应该改为 AddScoped 吗? @syclee 我也想知道,但根据上面的链接(以及basic MVC sample app),我的答案中的语法是正确的。 我认为访问者可以是单例的......这只是让我们能够获取上下文的东西......所以访问(上下文)的结果通常是每个请求值。 @Nate Barbettini 在代码中的任何地方,你都在当前的 HttpContext 中,传递它没有任何好处,我是为了简单和功能,我看不出它的缺点,传递它只是让我工作更多,没有任何好处。【参考方案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>()
    );

【讨论】:

请注意辅助类方式也需要运行services.AddSingleton 部分【参考方案3】:

如果您确实需要对当前上下文的静态访问,有一个解决方案。 在 Startup.Configure(….)

app.Use(async (httpContext, next) =>

    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    
        await next();
    
    finally
    
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    
);

当您需要它时,您可以通过以下方式获得它:

HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;

我希望这会有所帮助。请记住,这种解决方法是在您别无选择时。最佳实践是使用去依赖注入。

【讨论】:

这对我不起作用,我应该能够在引用的类库中使用这一行吗? HttpContext 上下文 = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;当我这样做时,上下文只是设置为空 您可以在库中使用它。但我认为你只是使用了 System.Web.HttpContext 类,它与 aspnet 核心 http 上下文(Microsoft.AspNetCore.Http.HttpContext)不是同一个类

以上是关于如何在 ASP.NET Core 中获取 HttpContext.Current? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 ASP.NET Core 3 中的中间 Web API 传递未更改的 HTTP 响应?

如何在asp.net core中获取当前用户

ASP.Net Core 如何在 EF Core 和 Identity 中获取用户角色

如何在 ASP.NET Core 中获取 HttpContext.Current? [复制]

如何在 ASP.NET Core MVC 中获取 Url Referrer?

ASP.Net Core:如何获取无效 ModelState 值的键?