在 ASP.NET Core 中访问 HttpContextAccess HttpContext in ASP.NET Core

ASP.NET Core 应用通过 IHttpContextAccessor 接口及其默认实现 HttpContextAccessor 访问 HttpContextASP.NET Core apps access HttpContext through the IHttpContextAccessor interface and its default implementation HttpContextAccessor. 只有在需要访问服务内的 HttpContext 时,才有必要使用 IHttpContextAccessorIt's only necessary to use IHttpContextAccessor when you need access to the HttpContext inside a service.

通过 Razor Pages 使用 HttpContextUse HttpContext from Razor Pages

Razor Pages PageModel 公开 HttpContext 属性:The Razor Pages PageModel exposes the HttpContext property:

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

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

通过 Razor 视图使用 HttpContextUse HttpContext from a Razor view

Razor 视图通过视图上的 RazorPage.Context 属性直接公开 HttpContext views expose the HttpContext directly via a RazorPage.Context property on the view. 下面的示例使用 Windows 身份验证检索 Intranet 应用中的当前用户名:The following example retrieves the current username in an intranet app using Windows Authentication:

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

通过控制器使用 HttpContextUse HttpContext from a controller

控制器公开 ControllerBase.HttpContext 属性:Controllers expose the ControllerBase.HttpContext property:

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

        ...

        return View();
    }
}

通过中间件使用 HttpContextUse HttpContext from middleware

使用自定义中间件组件时,HttpContext 传递到 InvokeInvokeAsync 方法,在中间件配置后可供访问: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)
    {
        ...
    }
}

通过自定义组件使用 HttpContextUse HttpContext from custom components

对于需要访问 HttpContext 的其他框架和自定义组件,建议使用内置的依赖项注入容器来注册依赖项。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. 依赖项注入容器向任意类提供 IHttpContextAccessor,以供类在自己的构造函数中将它声明为依赖项: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 声明自己对 IHttpContextAccessor 的依赖。UserRepository declares its dependency on IHttpContextAccessor.
  • 当依赖项注入容器解析依赖链并创建 UserRepository 实例时,就会注入依赖项。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);
    }
}

从后台线程访问 HttpContextHttpContext access from a background thread

HttpContext 不是线程安全型。HttpContext isn't thread-safe. 在处理请求之外读取或写入 HttpContext 的属性可能会导致 NullReferenceExceptionReading or writing properties of the HttpContext outside of processing a request can result in a NullReferenceException.

备注

如果应用生成偶发的 NullReferenceException 错误,请评审启动后台处理的部分代码,或者在请求完成后继续处理的部分代码。If your app generates sporadic NullReferenceException errors, review parts of the code that start background processing or that continue processing after a request completes. 查找诸如将控制器方法定义为 async void 的错误。Look for mistakes, such as defining a controller method as async void.

要使用 HttpContext 数据安全地执行后台工作,请执行以下操作:To safely perform background work with HttpContext data:

  • 在请求处理过程中复制所需的数据。Copy the required data during request processing.
  • 将复制的数据传递给后台任务。Pass the copied data to a background task.

要避免不安全代码,请勿将 HttpContext 传递给执行后台工作的方法。To avoid unsafe code, never pass the HttpContext into a method that performs background work. 而是传递所需要的数据。Pass the required data instead. 在以下示例中,调用 SendEmailCore,开始发送电子邮件。In the following example, SendEmailCore is called to start sending an email. correlationId 传递到 SendEmailCore,而不是 HttpContextThe correlationId is passed to SendEmailCore, not the HttpContext. 代码执行不会等待 SendEmailCore 完成: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 服务器应用位于服务器内存中。Blazor server apps live in server memory. 这意味着同一进程中托管了多个应用。That means that there are multiple apps hosted within the same process. 对于每个应用会话,Blazor 会启动具有其自己的 DI 容器作用域的线路。For each app session, Blazor starts a circuit with its own DI container scope. 这意味着,每个 Blazor 会话的作用域内服务都是唯一的。That means that scoped services are unique per Blazor session.

警告

我们不建议同一服务器上的应用共享使用单一实例服务的状态,除非采取了极其谨慎的措施,因为这可能会带来安全漏洞,如跨线路泄露用户状态。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.

如果有状态的单一实例服务是专门为 Blazor 应用设计的,则可以在该应用中使用这些服务。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.

另外,出于安全原因,不得在 Blazor 应用中使用 IHttpContextAccessorAdditionally, again for security reasons, you must not use IHttpContextAccessor within Blazor apps. Blazor 应用在 ASP.NET Core 管道的上下文之外运行。Blazor apps run outside of the context of the ASP.NET Core pipeline. HttpContext 既不保证在 IHttpContextAccessor 中可用,也不保证它会保留启动了 Blazor 应用的上下文。The HttpContext isn't guaranteed to be available within the IHttpContextAccessor, nor is it guaranteed to be holding the context that started the Blazor app.

若要向 Blazor 应用传递请求状态,建议在初次呈现应用时通过传递到根组件的参数进行传递: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:

  • 使用要传递到 Blazor 应用的所有数据定义类。Define a class with all the data you want to pass to the Blazor app.
  • 使用目前可用的 HttpContext 在 Razor 页中填充该数据。Populate that data from the Razor page using the HttpContext available at that time.
  • 将数据作为传递给根组件(应用)的参数传递给 Blazor 应用。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.
  • 在应用中使用用户特定的数据;或者,将该数据复制到 OnInitializedAsync 中的作用域内的服务,以便可以跨应用使用该数据。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.

有关更多信息及代码示例,请参见 ASP.NET Core [Blazor Server 其他安全方案For more information and example code, see ASP.NET Core [Blazor Server 其他安全方案.