ASP.NET Core のミドルウェア

作成者: Rick AndersonSteve Smith

ミドルウェアとは、要求と応答を処理するために、アプリのパイプラインに組み込まれたソフトウェアです。 各コンポーネントで次の処理を実行します。

  • 要求をパイプライン内の次のコンポーネントに渡すかどうかを選択します。
  • パイプラインの次のコンポーネントの前または後に作業を実行できます。

要求デリゲートは、要求パイプラインの構築に使用されます。 要求デリゲートは、各 HTTP 要求を処理します。

要求デリゲートは、RunMapUse の各拡張メソッドを使って構成されます。 個々の要求デリゲートは、匿名メソッドとしてインラインで指定するか (インライン ミドルウェアと呼ばれます)、または再利用可能なクラスで定義することができます。 これらの再利用可能なクラスとインラインの匿名メソッドは、"ミドルウェア" です。"ミドルウェア コンポーネント" とも呼ばれます。 要求パイプライン内の各ミドルウェア コンポーネントは、パイプラインの次のコンポーネントを呼び出すか、パイプラインをショートさせます。 ショートサーキットしたミドルウェアは "ターミナル ミドルウェア" と呼ばれます。これによってさらなるミドルウェアによる要求の処理が回避されるためです。

HTTP ハンドラーとモジュールをミドルウェアに ASP.NET Coreする では、ASP.NET Core と ASP.NET 4.x の要求パイプラインの違いについて説明し、その他のミドルウェア サンプルが提供されています。

IApplicationBuilder を使用したミドルウェア パイプラインの作成

ASP.NET Core 要求パイプラインは、順番に呼び出される一連の要求デリゲートで構成されています。 次の図は、その概念を示しています。 実行のスレッドは黒い矢印をたどります。

要求の到着、3 つのミドルウェアによる処理、アプリからの応答の送信を示す要求処理パターン。 各ミドルウェアは、そのロジックを実行し、next() ステートメントで要求を次のミドルウェアに渡します。 3 番目のミドルウェアが要求を処理した後、要求は前の 2 つのミドルウェアを逆の順序で通過して、それぞれの next() ステートメントの後の追加処理が行われた後、クライアントへの応答としてアプリを終了します。

各デリゲートは、次のデリゲートの前と後に操作を実行できます。 例外処理デリゲートは、パイプラインの後のステージで発生する例外をキャッチできるように、パイプラインの早い段階で呼び出される必要があります。

考えられる最も簡単な ASP.NET Core アプリは、1 つの要求デリゲートを設定してすべての要求を処理するものです。 この場合、実際の要求パイプラインは含まれません。 代わりに、すべての HTTP 要求に対応して単一の匿名関数が呼び出されます。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

複数の要求デリゲートを Use と一緒にチェーンします。 next パラメーターは、パイプラインの次のデリゲートを表します next パラメーターを "呼び出さない" ことで、パイプラインをショートさせることができます。 次の例で示すように、通常は、次のデリゲートの前と後の両方でアクションを実行できます。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from 2nd delegate.");
        });
    }
}

デリゲートが次のデリゲートに要求を渡さない場合、これは "要求パイプラインのショートサーキット" と呼ばれます。 不要な処理を回避するために、ショートさせることが望ましい場合がよくあります。 たとえば、静的ファイル ミドルウェアは、静的ファイルの要求を処理して残りのパイプラインをショートサーキットすることにより、"ターミナル ミドルウェア" として動作させることができます。 後続の処理を終了させるミドルウェアの前にパイプラインに追加されたミドルウェアでは、その next.Invoke ステートメントの後も引き続きコードが処理されます。 ただし、既に送信された応答に対する書き込みの試行については、次の警告を参照してください。

警告

応答がクライアントに送信された後に、next.Invoke を呼び出さないでください。 応答が開始した後で HttpResponse を変更すると、例外がスローされます。 たとえば、ヘッダーや状態コードを設定すると、例外がスローされますnext を呼び出した後で応答本文に書き込むと、次のようになります。

  • プロトコル違反が発生する可能性があります。 たとえば、示されている Content-Length より多くを書き込んだ場合。
  • 本文の形式が破損する可能性があります。 たとえば、CSS ファイルに HTML フッターを書き込んだ場合。

