ミドルウェアについて理解する

完了

Web アプリケーションの目的は、HTTP 要求を受信して応答することです。 要求が受信され、サーバーによって適切な応答が生成されます。 ASP.NET Core 内のあらゆるものが、この要求と応答のサイクルに関係しています。

ASP.NET Core アプリが HTTP 要求を受信すると、それが一連のコンポーネントを通過して応答が生成されます。 これらのコンポーネントはミドルウェアと呼ばれます。 ミドルウェアは、要求が流れるパイプラインと考えることができます。各ミドルウェア レイヤーは、パイプライン内の次のレイヤーの前後にコードを実行できます。

複数のミドルウェアである HTTP 要求を示す図。

ミドルウェアとデリゲート

ミドルウェアは、HttpContext オブジェクトを受け取り、Task を返すデリゲートとして実装されます。 HttpContext オブジェクトは、現在の要求と応答を表します。 デリゲートは、要求と応答を処理する機能です。

表すクレソン、ダン橄欖岩製品構文解析木作法:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

上のコードでは以下の操作が行われます。

  • WebApplication.CreateBuilder(args) で新しい WebApplicationBuilder オブジェクトを作成します。
  • builder.Build() で新しい WebApplication オブジェクトを作成します。
  • 最初の app.Run() は、HttpContext オブジェクトを受け取って Task を返すデリゲートを定義します。 デリゲートは、応答に "Hello world!" と書き込みます。
  • 2 番目の app.Run() はアプリを開始します。

アプリが HTTP 要求を受信すると、デリゲートが呼び出されます。 デリゲートは"Hello world!" を応答に書き込み、要求を完了します。

ミドルウェアのチェーン

ほとんどのアプリでは、複数のミドルウェア コンポーネントが順番に実行されます。 ミドルウェア コンポーネントをパイプラインに追加する順序は重要です。 コンポーネントは、追加された順序で実行されます。

終端と非終端のミドルウェア

各ミドルウェアは、終端または非終端と考えることができます。 非終端ミドルウェアは要求を処理し、パイプライン内の次のミドルウェアを呼び出します。 終端ミドルウェアはパイプラインの最後のミドルウェアであり、呼び出す次のミドルウェアはありません。

app.Use() で追加されるデリゲートは、終端または非終端ミドルウェアとすることができます。 これらのデリゲートには、パラメーターとして HttpContext オブジェクトと RequestDelegate オブジェクトが必要です。 通常、デリゲートには await next.Invoke(); が含まれます。 これにより、パイプライン上の次のミドルウェアに制御が渡されます。 その行の前のコードは、次のミドルウェアの前に実行され、その行の後のコードは、次のミドルウェアの後で実行されます。 app.Use() で追加されたデリゲートには、応答がクライアントに送信される前に、要求を処理する機会が 2 回あります。1 回は終端ミドルウェアによって応答が生成される前、もう 1 回は終端ミドルウェアによって応答が生成された後です。

app.Run() で追加されるデリゲートは、常に終端ミドルウェアです。 パイプライン内の次のミドルウェアを呼び出すことはありません。 実行される最後のミドルウェア コンポーネントです。 パラメーターとして必要なのは HttpContext オブジェクトだけです。 app.Run() は、終端ミドルウェアを追加するための便利な方法です。

次の例を確認してください。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Hello from middleware 1. Passing to the next middleware!\r\n");

    // Call the next middleware in the pipeline
    await next.Invoke();

    await context.Response.WriteAsync("Hello from middleware 1 again!\r\n");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from middleware 2!\r\n");
});

app.Run();

上のコードでは以下の操作が行われます。

  • app.Use() は、次のようにミドルウェア コンポーネントを定義します。
    • "Hello from middleware 1. Passing to the next middleware!" を応答に書き込む。
    • 要求をパイプライン内の次のミドルウェア コンポーネントに渡し、await next.Invoke() で完了するまで待機する。
    • パイプライン内の次のコンポーネントの完了後、"Hello from middleware 1 again!" と書き込む。
  • 最初の app.Run() は、"Hello from middleware 2!" を応答に書き込むミドルウェア コンポーネントを定義します。
  • 2 番目の app.Run() はアプリを開始します。

実行時に Web ブラウザーからこのアプリに要求が送信されると、ミドルウェア コンポーネントはパイプラインに追加された順序で実行されます。 アプリから以下の応答が返されます。

Hello from middleware 1. Passing to the next middleware!
Hello from middleware 2!
Hello from middleware 1 again!

組み込みミドルウェア

ASP.NET Core には一連の組み込みミドルウェア コンポーネントが用意されていて、アプリに一般的な機能を追加するために使用できます。 明示的に追加されるミドルウェア コンポーネントに加えて、一部のミドルウェアは既定で暗黙的に追加されます。 たとえば、WebApplication.CreateBuilder()WebApplicationBuilder を返して、開発者例外ページへのルーティングを行うミドルウェアを追加し、関連するサービスが構成されている場合は条件付きで認証と承認のミドルウェアを追加し、エンドポイント ルーティング ミドルウェアを追加します。

たとえば、次の Program.cs ファイルについて考えてみましょう。

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseAntiforgery();

app.MapStaticAssets();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

上のコードでは以下の操作が行われます。

  • app.UseExceptionHandler() は、例外をキャッチしてエラー ページを返すミドルウェア コンポーネントを追加します。
  • app.UseHsts() は、Strict-Transport-Security ヘッダーを設定するミドルウェア コンポーネントを追加します。
  • app.UseHttpsRedirection() は、HTTP 要求を HTTPS にリダイレクトするミドルウェア コンポーネントを追加します。
  • app.UseAntiforgery() は、クロスサイト リクエスト フォージェリ (CSRF) 攻撃を防ぐミドルウェア コンポーネントを追加します。
  • app.MapStaticAssets()app.MapRazorComponents<App>() は、ルートをエンドポイントにマップします。それらはエンドポイント ルーティング ミドルウェアによって処理されます。 エンドポイント ルーティング ミドルウェアは、WebApplicationBuilder によって暗黙的に追加されます。

アプリの種類とニーズに応じてアプリで使用できる、さらに多くの組み込みミドルウェア コンポーネントがあります。 完全な一覧は、こちらのドキュメントで確認してください

ヒント

このコンテキストでは、Use で始まるメソッドは通常、マッピング ミドルウェア用です。 Map で始まるメソッドは通常、マッピング エンドポイント用です。

重要

ミドルウェア コンポーネントがパイプラインに追加される順序は重要です。 特定のミドルウェア コンポーネントは、正しく動作するために他のものより前に実行される必要があります。 各ミドルウェア コンポーネントのドキュメントを確認して、正しい順序を決定してください。