Coder Perfect

In ASP.NET Core, how do you acquire HttpContext.Current? [duplicate]

Problem

Our ASP.NET WebForms application is now being rewritten/converted to ASP.NET Core. Trying to stay as far away from re-engineering as possible.

We utilize HttpContext in a class library to check the current state in one area. In.NET Core 1.0, how can I get to HttpContext.Current?

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

In order to build the current application host, I require access to this.

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

Asked by HaBo

Solution #1

Converting a Web Forms or MVC5 application to ASP.NET Core will, on average, necessitate a large amount of restructuring.

In ASP.NET Core, HttpContext.Current was deprecated. The type of complicated design that ASP.NET Core attempts to avoid is accessing the current HTTP context from a separate class library. In ASP.NET Core, there are a couple options for re-architecting this.

The HttpContext property on any controller gives you access to the current HTTP context. Passing HttpContext into the method you’re calling is the closest thing to your original code sample:

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
}

The current request’s HttpContext is automatically supplied into your Invoke method if you’re building custom middleware for the ASP.NET Core pipeline:

public Task Invoke(HttpContext context)
{
    // Do something with the current HTTP context...
}

Finally, you may get the HTTP context in any class maintained by the ASP.NET Core dependency injection mechanism by using the IHttpContextAccessor helper service. When you have a shared service that all of your controllers use, this is quite beneficial.

In your constructor, make the following request:

public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

You may then safely access the current HTTP context:

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

IHttpContextAccessor isn’t always added to the service container by default, so just to be safe, register it in ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}

Answered by Nate Barbettini

Solution #2

Necromancing. YES, YOU CAN, and here’s how to do it. For those moving significant pieces of code, here’s a secret tip: In the eyes of.NET Core framework developers, the following method is a wicked carbuncle of a hack that is actively engaged in carrying out the express work of satan, but it works:

Startup is a public class.

add a property

public IConfigurationRoot Configuration { get; }

Then, in ConfigureServices, add a singleton IHttpContextAccessor to DI.

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

To make the method appear like this, add the DI parameter IServiceProvider svp to the method:

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

Next, make a System replacement class. 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 


}

Save this service provider into the static variable “ServiceProvider” in the newly generated dummy class System in Configure, where you added the IServiceProvider svp. HttpContext.Web (System.Web.HttpContext.ServiceProvider)

Set the HostingEnvironment variable. IsHosted is set to true.

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

This is essentially what System.Web did, only you weren’t aware of 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

    });

When you try to use a HttpContext when there is none, as in ASP.NET Web-Forms, you’ll get a NullReference, as it did in Application Start in global.asax.

I want to emphasize that this only works if you actually added something.

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

As I already stated, you should. Welcome to the DI pattern’s ServiceLocator pattern 😉 Ask your local doctor or pharmacist about hazards and side effects, or look at the.NET Core source code at github.com/aspnet and perform some testing.

Adding this helper class might be a more maintainable solution.

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


    }


}

Then HttpContext is called. Set up 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>()
    );

Answered by Stefan Steiger

Solution #3

If you really require static access to the current context, there is a workaround. In the early stages of development. Configure(….)

app.Use(async (httpContext, next) =>
{
    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    {
        await next();
    }
    finally
    {
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    }
});

And you can obtain it when you need it with:

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

I hope this information is useful. Keep in mind that you should only use this workaround if you have no other option. De dependency injection is the best practice.

Answered by Hicham

Post is based on https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core