HasStarted は、ヘッダーが送信されたかどうかや本文が書き込まれたかどうかを示すために役立つヒントです。

Run デリゲートでは、next パラメーターは受け取られません。 最初の Run デリゲートが常に終点となり、パイプラインが終了されます。 Run は規則です。 一部のミドルウェア コンポーネントでは、パイプラインの最後に実行される Run[Middleware] メソッドが公開されることがあります。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from 2nd delegate.");
        });
    }
}

コードのコメントを英語以外の言語に翻訳し表示したい場合、こちらの GitHub ディスカッション イシューにてお知らせください。

前の例では、Run デリゲートによって応答に "Hello from 2nd delegate." が書き込まれ、その後、パイプラインが終了となります。 別の Use または Run デリゲートが Run デリゲートの後に追加される場合、そのデリゲートは呼び出されません。

ミドルウェアの順序

次の図は、ASP.NET Core MVC と Razor Pages アプリの完全な要求処理パイプラインを示しています。 一般的なアプリでどのように既存のミドルウェアが順序付けされ、どこにカスタム ミドルウェアが追加されるかを確認できます。 シナリオでの必要性に応じて、既存のミドルウェアの順序を変更したり、新しいカスタム ミドルウェアを挿入したりする方法については、完全に制御できます。

ASP.NET Core のミドルウェア パイプライン

前の図の エンドポイント ミドルウェアでは、対応するアプリの種類 (MVC または Razor Pages) のフィルター パイプラインが実行されます。

ASP.NET Core のフィルター パイプライン

Startup.Configure メソッドでミドルウェア コンポーネントを追加する順序は、要求でミドルウェア コンポーネントが呼び出される順序および応答での逆の順序を定義します。 この順序は、セキュリティ、パフォーマンス、および機能にとって 重要 です。

次の Startup.Configure メソッドでは、セキュリティ関連のミドルウェア コンポーネントが、一般的な推奨される順序で追加されています。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    // app.UseCookiePolicy();

    app.UseRouting();
    // app.UseRequestLocalization();
    // app.UseCors();

    app.UseAuthentication();
    app.UseAuthorization();
    // app.UseSession();
    // app.UseResponseCompression();
    // app.UseResponseCaching();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

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

  • 個人のユーザー アカウントを使用して新しい Web アプリを作成するときに追加されないミドルウェアは、コメント アウトされています。
  • すべてのミドルウェアが厳密にこの順序で配置されるわけではありませんが、多くの場合はそのように配置されます。 次に例を示します。
    • UseCorsUseAuthentication、および UseAuthorization は、示されている順序で配置する必要があります。
    • 現時点では、このバグのため、UseCorsUseResponseCaching の前に配置する必要があります。
    • UseRequestLocalization は、要求のカルチャをチェックする可能性があるすべてのミドルウェア (たとえば、app.UseMvcWithDefaultRoute()) の前に配置する必要があります。

シナリオによっては、ミドルウェアの順序が異なる場合があります。 たとえば、キャッシュと圧縮の順序はシナリオ固有のものであり、複数の有効な順序があります。 次に例を示します。

app.UseResponseCaching();
app.UseResponseCompression();

上記のコードでは、圧縮された応答をキャッシュすることによって CPU を節約できますが、Gzip や Brotli などの異なる圧縮アルゴリズムを使用してリソースの複数の表現をキャッシュすることができます。

次の順序では、圧縮された静的ファイルをキャッシュできるように、静的ファイルが結合されます。

app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();

