diff --git a/aspnetcore/fundamentals/http-context.md b/aspnetcore/fundamentals/http-context.md index a2cda316a8..8337bead31 100644 --- a/aspnetcore/fundamentals/http-context.md +++ b/aspnetcore/fundamentals/http-context.md @@ -1,8 +1,8 @@ --- title: Access HttpContext in ASP.NET Core author: coderandhiker -description: HttpContext in ASP.NET Core. HttpContext is not thread-safe and can throw NullReferenceException. -monikerRange: '>= aspnetcore-2.1' +description: HttpContext in ASP.NET Core. HttpContext isn't thread-safe and can throw NullReferenceException. +monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 5/5/2020 @@ -11,52 +11,54 @@ uid: fundamentals/httpcontext --- # Access HttpContext in ASP.NET Core +:::moniker range="< aspnetcore-6.0" + ASP.NET Core apps access `HttpContext` through the interface and its default implementation . 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 exposes the property: +The Razor Pages exposes the property: ```csharp -public class AboutModel : PageModel +public class IndexModel : PageModel { - public string Message { get; set; } - public void OnGet() { - Message = HttpContext.Request.PathBase; + var message = HttpContext.Request.PathBase; + + // ... } } ``` -The same property can be used in the corresponding Razor Page View +The same property can be used in the corresponding Razor Page View: ```cshtml @page -@model AboutModel +@model IndexModel + @{ var message = HttpContext.Request.PathBase; - ... + // ... } ``` - ## Use HttpContext from a Razor view in MVC -Razor views in the MVC pattern expose the `HttpContext` via the [RazorPage.Context](xref:Microsoft.AspNetCore.Mvc.Razor.RazorPage.Context) property on the view. The following example retrieves the current username in an intranet app using Windows Authentication: +Razor views in the MVC pattern expose the `HttpContext` via the property on the view. The following example retrieves the current username in an intranet app using Windows Authentication: ```cshtml @{ var username = Context.User.Identity.Name; - ... + // ... } ``` ## Use HttpContext from a controller -Controllers expose the [ControllerBase.HttpContext](xref:Microsoft.AspNetCore.Mvc.ControllerBase.HttpContext) property: +Controllers expose the property: ```csharp public class HomeController : Controller @@ -65,7 +67,7 @@ public class HomeController : Controller { var pathBase = HttpContext.Request.PathBase; - ... + // ... return View(); } @@ -81,16 +83,14 @@ 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](xref:fundamentals/dependency-injection) container. The dependency injection container supplies the `IHttpContextAccessor` to any classes that declare it as a dependency in their constructors: - -::: moniker range=">= aspnetcore-3.0" +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 (DI)](xref:fundamentals/dependency-injection) container. The DI container supplies the `IHttpContextAccessor` to any classes that declare it as a dependency in their constructors: ```csharp public void ConfigureServices(IServiceCollection services) @@ -101,26 +101,10 @@ public void ConfigureServices(IServiceCollection services) } ``` -::: moniker-end - -::: moniker range="< aspnetcore-3.0" - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc() - .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - services.AddHttpContextAccessor(); - services.AddTransient(); -} -``` - -::: moniker-end - 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`. +* The dependency is supplied when DI resolves the dependency chain and creates an instance of `UserRepository`. ```csharp public class UserRepository : IUserRepository @@ -147,13 +131,13 @@ public class UserRepository : IUserRepository > [!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: +To safely do background work with `HttpContext` data: * Copy the required data during request processing. * Pass the copied data to a background task. * Do ***not*** reference `HttpContext` data in parallel tasks. Extract the data needed from the context before starting the parallel tasks. -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: +To avoid unsafe code, never pass the `HttpContext` into a method that does 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: ```csharp public class EmailController : Controller @@ -169,7 +153,7 @@ public class EmailController : Controller private async Task SendEmailCore(string correlationId) { - ... + // ... } } ``` @@ -177,3 +161,159 @@ public class EmailController : Controller ## Blazor and shared state [!INCLUDE[](~/blazor/security/includes/blazor-shared-state.md)] + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0" + +ASP.NET Core apps access `HttpContext` through the interface and its default implementation . 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 exposes the property: + +```csharp +public class IndexModel : PageModel +{ + public void OnGet() + { + var message = HttpContext.Request.PathBase; + + // ... + } +} +``` + +The same property can be used in the corresponding Razor Page View: + +```cshtml +@page +@model IndexModel + +@{ + var message = HttpContext.Request.PathBase; + + // ... +} +``` + +## Use HttpContext from a Razor view in MVC + +Razor views in the MVC pattern expose the `HttpContext` via the property on the view. The following example retrieves the current username in an intranet app using Windows Authentication: + +```cshtml +@{ + var username = Context.User.Identity.Name; + + // ... +} +``` + +## Use HttpContext from a controller + +Controllers expose the property: + +```csharp +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: + +```csharp +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 (DI)](xref:fundamentals/dependency-injection) container. The DI container supplies the `IHttpContextAccessor` to any classes that declare it as a dependency in their constructors: + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddControllersWithViews(); + services.AddHttpContextAccessor(); + services.AddTransient(); +} +``` + +In the following example: + +* `UserRepository` declares its dependency on `IHttpContextAccessor`. +* The dependency is supplied when DI resolves the dependency chain and creates an instance of `UserRepository`. + +```csharp +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 . + +> [!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 do background work with `HttpContext` data: + +* Copy the required data during request processing. +* Pass the copied data to a background task. +* Do ***not*** reference `HttpContext` data in parallel tasks. Extract the data needed from the context before starting the parallel tasks. + +To avoid unsafe code, never pass the `HttpContext` into a method that does 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: + +```csharp +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 + +[!INCLUDE[](~/blazor/security/includes/blazor-shared-state.md)] + +:::moniker-end +