178

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?

7 Answers 7

189

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*);
    }
}
Sign up to request clarification or add additional context in comments.

14 Comments

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 :)
|
51

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

5 Comments

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.
@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.
We created our own IHttpContext and IHttp???? for all the objects that HttpContext returns. This allows us to test around HttpContext.Current -> MethodA() calls InternalMethodA(IHttpContext httpContext) as InternalMethodA(HttpContext.Current). We then test InternalMethodA with our own mocked IHttpContext (I know this is old question and just added for anybody who wants to use this solution)
43

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

3 Comments

I guess you can directly use services.AddHttpContextAccessor(); within the ConfigureServices function in Startup.cs, not really needed the services.AddSingleton
To avoid a compiler error use: ((IApplicationBuilder)app).ApplicationServices
I have a question. Doesn't this just provide a single/shared instance of the HttpContext for all callers of the HttpHelper.HttpContext? Just trying to understand.
32

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

4 Comments

@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.
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
   }
}

Comments

8

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

2 Comments

There's not enough information here to know what to do next
This seemed like the easiest solution to the problem for me
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 :)

2 Comments

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.