次の Startup.Configure メソッドにより、共通アプリ シナリオのためのミドルウェア コンポーネントが追加されます。

  1. 例外/エラー処理
    • 開発環境でアプリを実行する場合:
      • 開発者例外ページ ミドルウェア (UseDeveloperExceptionPage) によりアプリの実行時エラーが報告されます。
      • データベース エラー ページ ミドルウェアによりデータベースの実行時エラーが報告されます。
    • 運用環境でアプリを実行する場合:
      • 例外ハンドラー ミドルウェア (UseExceptionHandler) によって、後続のミドルウェアによってスローされた例外がキャッチされます。
      • HTTP Strict Transport Security プロトコル (HSTS) ミドルウェア (UseHsts) により Strict-Transport-Security ヘッダーが追加されます。
  2. HTTPS リダイレクト ミドルウェア (UseHttpsRedirection) により、HTTP 要求が HTTPS にリダイレクトされます。
  3. 静的ファイル ミドルウェア (UseStaticFiles) によって静的ファイルが返され、さらなる要求の処理がスキップされます。
  4. Cookie ポリシー ミドルウェア (UseCookiePolicy) により、アプリを EU の一般データ保護規制 (GDPR) に準拠させます。
  5. ルーティング ミドルウェア (UseRouting) により、要求がルーティングされます。
  6. 認証ミドルウェア (UseAuthentication) により、ユーザーがセキュリティで保護されたリソースにアクセスする前に、ユーザーの認証が試行されます。
  7. 承認ミドルウェア (UseAuthorization) により、ユーザーがセキュリティで保護されたリソースにアクセスすることが承認されます。
  8. セッション ミドルウェア (UseSession) により、セッション状態が確立され保持されます。 アプリでセッション状態が使用されている場合は、Cookie ポリシー ミドルウェアの後、MVC ミドルウェアの前に、セッション ミドルウェアを呼び出します。
  9. エンドポイント ルーティング ミドルウェア (MapRazorPages を含む UseEndpoints) により、Razor Pages エンドポイントが要求パイプラインに追加されます。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseSession();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

前のコード例では、各ミドルウェアの拡張メソッドが Microsoft.AspNetCore.Builder 名前空間を通じて IApplicationBuilder で公開されています。

パイプラインに追加された最初のミドルウェア コンポーネントは UseExceptionHandler です。 そのため、例外ハンドラー ミドルウェアでは、以降の呼び出しで発生するすべての例外がキャッチされます。

静的ファイル ミドルウェアはパイプラインの早い段階で呼び出されるので、要求を処理し、残りのコンポーネントを通過せずにショートさせることができます。 静的ファイル ミドルウェアでは、承認チェックは提供 されませんwwwroot の下にあるものも含め、この静的ファイル ミドルウェアによって提供されるすべてのファイルは、一般に公開されます。 静的ファイルを保護する方法については、「ASP.NET Core の静的ファイル」を参照してください。

要求が静的ファイル ミドルウェアによって処理されない場合、要求は認証を実行する認証ミドルウェア (UseAuthentication) に渡されます。 認証は、認証されない要求をショートさせません。 認証ミドルウェアは要求を認証しますが、承認 (および却下) は、MVC が特定の Razor ページまたは MVC コントローラーとアクションを選んだ後でのみ行われます。

次の例は、静的ファイルの要求が応答圧縮ミドルウェアの前に静的ファイル ミドルウェアによって処理される、ミドルウェアの順序を示します。 静的ファイルは、このミドルウェアの順序では圧縮されません。 Razor Pages の応答は圧縮できます。

