ASP.NET Core で HttpContext にアクセスする

ASP.NET Core アプリでは、IHttpContextAccessor インターフェイスと、その既定の実装 HttpContextAccessor を介して HttpContext にアクセスします。 IHttpContextAccessor を使用する必要があるのは、サービス内の HttpContext にアクセスする必要がある場合のみです。

Razor Pages から HttpContext を使用する

Razor Pages PageModel では、HttpContext プロパティが公開されます。

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

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

同じプロパティを対応する Razor ページ ビューで使用できます

@page
@model AboutModel
@{
    var message = HttpContext.Request.PathBase;
    
    ...
}

MVC で Razor ビューから HttpContext を使用する

MVC パターンの Razor ビューには、ビューの RazorPage.Context プロパティを使用して HttpContext が公開されます。 次の例では、Windows 認証を使用して、イントラネット アプリで現在のユーザー名を取得します。

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

コントローラーから HttpContext を使用する

コントローラーでは ControllerBase.HttpContext プロパティが公開されます。

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

        ...

        return View();
    }
}

ミドルウェアから HttpContext を使用する

カスタム ミドルウェア コンポーネントを使用する場合、HttpContextInvoke メソッドまたは InvokeAsync メソッドに渡され、ミドルウェアを構成する際にアクセスできます。

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

カスタム コンポーネントから HttpContext を使用する

HttpContext へのアクセスを必要とするその他のフレームワークおよびカスタム コンポーネントに対して推奨される方法は、組み込みの依存関係の挿入コンテナーを使用して依存関係を登録することです。 依存関係の挿入コンテナーは、それぞれのコンストラクター内で IHttpContextAccessor を依存関係として宣言するすべてのクラスに、これを提供します。

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>();
}

次に例を示します。

  • UserRepositoryIHttpContextAccessor に対する依存関係を宣言します。
  • 依存関係の挿入で依存関係のチェーンが解決され、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 にアクセスする

HttpContext はスレッド セーフではありません。 要求の処理以外で HttpContext のプロパティを読み書きすると、結果的に NullReferenceException になることがあります。

注意

アプリで NullReferenceException エラーが散発的に生成される場合、コードの中で、バックグラウンド処理を開始する部分や要求完了後に処理を続行する部分を見直してください。 コントローラー メソッドを async void として定義するなどの間違いを探します。

HttpContext データでバックグラウンド作業を安全に実行するには:

  • 要求処理中に必要なデータをコピーします。
  • コピーしたデータをバックグラウンド タスクに渡します。
  • 並列タスクで HttpContext データを参照 "しないで" ください。 必要なデータは、並列タスクを開始する前にコンテキストから抽出してください。

アンセーフ コードを避けるために、バックグラウンド処理を実行しないメソッドには HttpContext を決して渡さないでください。 代わりに必要なデータを渡してください。 次の例では、電子メールの送信を開始するために SendEmailCore が呼び出されます。 correlationId は、HttpContext ではなく SendEmailCore に渡されます。 コードの実行では、SendEmailCore が完了するのを待機しません。

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 と共有状態

Blazor サーバー アプリはサーバー メモリに存在します。 これは、同じプロセス内で複数のアプリがホストされていることを意味します。 Blazor では各アプリ セッションに対して、独自の DI コンテナー スコープで回線が開始されます。 つまり、スコープが指定されたサービスは、Blazor セッションごとに一意になるということです。

警告

特別な注意を払っていない限り、同じサーバーの共有状態のアプリがシングルトン サービスを使用することはお勧めできません。これにより、回線をまたいだユーザー状態のリークなど、セキュリティ上の脆弱性が生じる可能性があります。

Blazor アプリでは、ステートフル シングルトン サービスを使用できます (特に専用に設計されている場合)。 たとえば、メモリ キャッシュをシングルトンとして使用するのは問題ありません。これは、使用されるキャッシュ キーをユーザーが制御できない場合に、特定のエントリにアクセスするためにキーが必要になるためです。

また、セキュリティ上の理由から、Blazor アプリ内で IHttpContextAccessor を使用することはできません。 Blazor アプリは、ASP.NET Core パイプラインのコンテキストの外部で実行されます。 HttpContext は、IHttpContextAccessor 内で使用できるとは限りません。また、Blazor アプリが開始されたコンテキストが保持されることも保証されません。

Blazor アプリに要求状態を渡す方法としては、アプリの初期レンダリングでルート コンポーネントのパラメーターを使用することをお勧めします。

  • Blazor アプリに渡すデータをすべて含むクラスを定義します。
  • その時点で利用可能な HttpContext を使用して、Razor ページからそのデータを設定します。
  • ルート コンポーネント (アプリ) へのパラメーターとして Blazor アプリにデータを渡します。
  • アプリに渡されるデータを保持するために、ルート コンポーネントでパラメーターを定義します。
  • アプリ内のユーザー固有のデータを使用します。または、アプリ全体で使用できるようにそのデータを OnInitializedAsync 内のスコープ サービスにコピーします。

詳細およびプログラム例については、「ASP.NET Core Blazor Server のセキュリティに関するその他のシナリオ」を参照してください。