Prepare HttpContext for 6.0 (#24133)

pull/24135/head
Kirk Larkin 2021-11-30 15:57:18 +00:00 committed by GitHub
parent aa7755269f
commit 64ad0dc9a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 179 additions and 39 deletions

View File

@ -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 <xref:Microsoft.AspNetCore.Http.IHttpContextAccessor> interface and its default implementation <xref:Microsoft.AspNetCore.Http.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 <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel> exposes the <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel.HttpContext> property:
The Razor Pages <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel> exposes the <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel.HttpContext?displayProperty=nameWithType> 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 <xref:Microsoft.AspNetCore.Mvc.Razor.RazorPage.Context%2A?displayProperty=nameWithType> 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 <xref:Microsoft.AspNetCore.Mvc.ControllerBase.HttpContext%2A?displayProperty=nameWithType> 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<IUserRepository, UserRepository>();
}
```
::: 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 <xref:Microsoft.AspNetCore.Http.IHttpContextAccessor> interface and its default implementation <xref:Microsoft.AspNetCore.Http.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 <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel> exposes the <xref:Microsoft.AspNetCore.Mvc.RazorPages.PageModel.HttpContext?displayProperty=nameWithType> 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 <xref:Microsoft.AspNetCore.Mvc.Razor.RazorPage.Context%2A?displayProperty=nameWithType> 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 <xref:Microsoft.AspNetCore.Mvc.ControllerBase.HttpContext%2A?displayProperty=nameWithType> 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<IUserRepository, UserRepository>();
}
```
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 <xref:System.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 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