public void Configure(IApplicationBuilder app)
{
    // Static files aren't compressed by Static File Middleware.
    app.UseStaticFiles();

    app.UseRouting();

    app.UseResponseCompression();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

シングルページ アプリケーション (SPA) の場合、通常はミドルウェア パイプラインの最後に SPA ミドルウェア UseSpaStaticFiles を配置します。 SPA ミドルウェアは、次のために最後になります。

  • 対応する要求に最初にその他のミドルウェアが応答するため。
  • クライアント側ルーティングを使用する SPA がサーバー アプリに認識されないすべてのルートを実行するため。

SPA の詳細については、ReactAngular プロジェクト テンプレートのガイドを参照してください。

Forwarded Headers Middleware の順序

Forwarded Headers Middleware は、他のミドルウェアの前に実行する必要があります。 この順序により、転送されるヘッダー情報に依存するミドルウェアが処理にヘッダー値を使用できます。 診断およびエラー処理ミドルウェアの後に Forwarded Headers Middleware を実行する方法については、「Forwarded Headers Middleware の順序」を参照してください。

ミドルウェア パイプラインを分岐する

Map 拡張メソッドは、パイプラインを分岐する規則として使われます。 Map は、指定された要求パスの一致に基づいて、要求パイプラインを分岐します。 要求パスが指定されたパスで開始する場合、分岐が実行されます。

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

次の表は、前のコードを使用した http://localhost:1234 からの要求および応答を示しています。

要求 応答
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

Map を使用すると、一致したパス セグメントが HttpRequest.Path から削除され、要求ごとに HttpRequest.PathBase に追加されます。

Map は入れ子をサポートします。次にその例を示します。

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map では、次のように一度に複数のセグメントを照合できます。

public class Startup
{
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

MapWhen は、指定された述語の結果に基づいて、要求パイプラインを分岐します。 Func<HttpContext, bool> という型の任意の述語を使って、要求をパイプラインの新しい分岐にマップできます。 次の例では、クエリ文字列変数 branch の存在を検出するために術後が使用されます。

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

次の表は、前のコードを使用した http://localhost:1234 からの要求および応答を示しています。

要求 応答
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

また UseWhen では、指定された述語の結果に基づいて、要求パイプラインが分岐されます。 MapWhen とは異なり、この分岐は、ショートしたり、ターミナル ミドルウェアが含まれたりしなければ、メイン パイプラインに再参加します。

public class Startup
{
    private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.Use(async (context, next) =>
        {
            var branchVer = context.Request.Query["branch"];
            logger.LogInformation("Branch used = {branchVer}", branchVer);

            // Do work that doesn't write to the Response.
            await next();
            // Do other work that doesn't write to the Response.
        });
    }

    public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
    {
        app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
                               appBuilder => HandleBranchAndRejoin(appBuilder, logger));

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from main pipeline.");
        });
    }
}

前の例では、"Hello from main pipeline." の応答が すべての要求に対して書き込まれます。 要求にクエリ文字列変数 branch が含まれる場合、メイン パイプラインの再参加前にその値がログに記録されます。

組み込みミドルウェア

ASP.NET Core には、次のミドルウェア コンポーネントが付属しています。 "順番" 列には、要求を処理するパイプライン内のミドルウェアの配置と、ミドルウェアが要求処理を終了する条件に関するメモが記載されています。 ミドルウェアが要求を処理するパイプラインをショートサーキットし、下流のさらなるミドルウェアによる要求の処理を回避する場合、これは "ターミナル ミドルウェア" と呼ばれます。 ショートサーキットについて詳しくは、「IApplicationBuilder を使用したミドルウェア パイプラインの作成」セクションをご覧ください。

