Access HttpContext in ASP.NET Core

ASP.NET Core apps access HttpContext through the IHttpContextAccessor interface and its default implementation HttpContextAccessor. It's only necessary to use IHttpContextAccessor when you need access to the HttpContext inside a service.

Use HttpContext from Razor Pages

The Razor Pages PageModel exposes the HttpContext property:

public class AboutModel : PageModel
{
    public string Message { get; set; }

    public void OnGet()
    {
        Message = HttpContext.Request.PathBase;
    }
}

Use HttpContext from a Razor view

Razor views expose the HttpContext directly via a RazorPage.Context property on the view. The following example retrieves the current username in an intranet app using Windows Authentication:

@{
    var username = Context.User.Identity.Name;
    
    ...
}

Use HttpContext from a controller

Controllers expose the ControllerBase.HttpContext property:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var pathBase = HttpContext.Request.PathBase;

        ...

        return View();
    }
}

Use HttpContext from middleware

When working with custom middleware components, HttpContext is passed into the Invoke or InvokeAsync method and can be accessed when the middleware is configured:

public class MyCustomMiddleware
{
    public Task InvokeAsync(HttpContext context)
    {
        ...
    }
}

Use HttpContext from custom components

For other framework and custom components that require access to HttpContext, the recommended approach is to register a dependency using the built-in dependency injection container. The dependency injection container supplies the IHttpContextAccessor to any classes that declare it as a dependency in their constructors:

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllersWithViews();
     services.AddHttpContextAccessor();
     services.AddTransient<IUserRepository, UserRepository>();
}
public void ConfigureServices(IServiceCollection services)
{
     services.AddMvc()
         .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
     services.AddHttpContextAccessor();
     services.AddTransient<IUserRepository, UserRepository>();
}

In the following example:

  • UserRepository declares its dependency on IHttpContextAccessor.
  • The dependency is supplied when dependency injection resolves the dependency chain and creates an instance of UserRepository.
public class UserRepository : IUserRepository
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    public void LogCurrentUser()
    {
        var username = _httpContextAccessor.HttpContext.User.Identity.Name;
        service.LogAccessRequest(username);
    }
}

HttpContext access from a background thread

HttpContext isn't thread-safe. Reading or writing properties of the HttpContext outside of processing a request can result in a NullReferenceException.

Note

If your app generates sporadic NullReferenceException errors, review parts of the code that start background processing or that continue processing after a request completes. Look for mistakes, such as defining a controller method as async void.

To safely perform background work with HttpContext data:

  • Copy the required data during request processing.
  • Pass the copied data to a background task.

To avoid unsafe code, never pass the HttpContext into a method that performs background work. Pass the required data instead. In the following example, SendEmailCore is called to start sending an email. The correlationId is passed to SendEmailCore, not the HttpContext. Code execution doesn't wait for SendEmailCore to complete:

public class EmailController : Controller
{
    public IActionResult SendEmail(string email)
    {
        var correlationId = HttpContext.Request.Headers["x-correlation-id"].ToString();

        _ = SendEmailCore(correlationId);

        return View();
    }

    private async Task SendEmailCore(string correlationId)
    {
        ...
    }
}

Blazor and shared state

Blazor server apps live in server memory. That means that there are multiple apps hosted within the same process. For each app session, Blazor starts a circuit with its own DI container scope. That means that scoped services are unique per Blazor session.

Warning

We don't recommend apps on the same server share state using singleton services unless extreme care is taken, as this can introduce security vulnerabilities, such as leaking user state across circuits.

You can use stateful singleton services in Blazor apps if they are specifically designed for it. For example, it's ok to use a memory cache as a singleton because it requires a key to access a given entry, assuming users don't have control of what cache keys are used.

Additionally, again for security reasons, you must not use IHttpContextAccessor within Blazor apps. Blazor apps run outside of the context of the ASP.NET Core pipeline and the HttpContext isn't guaranteed to be available within the IHttpContextAccessor, nor it is guaranteed to be holding the context that started the Blazor app.

The recommended way to pass request state to the Blazor app is through parameters to the root component in the initial rendering of the app:

  • Define a class with all the data you want to pass to the Blazor app.
  • Populate that data from the Razor page using the HttpContext available at that time.
  • Pass the data to the Blazor app as a parameter to the root component (App).
  • Define a parameter in the root component to hold the data being passed to the app.
  • Use the user-specific data within the app; or alternatively, copy that data into a scoped service within OnInitializedAsync so that it can be used across the app.

For more information and example code, see ASP.NET Core Blazor Server additional security scenarios.