ChatGPT解决这个技术问题 Extra ChatGPT

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

这个问题在这里已经有了答案:Access the current HttpContext in ASP.NET Core (7 answers) Closed 5 years ago。

我们目前正在使用 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 中的等价物是什么,我认为没有太大的危害。答案可能是没有或有不同/更好的方法。

R
Richard Garside

作为一般规则,将 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 的代码中的任何地方,传递它没有任何好处,我是为了简单和功能,我没有看到它的缺点,传递它只会让我工作更多,给我没有好处。
S
Stefan Steiger

死灵术。是的,你可以,这就是方法。给那些迁移大型垃圾代码块的人的秘密提示:以下方法是黑客的邪恶痈肿,它积极参与执行撒旦的快速工作(在 .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 部分
H
Hicham

如果您确实需要对当前上下文的静态访问,则有一个解决方案。在 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;

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


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