ミドルウェア 説明 順番
認証 認証のサポートを提供します。 HttpContext.User が必要な場所の前。 OAuth コールバックの終端。
承認 承認のサポートを提供します。 認証ミドルウェアの直後。
Cookie ポリシー 個人情報の保存に関してユーザーからの同意を追跡し、secureSameSite など、cookie フィールドの最小要件を適用します。 cookie を発行するミドルウェアの前。 次に例を示します。 認証、セッション、MVC (TempData)
CORS クロス オリジン リソース共有を構成します。 CORS を使うコンポーネントの前。 現時点では、こちらのバグのため、UseResponseCaching の前に UseCors を追加する必要があります。
診断 開発者の例外ページ、例外処理、状態コード ページ、および新しいアプリの既定の Web ページを提供する複数の独立したミドルウェア。 エラーを生成するコンポーネントの前。 例外または新しいアプリ用の既定の Web ページの提供の終端。
転送されるヘッダー プロキシされたヘッダーを現在の要求に転送します。 更新されたフィールドを使用するコンポーネントの前。 例: スキーム、ホスト、クライアント IP、メソッド。
正常性チェック ASP.NET Core アプリとその依存関係の正常性を、データベースの可用性などで確認します。 要求が正常性チェックのエンドポイントと一致した場合の終端。
ヘッダーの伝達 HTTP ヘッダーを受信要求から送信 HTTP クライアント要求に伝達します。
HTTP メソッドのオーバーライド メソッドをオーバーライドする受信 POST 要求を許可します。 更新されたメソッドを使うコンポーネントの前。
HTTPS リダイレクト すべての HTTP 要求を HTTPS にリダイレクトします。 URL を使うコンポーネントの前。
HTTP Strict Transport Security (HSTS) 特殊な応答ヘッダーを追加するセキュリティ拡張機能のミドルウェア。 応答が送信される前と要求を変更するコンポーネントの後。 次に例を示します。 転送されるヘッダー、URL リライト。
MVC MVC または Razor Pages で要求を処理します。 要求がルートと一致した場合の終端。
OWIN OWIN ベースのアプリ、サーバー、およびミドルウェアと相互運用します。 OWIN ミドルウェアが要求を完全に処理した場合の終端。
応答キャッシュ 応答のキャッシュのサポートを提供します。 キャッシュが必要なコンポーネントの前。 UseResponseCaching の前に UseCORS を追加する必要があります。
応答圧縮 応答の圧縮のサポートを提供します。 圧縮が必要なコンポーネントの前。
要求のローカライズ ローカライズのサポートを提供します。 ローカリゼーションが重要なコンポーネントの前。 RouteDataRequestCultureProvider を使用する場合は、ルーティング ミドルウェアの後に配置する必要があり ます。
エンドポイント ルーティング 要求のルートを定義および制約します。 一致するルートの終端。
SPA シングルページ アプリケーション (SPA) の既定のページを返し、ミドルウェア チェーン内のこの時点以降のすべての要求を処理します。 チェーンの終わりで、静的ファイルや MVC アクションなどにサービスを提供する他のミドルウェアが優先されるようにするためです。
セッション ユーザー セッションの管理のサポートを提供します。 セッションが必要なコンポーネントの前。
静的ファイル 静的ファイルとディレクトリ参照に対応するサポートを提供します。 要求がファイルと一致した場合の終端。
URL 書き換え URL の書き換えと要求のリダイレクトのサポートを提供します。 URL を使うコンポーネントの前。
WebSocket WebSocket プロトコルを有効にします。 WebSocket 要求を受け入れる必要があるコンポーネントの前。

その他の技術情報

作成者: Rick AndersonSteve Smith

ミドルウェアとは、要求と応答を処理するために、アプリのパイプラインに組み込まれたソフトウェアです。 各コンポーネントで次の処理を実行します。

  • 要求をパイプライン内の次のコンポーネントに渡すかどうかを選択します。
  • パイプラインの次のコンポーネントの前または後に作業を実行できます。

要求デリゲートは、要求パイプラインの構築に使用されます。 要求デリゲートは、各 HTTP 要求を処理します。

要求デリゲートは、RunMapUse の各拡張メソッドを使って構成されます。 個々の要求デリゲートは、匿名メソッドとしてインラインで指定するか (インライン ミドルウェアと呼ばれます)、または再利用可能なクラスで定義することができます。 これらの再利用可能なクラスとインラインの匿名メソッドは、"ミドルウェア" です。"ミドルウェア コンポーネント" とも呼ばれます。 要求パイプライン内の各ミドルウェア コンポーネントは、パイプラインの次のコンポーネントを呼び出すか、パイプラインをショートさせます。 ショートサーキットしたミドルウェアは "ターミナル ミドルウェア" と呼ばれます。これによってさらなるミドルウェアによる要求の処理が回避されるためです。

HTTP ハンドラーとモジュールをミドルウェアに ASP.NET Coreする では、ASP.NET Core と ASP.NET 4.x の要求パイプラインの違いについて説明し、その他のミドルウェア サンプルが提供されています。

IApplicationBuilder を使用したミドルウェア パイプラインの作成

ASP.NET Core 要求パイプラインは、順番に呼び出される一連の要求デリゲートで構成されています。 次の図は、その概念を示しています。 実行のスレッドは黒い矢印をたどります。

