ChatGPT解决这个技术问题 Extra ChatGPT

Access the current HttpContext in ASP.NET Core

I need to access current HttpContext in a static method or a utility service.

With classic ASP.NET MVC and System.Web, I would just use HttpContext.Current to access the context statically. But how do I do this in ASP.NET Core?


C
Community

HttpContext.Current doesn't exist anymore in ASP.NET Core but there's a new IHttpContextAccessor that you can inject in your dependencies and use to retrieve the current HttpContext:

public class MyComponent : IMyComponent
{
    private readonly IHttpContextAccessor _contextAccessor;

    public MyComponent(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public string GetDataFromSession()
    {
        return _contextAccessor.HttpContext.Session.GetString(*KEY*);
    }
}

Good point! It's also worth mentioning that IHttpContextAccessor would be only available in places where the DI container is resolving the instance.
@tugberk well, in theory, you could also use the CallContextServiceLocator to resolve a service, even from a non-DI-injected instance: CallContextServiceLocator.Locator.ServiceProvider.GetService<IHttpContextAccessor>(). In practice, it's a great thing if you can avoid it :)
Don't use CallContextServiceLocator
@davidfowl unless you have a valid technical reason (apart from 'statics are evil', of course), I bet people will use it if they have no other choice.
Sure, people rarely have a valid technical reason though. It's more like it's easier to use a static and who cares about testability :)
S
Stefan Steiger

Necromancing. YES YOU CAN Secret tip for those migrating large junks chunks (sigh, Freudian slip) of code. The following method is an evil carbuncle of a hack which is actively engaged in carrying out the express work of satan (in the eyes of .NET Core framework developers), but it works:

In public class Startup

add a property

public IConfigurationRoot Configuration { get; }

And then add a singleton IHttpContextAccessor to DI in ConfigureServices.

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

Then in Configure

    public void Configure(
              IApplicationBuilder app
             ,IHostingEnvironment env
             ,ILoggerFactory loggerFactory
    )
    {

add the DI Parameter IServiceProvider svp, so the method looks like:

    public void Configure(
           IApplicationBuilder app
          ,IHostingEnvironment env
          ,ILoggerFactory loggerFactory
          ,IServiceProvider svp)
    {

Next, create a replacement class for 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 


}

Now in Configure, where you added the IServiceProvider svp, save this service provider into the static variable "ServiceProvider" in the just created dummy class System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)

and set HostingEnvironment.IsHosted to true

System.Web.Hosting.HostingEnvironment.m_IsHosted = true;

this is essentially what System.Web did, just that you never saw it (I guess the variable was declared as internal instead of public).

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

    });

Like in ASP.NET Web-Forms, you'll get a NullReference when you're trying to access a HttpContext when there is none, such as it used to be in Application_Start in global.asax.

I stress again, this only works if you actually added

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

like I wrote you should.
Welcome to the ServiceLocator pattern within the DI pattern ;)
For risks and side effects, ask your resident doctor or pharmacist - or study the sources of .NET Core at github.com/aspnet, and do some testing.

Perhaps a more maintainable method would be adding this helper class

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


    }


}

And then calling HttpContext.Configure in Startup->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>()
    );

THIS IS PURE EVIL
Is the version with the helper method work properly in each scenario. Thinking of multithreading, async and with services in IoC container with different lifetime?
I know we all have to go out of our way to point out how fiendishly diabolical this is... But if you were porting a huge project to Core, where HttpContext.Current was used in some hard-to-reach static classes... This would probably be quite useful. There, I said it.
This is pure evil...and appropriate that I'm going to implement it on Halloween. I love DI and IoC...but I'm dealing with a legacy app with evil Static Classes with evil Static variables, that we need to push using Kestrel and trying to inject HttpContext would be just undoable for us, without breaking everything.
Ya, this is the correct answer for MIGRATIONS. ;)
J
Jan

The most legit way I came up with was by injecting IHttpContextAccessor in your static implementation as follow:

public static class HttpHelper
{
     private static IHttpContextAccessor _accessor;
     public static void Configure(IHttpContextAccessor httpContextAccessor)
     {
          _accessor = httpContextAccessor;
     }

     public static HttpContext HttpContext => _accessor.HttpContext;
}

Then assigning the IHttpContextAccessor in the Startup Configure should do the job.

HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());

I guess you should also need to register the service singleton:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

I guess you can directly use services.AddHttpContextAccessor(); within the ConfigureServices function in Startup.cs, not really needed the services.AddSingleton
This was the most right answer rather than the shortcut answers from others.
To avoid a compiler error use: ((IApplicationBuilder)app).ApplicationServices
k
khellang

Just to add to the other answers...

In ASP.NET Core 2.1, there's the AddHttpContextAccessor extension method, that will register the IHttpContextAccessor with the correct lifetime:

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

        // Other code...
    }
}

Glad to see a more official alternative to the Satanic carbuncle!
@Ken Lyon: ;) khellang: Singleton is the correct lifetime. Scoped would be wrong. Or at the very least at the time of writing, that was so. But all the better if AddHttpContextAccessor does it correctly without us needing a reference for the specific framework version.
Can you please share an example?
@Toolkit Added some example code. Not sure what value it provides over the text above, though.
There's not enough code to implement this suggestion.
x
x19

According to this article: 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;
        }
    }
}

Then:

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

Then:

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

    public void Configure(IApplicationBuilder app)
    {
        app.UseStaticHttpContext();
        app.UseMvc();
    }
}

You can use it like this:

using System.Web;

public class MyService
{
   public void DoWork()
   {
    var context = HttpContext.Current;
    // continue with context instance
   }
}

D
Diana Tereshko

In Startup

services.AddHttpContextAccessor();

In Controller

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

There's not enough information here to know what to do next
J
Jorge Valvert

To access to the session object from a class without explicitly use dependency injection in class constructor follow the next steps:

Add a Singleton instance on Startup.cs (ConfigureServices): services.AddSingleton(); In your target class declare an instance of HttpContextAccessor: IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor(); Access to the session object : string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");

EXAMPLE

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

Enjoy :)


Yes, but how do you set it?
In the above example, we assume the Session object was set before. In general if you have to access to the HttpContext you only have to add the singleton instance,then declare a IHttpContextAccessor object to access to the session object.