175

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?

poke
  • 369,085
  • 72
  • 557
  • 602
maxswitcher
  • 1,717
  • 2
  • 12
  • 14

7 Answers7

183

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*);
    }
}
Community
  • 1
  • 1
Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
  • 6
    Good point! It's also worth mentioning that `IHttpContextAccessor` would be only available in places where the DI container is resolving the instance. – tugberk Jul 06 '15 at 10:41
  • 6
    @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()`. In practice, it's a great thing if you can avoid it :) – Kévin Chalet Jul 06 '15 at 10:48
  • 19
    Don't use CallContextServiceLocator – davidfowl Jul 07 '15 at 10:55
  • 12
    @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. – Kévin Chalet Jul 07 '15 at 11:03
  • 9
    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 :) – davidfowl Jul 07 '15 at 11:03
  • 3
    can you please provide full code on how to configure this service and how to access 'MyComponent' outside the controller? – Mihai Bratulescu Sep 02 '15 at 06:27
  • 2
    I have a non injected instance (Actually a NLog LayoutRenderer) and I cannot use CallContextServiceLocator, because they only have like 7 services available from the ServiceLocator, and for some reason is not including the IHttpContextAccessor... – Israel Garcia Oct 28 '15 at 15:07
  • 1
    So where do you get the `contextAccessor` to pass to the constructor? If I had an `IHttpContextAccessor`, I wouldn't be having this problem. – recursive Aug 04 '16 at 00:19
  • `IHttpContextAccessor` is injected by the DI container. Note that this service is no longer registered by default in RC2/RTM (but it is by stacks like ASP.NET Core Identity): https://github.com/aspnet/Announcements/issues/190 – Kévin Chalet Aug 04 '16 at 00:22
  • 2
    I'm confused about this. I second recursive's question. Where do we get an instance of IHttpContextAccessor to pass in to the constructor? – Christian Findlay Aug 19 '16 at 04:44
  • 1
    It seems the github issue has been closed. Do I take it that there's just no way to get at the HttpContext unless you have the Request? – Christian Findlay Aug 19 '16 at 04:48
  • 1
    @MelbourneDeveloper This question had me puzzled too, but I finally found the answer (more or less): https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection The basic idea is that dependency injection is a built-in behavior of .NET Core that automatically gets the instance of IHttpContextAccessor, as long as you set things up right in your Startup.cs and specify it in your controller's constructor. – Ben Sutton Dec 08 '16 at 01:10
  • 3
    an instance of `IHttpContextAccessor` will be available to you if you both inject it via DI *and* register an instance of it by calling `services.AddHttpContextAccessor()` in your `ConfigureServices` method of your `Startup` class – Dave Black Nov 26 '19 at 20:50
  • How can I get the same result with a static constructor? – JacobIRR Apr 19 '21 at 21:26
47

Secret tip for those migrating large chunks 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>()
    );
TylerH
  • 20,799
  • 66
  • 75
  • 101
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • 2
    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? – Tamas Molnar Jul 14 '17 at 12:16
  • 10
    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. – Brian MacKay Dec 27 '17 at 15:34
  • 3
    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. – House of Dexter Oct 31 '18 at 14:59
  • @Paras Parmar: Well, for one, you're assuming that the one static instance of IHttpContextAccessor is thread-safe. Even if it is now, there is no guarantee it will be so in the future. Second, you're sacrificing testability to have ch(j?)unks of legacy code ported faster. I guess you have to test if it works for you. But you should probably remove the static references once you're done porting, unless you don't use tests at all, which ain't good either.But if you test it and it works,and you can save a lot of time, why not use it? Staying&accumulating more technical debt is no solution either. – Stefan Steiger Jul 27 '21 at 09:03
42

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>();
Jan
  • 1,661
  • 1
  • 14
  • 7
  • 2
    I guess you can directly use `services.AddHttpContextAccessor();` within the ConfigureServices function in Startup.cs, not really needed the `services.AddSingleton` – jefferyleo Aug 25 '21 at 08:24
  • To avoid a compiler error use: ((IApplicationBuilder)app).ApplicationServices – Brian Birtle May 23 '22 at 11:50
30

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...
    }
}
khellang
  • 17,550
  • 6
  • 64
  • 84
  • @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. – Stefan Steiger Feb 05 '19 at 13:41
  • Can you please share an example? – Toolkit Mar 07 '20 at 10:37
  • @Toolkit Added some example code. Not sure what value it provides over the text above, though. – khellang Mar 09 '20 at 16:08
  • 1
    There's not enough code to implement this suggestion. – pianocomposer Jan 05 '22 at 00:27
13

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
   }
}
x19
  • 8,277
  • 15
  • 68
  • 126
7

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();
        }
   }
Diana Tereshko
  • 139
  • 1
  • 6
1

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

  1. Add a Singleton instance on Startup.cs (ConfigureServices):

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
  2. In your target class declare an instance of HttpContextAccessor:

    IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
    
  3. 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 :)

Jorge Valvert
  • 927
  • 1
  • 11
  • 20
  • Yes, but how do you set it? – pianocomposer Sep 24 '21 at 19:33
  • 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. – Jorge Valvert Oct 18 '21 at 16:54