要求の到着、3 つのミドルウェアによる処理、アプリからの応答の送信を示す要求処理パターン。 各ミドルウェアは、そのロジックを実行し、next() ステートメントで要求を次のミドルウェアに渡します。 3 番目のミドルウェアが要求を処理した後、要求は前の 2 つのミドルウェアを逆の順序で通過して、それぞれの next() ステートメントの後の追加処理が行われた後、クライアントへの応答としてアプリを終了します。

各デリゲートは、次のデリゲートの前と後に操作を実行できます。 例外処理デリゲートは、パイプラインの後のステージで発生する例外をキャッチできるように、パイプラインの早い段階で呼び出される必要があります。

考えられる最も簡単な ASP.NET Core アプリは、1 つの要求デリゲートを設定してすべての要求を処理するものです。 この場合、実際の要求パイプラインは含まれません。 代わりに、すべての HTTP 要求に対応して単一の匿名関数が呼び出されます。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

最初の Run デリゲートが、パイプラインを終了します。

複数の要求デリゲートを Use と一緒にチェーンします。 next パラメーターは、パイプラインの次のデリゲートを表します next パラメーターを "呼び出さない" ことで、パイプラインをショートさせることができます。 次の例で示すように、通常は、次のデリゲートの前と後の両方でアクションを実行できます。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from 2nd delegate.");
        });
    }
}

デリゲートが次のデリゲートに要求を渡さない場合、これは "要求パイプラインのショートサーキット" と呼ばれます。 不要な処理を回避するために、ショートさせることが望ましい場合がよくあります。 たとえば、静的ファイル ミドルウェアは、静的ファイルの要求を処理して残りのパイプラインをショートサーキットすることにより、"ターミナル ミドルウェア" として動作させることができます。 後続の処理を終了させるミドルウェアの前にパイプラインに追加されたミドルウェアでは、その next.Invoke ステートメントの後も引き続きコードが処理されます。 ただし、既に送信された応答に対する書き込みの試行については、次の警告を参照してください。

警告

応答がクライアントに送信された後に、next.Invoke を呼び出さないでください。 応答が開始した後で HttpResponse を変更すると、例外がスローされます。 たとえば、ヘッダーや状態コードの設定などの変更があると、例外がスローされます。 next を呼び出した後で応答本文に書き込むと、次のようになります。

  • プロトコル違反が発生する可能性があります。 たとえば、示されている Content-Length より多くを書き込んだ場合。
  • 本文の形式が破損する可能性があります。 たとえば、CSS ファイルに HTML フッターを書き込んだ場合。

HasStarted は、ヘッダーが送信されたかどうかや本文が書き込まれたかどうかを示すために役立つヒントです。

ミドルウェアの順序

Startup.Configure メソッドでミドルウェア コンポーネントを追加する順序は、要求でミドルウェア コンポーネントが呼び出される順序および応答での逆の順序を定義します。 この順序は、セキュリティ、パフォーマンス、および機能にとって 重要 です。

次の Startup.Configure メソッドでは、セキュリティ関連のミドルウェア コンポーネントが推奨される順序で追加されています。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    // app.UseRequestLocalization();
    // app.UseCors();

    app.UseAuthentication();
    // app.UseSession();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

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

  • 個人のユーザー アカウントを使用して新しい Web アプリを作成するときに追加されないミドルウェアは、コメント アウトされています。
  • すべてのミドルウェアを厳密にこの順序で追加する必要があるわけではありませんが、多くはそうする必要があります。 たとえば、UseCorsUseAuthentication は、示されている順序で追加する必要があります。

