Spinner

ASP.NET Core - Where did my HttpContext go?

A critical thing to understand is that ASP.NET Core is no longer based on System.Web.dll. The new architecture is modular and decoupled from IIS. The HttpContext sealed class that used to live in the System.Web namespace it is now in the Microsoft.AspNetCore.Http Nuget package with a few noteworthy design differences. Instead of accessing the static property Current, you now instantiate the HttpContextAccessor which provides you with a HttpContext property of type from the abstract class HttpContext.

ASP.NET Core is designed from the ground up to support and leverage dependency injection. I believe the best way to instantiate the HttpContextAccessor is to utilize that pattern using the baked-in functionality. We will be creating a singleton by registering the type via its implemented interface. In Startup.cs look for ConfigureServices and add line 4.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
   
    services.AddMvc();

    // Add any other application services here...
}

Secondly, we will create a helper class with several static methods to access various common pieces of information from the request, including HttpContext itself. Feel free to add any helper methods here that you feel are appropriate.

public class WebHelpers
{
    private static IHttpContextAccessor _httpContextAccessor;

    public static void Configure(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public static HttpContext HttpContext
    {
        get { return _httpContextAccessor.HttpContext; }
    }

    public static string GetRemoteIP
    {
        get { return HttpContext.Connection.RemoteIpAddress.ToString(); }
    }

    public static string GetUserAgent
    {
        get { return HttpContext.Request.Headers["User-Agent"].ToString(); }
    }

    public static string GetScheme
    {
        get { return HttpContext.Request.Scheme; }
    }
}

Lastly, in the Configure method within Startup.cs, initialize the web helpers with our singleton HttpContextAccessor resolved by the built-in dependency injection container using app.ApplicationServices.GetRequiredService<IHttpContextAccessor>() (see line 28).

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseApplicationInsightsRequestTelemetry();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseApplicationInsightsExceptionTelemetry();
    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });

    WebHelpers.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
}

I hope this simple approach might shed some light on the subject and help you structure things in a clean, idiomatic fashion.

No comments: