演習 - カスタム ミドルウェアを作成する

完了

開発者は、カスタム ミドルウェア コンポーネントを作成して、ASP.NET Core アプリに機能を追加できます。 カスタム ミドルウェアはミドルウェア パイプライン内のどこにでも挿入でき、次の例に示すように組み込みミドルウェア コンポーネントと共に使用できます。

パイプラインでの要求のフローを示す図。

あなたの会社のネットワーク運用チームは、運用環境でのパフォーマンスの問題のトラブルシューティングを行っています。 チーム リーダーから、アプリのリアルタイム監視をより適切にサポートするために、いくつかの機能を実装する作業を任されました。 アプリは、要求の詳細をコンソールに記録する必要があります。 要求ごとに、要求メソッド、パス、応答状態コードをログに記録する必要があります。

この演習では、要求の詳細をコンソールに記録するカスタム ミドルウェア コンポーネントを作成します。

カスタム ミドルウェアを追加する

要求の詳細をコンソールに記録するカスタム ミドルウェアを含むように、既存の ASP.NET Core アプリを変更してみましょう。

  1. Program.cs ファイルをまだ開いていない場合は、これを開きます。

  2. app.Run() の直前に、次のコードを挿入します。

    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    

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

    • app.Use() は、カスタム ミドルウェア コンポーネントをパイプラインに追加します。 コンポーネントは、パラメーターとして HttpContext オブジェクトと RequestDelegate オブジェクトを受け取ります。
    • 委任は、要求メソッド、パス、応答状態コードをコンソールに書き込みます。
    • await next() は、パイプライン内の次のミドルウェア コンポーネントを呼び出します。

変更内容をテストする

  1. Ctrl+Shift+F5 キーを押してアプリをリビルドして再起動します。

  2. ブラウザー ウィンドウが開いたら、ルート URL に "Welcome to Contoso!" と表示されることに注意してください

  3. /history を URL に追加し、Enter キーを押します。 ブラウザーは [/about] ページにリダイレクトされます。

  4. Visual Studio Code で、Ctrl + Shift + P キーを押してコマンド パレットを開きます。 [デバッグ コンソール: デバッグ コンソール ビューにフォーカスする] を検索して選択し、下部パネルの [デバッグ コンソール] タブに切り替えます。 以下の行に注意してください。

    GET / 200
    GET /about 200
    

    コンソール出力は、各要求の要求メソッド、パス、応答状態コードを示します。 最初の行はルート URL の要求を示し、2 行目は /about ページの要求を示します。

    ブラウザーが /favicon.ico を要求することもあります。 これは、Web サイトのファビコンに対する標準的な要求であり、無視できます。

  5. 次の演習のために、このアプリを実行したままにします。

ミドルウェアの順序を変更する

アプリは動作しているように見えますが、問題があります。 /history ページを要求しましたが、コンソール出力に表示されません。 この動作は、要求の詳細をログに記録するカスタム ミドルウェア コンポーネントが URL リライター ミドルウェアの後に追加されたためです。 URL リライター ミドルウェアは、要求を /history から /about にリダイレクトして応答を送信します。カスタム ミドルウェア コンポーネントには要求が表示されません。 これを修正しましょう。

  1. 追加した app.Use() 行を app.UseRewriter() 行の直前に移動します。

    完全な Program.cs ファイルはこのようになります。

    using Microsoft.AspNetCore.Rewrite;
    
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    
    app.UseRewriter(new RewriteOptions().AddRedirect("history", "about"));
    
    app.MapGet("/", () => "Hello World!");
    app.MapGet("/about", () => "Contoso was founded in 2000.");
    
    app.Run();
    

    URL リライター ミドルウェアの前にカスタム ミドルウェア コンポーネントが追加されました。 URL リライター ミドルウェアが要求を処理し、リダイレクトする前に、カスタム ミドルウェア コンポーネントが要求の詳細をログに記録します。

  2. アプリをもう一度再起動し、前と同様にテストします。 今回は、デバッグ コンソールの出力に、/history ページの要求が含まれている必要があります。

    GET / 200
    GET /history 200
    GET /about 200
    

    コンソール出力には、/history ページにリダイレクトする直前に /about ページの要求が表示されるようになりました。

状態コードを修正する

アプリはほぼ準備ができていますが、もう 1 つ問題があります。 アプリが要求をリダイレクトした場合でも、コンソール出力の状態コードは常に 200 です。 /history 要求の状態コードは 302 リダイレクトになる必要があります。 この動作の理由は、ミドルウェア コンポーネントが処理される順序に関する別の問題にあります。

カスタム ミドルウェア コンポーネントは、詳細をコンソールに記録し、await next() を呼び出して次のミドルウェア コンポーネントに渡します。 問題は、ターミナル ミドルウェア コンポーネントが応答を開始した後に、StatusCode オブジェクトの Response プロパティが設定されていることです。 コードを変更してこれを修正しましょう。

  1. 追加したデリゲートで、Console.WriteLine() 行を await next() 行の後に移動します。

    更新したコードは次のようになります。

    app.Use(async (context, next) =>
    {
        await next(); 
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
    });
    

    これで、カスタム ミドルウェア コンポーネントは、ターミナル ミドルウェア コンポーネントが応答状態コードを設定した後、要求の詳細をログに記録します。

  2. 再起動して、/history 要求をもう一度テストします。 デバッグ コンソールの出力に正しい状態コードが表示されるようになりました。

    GET / 200
    GET /history 302
    GET /about 200