次の Startup.Configure メソッドにより、共通アプリ シナリオのためのミドルウェア コンポーネントが追加されます。

  1. 例外/エラー処理
    • 開発環境でアプリを実行する場合:
      • 開発者例外ページ ミドルウェア (UseDeveloperExceptionPage) によりアプリの実行時エラーが報告されます。
      • データベース エラー ページ ミドルウェア (Microsoft.AspNetCore.Builder.DatabaseErrorPageExtensions.UseDatabaseErrorPage) によりデータベースの実行時エラーが報告されます。
    • 運用環境でアプリを実行する場合:
      • 例外ハンドラー ミドルウェア (UseExceptionHandler) によって、後続のミドルウェアによってスローされた例外がキャッチされます。
      • HTTP Strict Transport Security プロトコル (HSTS) ミドルウェア (UseHsts) により Strict-Transport-Security ヘッダーが追加されます。
  2. HTTPS リダイレクト ミドルウェア (UseHttpsRedirection) により、HTTP 要求が HTTPS にリダイレクトされます。
  3. 静的ファイル ミドルウェア (UseStaticFiles) によって静的ファイルが返され、さらなる要求の処理がスキップされます。
  4. Cookie ポリシー ミドルウェア (UseCookiePolicy) により、アプリを EU の一般データ保護規制 (GDPR) に準拠させます。
  5. 認証ミドルウェア (UseAuthentication) により、ユーザーがセキュリティで保護されたリソースにアクセスする前に、ユーザーの認証が試行されます。
  6. セッション ミドルウェア (UseSession) により、セッション状態が確立され保持されます。 アプリでセッション状態が使用されている場合は、Cookie ポリシー ミドルウェアの後、MVC ミドルウェアの前に、セッション ミドルウェアを呼び出します。
  7. MVC (UseMvc) を使い、要求パイプラインに MVC を追加します。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseSession();
    app.UseMvc();
}

前のコード例では、各ミドルウェアの拡張メソッドが Microsoft.AspNetCore.Builder 名前空間を通じて IApplicationBuilder で公開されています。

パイプラインに追加された最初のミドルウェア コンポーネントは UseExceptionHandler です。 そのため、例外ハンドラー ミドルウェアでは、以降の呼び出しで発生するすべての例外がキャッチされます。

静的ファイル ミドルウェアはパイプラインの早い段階で呼び出されるので、要求を処理し、残りのコンポーネントを通過せずにショートさせることができます。 静的ファイル ミドルウェアでは、承認チェックは提供 されませんwwwroot の下にあるものも含め、この静的ファイル ミドルウェアによって提供されるすべてのファイルは、一般に公開されます。 静的ファイルを保護する方法については、「ASP.NET Core の静的ファイル」を参照してください。

要求が静的ファイル ミドルウェアによって処理されない場合、要求は認証を実行する認証ミドルウェア (UseAuthentication) に渡されます。 認証は、認証されない要求をショートさせません。 認証ミドルウェアは要求を認証しますが、承認 (および却下) は、MVC が特定の Razor ページまたは MVC コントローラーとアクションを選んだ後でのみ行われます。

次の例は、静的ファイルの要求が応答圧縮ミドルウェアの前に静的ファイル ミドルウェアによって処理される、ミドルウェアの順序を示します。 静的ファイルは、このミドルウェアの順序では圧縮されません。 UseMvcWithDefaultRoute からの MVC 応答を圧縮することができます。

public void Configure(IApplicationBuilder app)
{
    // Static files aren't compressed by Static File Middleware.
    app.UseStaticFiles();

    app.UseResponseCompression();

    app.UseMvcWithDefaultRoute();
}

Use、Run、および Map

HTTP パイプラインを構成するには、UseRunMap を使います。 Use メソッドは、パイプラインをショートさせることができます (つまり next 要求デリゲートを呼び出さない場合)。 Run は規則であり、一部のミドルウェア コンポーネントは、パイプラインの最後に実行される Run[Middleware] メソッドを公開することがあります。

Map 拡張メソッドは、パイプラインを分岐する規則として使われます。 Map は、指定された要求パスの一致に基づいて、要求パイプラインを分岐します。 要求パスが指定されたパスで開始する場合、分岐が実行されます。

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

次の表は、前のコードを使用した http://localhost:1234 からの要求および応答を示しています。

要求 応答
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

Map を使用すると、一致したパス セグメントが HttpRequest.Path から削除され、要求ごとに HttpRequest.PathBase に追加されます。

MapWhen は、指定された述語の結果に基づいて、要求パイプラインを分岐します。 Func<HttpContext, bool> という型の任意の述語を使って、要求をパイプラインの新しい分岐にマップできます。 次の例では、クエリ文字列変数 branch の存在を検出するために術後が使用されます。

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

次の表は、前のコードを使用した http://localhost:1234 からの要求および応答を示しています。

要求 応答
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=main Branch used = main

Map は入れ子をサポートします。次にその例を示します。

app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});

Map では、次のように一度に複数のセグメントを照合できます。

public class Startup
{
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

組み込みミドルウェア

ASP.NET Core には、次のミドルウェア コンポーネントが付属しています。 "順番" 列には、要求を処理するパイプライン内のミドルウェアの配置と、ミドルウェアが要求処理を終了する条件に関するメモが記載されています。 ミドルウェアが要求を処理するパイプラインをショートサーキットし、下流のさらなるミドルウェアによる要求の処理を回避する場合、これは "ターミナル ミドルウェア" と呼ばれます。 ショートサーキットについて詳しくは、「IApplicationBuilder を使用したミドルウェア パイプラインの作成」セクションをご覧ください。

ミドルウェア 説明 順番
認証 認証のサポートを提供します。 HttpContext.User が必要な場所の前。 OAuth コールバックの終端。
Cookie ポリシー 個人情報の保存に関してユーザーからの同意を追跡し、secureSameSite など、cookie フィールドの最小要件を適用します。 cookie を発行するミドルウェアの前。 次に例を示します。 認証、セッション、MVC (TempData)
CORS クロス オリジン リソース共有を構成します。 CORS を使うコンポーネントの前。
診断 開発者の例外ページ、例外処理、状態コード ページ、および新しいアプリの既定の Web ページを提供する複数の独立したミドルウェア。 エラーを生成するコンポーネントの前。 例外または新しいアプリ用の既定の Web ページの提供の終端。
転送されるヘッダー プロキシされたヘッダーを現在の要求に転送します。 更新されたフィールドを使用するコンポーネントの前。 例: スキーム、ホスト、クライアント IP、メソッド。
正常性チェック ASP.NET Core アプリとその依存関係の正常性を、データベースの可用性などで確認します。 要求が正常性チェックのエンドポイントと一致した場合の終端。
HTTP メソッドのオーバーライド メソッドをオーバーライドする受信 POST 要求を許可します。 更新されたメソッドを使うコンポーネントの前。
HTTPS リダイレクト すべての HTTP 要求を HTTPS にリダイレクトします。 URL を使うコンポーネントの前。
HTTP Strict Transport Security (HSTS) 特殊な応答ヘッダーを追加するセキュリティ拡張機能のミドルウェア。 応答が送信される前と要求を変更するコンポーネントの後。 次に例を示します。 転送されるヘッダー、URL リライト。
MVC MVC または Razor Pages で要求を処理します。 要求がルートと一致した場合の終端。
OWIN OWIN ベースのアプリ、サーバー、およびミドルウェアと相互運用します。 OWIN ミドルウェアが要求を完全に処理した場合の終端。
応答キャッシュ 応答のキャッシュのサポートを提供します。 キャッシュが必要なコンポーネントの前。
応答圧縮 応答の圧縮のサポートを提供します。 圧縮が必要なコンポーネントの前。
要求のローカライズ ローカライズのサポートを提供します。 ローカリゼーションが重要なコンポーネントの前。
エンドポイント ルーティング 要求のルートを定義および制約します。 一致するルートの終端。
セッション ユーザー セッションの管理のサポートを提供します。 セッションが必要なコンポーネントの前。
静的ファイル 静的ファイルとディレクトリ参照に対応するサポートを提供します。 要求がファイルと一致した場合の終端。
URL 書き換え URL の書き換えと要求のリダイレクトのサポートを提供します。 URL を使うコンポーネントの前。
WebSocket WebSocket プロトコルを有効にします。 WebSocket 要求を受け入れる必要があるコンポーネントの前。

その他の技術情報