ASP.NET Core のミドルウェアを作成する - Use メソッドの利用 - ASP.NET Core

ASP.NET Core でUseメソッドを利用してミドルウェアを作成するコードを紹介します。

概要

ASP.NET Core では、順番にデリゲートの処理を呼び出すことでレスポンスデータを作成する仕組みが利用できます。ASP.NET Coreのこの仕組みをミドルウェアと呼びます。この記事ではUseメソッドを利用して複数のミドルウェアを利用するコードを紹介します。

プログラム例1

事前準備

ASP.NET Coreアプリケーションを作成します。アプリケーションの作成手順はこちらの記事を参照して下さい。

コード

ASP.NET CoreアプリケーションのStartup.csのコードを変更します。下記のコードを記述します。
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace SimpleMiddlewareUse
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use1 Start</div>");
        await next();
      });

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use2 Start</div>");
        await next();
      });

      app.Run(async context =>
      {
        await context.Response.WriteAsync("<div>Run</div>");
      });

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use3 Start</div>");
        await next();
      });
    }
  }
}

解説

Startup.cs のConfigureメソッドで、Useメソッドを3回呼び出しています。Useメソッドの引数にデリゲートを与えています。最初のUseメソッドのデリゲートでは "Use1 Start" の文字を返します。2番目のUseメソッドのデリゲートでは "Use2 Start" の文字を返します。Runメソッドでは、"Run"の文字列をレスポンスで返します。最後のUseメソッドでは "Use3 Start" の文字列をレスポンスとして返します。

実行結果

プロジェクトを実行します。Webブラウザが起動し下図の画面が表示されます。上から順番に "Use1 Start" "Use2 Start" "Run" の文字列が表示されています。 "Use3 Start" の文字列は表示されていません。Runメソッドより手前に記述したUseは記述順に実行され、Runメソッドより後に記述したUseメソッドの処理は実行されないことが確認できます。


アプリケーションルートのURLの後ろにパラメーターを追加した場合でも同じ画面が表示されます。

プログラム例2 : next() の後にも処理を記述した場合

事前準備

ASP.NET Coreアプリケーションを作成します。アプリケーションの作成手順はこちらの記事を参照して下さい。
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace SimpleMiddlewareUse
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use1 Start</div>");
        await next();
        await context.Response.WriteAsync("<div>Use1 End</div>");
      });

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use2 Start</div>");
        await next();
        await context.Response.WriteAsync("<div>Use2 End</div>");
      });

      app.Run(async context =>
      {
        await context.Response.WriteAsync("<div>Run</div>");
      });

      app.Use(async (context, next) =>
      {
        await context.Response.WriteAsync("<div>Use3 Start</div>");
        await next();
        await context.Response.WriteAsync("<div>Use3 End</div>");
      });

    }
  }
}

解説

先のコードとほぼ同様ですが、Useメソッドに与えたデリゲートの await next(); メソッド以降にもレスポンスの出力のコードを記述しています。nextメソッドの前後でどのように処理が変わるかを確認します。

実行結果

プロジェクトを実行します。Webブラウザが起動し下図の画面が表示されます。先のコードと同様に、Runメソッドより後に記述したUseメソッドの処理は実行されません。画面のメッセージは上から順番に "Use1 Start" "Use2 Start" "Run" "Use2 End" "Use1 End"の文字列が表示されています。 Nextメソッドにより次のミドルウェアの処理が実行されることがわかります。


アプリケーションルートのURLの後ろにパラメーターを追加した場合でも同じ画面が表示されます。


Useメソッドのnext()メソッドの呼び出しにより次のUseメソッドで追加されたミドルウェアを実行します。最後にRunメソッドで呼び出されたミドルウェアが最後のミドルウェアとなり処理の実行後、前のミドルウェアに処理を渡し、最初に呼び出したミドルウェアの処理終了部分でレスポンスが完成する動作になります。

具体的な実装例

具体的な実装例のコードを紹介します。

コード

ASP.NET Coreプロジェクトを作成し、Startup.csに下記のコードを記述します。
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MiddlewareProc
{
  public class Startup
  {
    private string qs;

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.Use(async (context, next) =>
      {
        qs = context.Request.Query["key"].ToString();
        if (qs != "") {
          await next();
        }
        else {
          context.Response.StatusCode = 404;
        }
      });

      app.Use(async (context, next) =>
      {
        if (qs == "json") {
          await context.Response.WriteAsync("{\"name\":\"iPentec\",\"code\":4001}");
        }
        else { 
          await context.Response.WriteAsync("<html><head><meta charset=\"utf-8\"/></head><body>");
          await next();
          await context.Response.WriteAsync("</body></html>");
        }
      });

      app.Run(async context =>
      {
        await context.Response.WriteAsync("<div>取得した値:"+qs+"</div>");
      });
    }
  }
}

解説

最初のミドルウェアではcontextオブジェクトのRequestオブジェクトにアクセスしクエリ文字列を取得します。URLパラメーターのKeyの値を取得します。値が空であった場合は正常に呼び出せなかったと判断し、NotFound となるHTTPレスポンス404を返します。Keyの値が存在している場合は次のミドルウェアに処理を渡します。
  app.Use(async (context, next) =>
  {
    qs = context.Request.Query["key"].ToString();
    if (qs != "") {
      await next();
    }
    else {
      context.Response.StatusCode = 404;
    }
  });

2番目のミドルウェアではkeyパラメータの値を判定します。値が"json"であった場合はJSON形式のレスポンスを返します。それ以外の値の場合はHTMLをレスポンスで返します。HTMLタグを出力し次のミドルウェアに制御を渡します。
  app.Use(async (context, next) =>
  {
    if (qs == "json") {
      await context.Response.WriteAsync("{\"name\":\"iPentec\",\"code\":4001}");
    }
    else { 
      await context.Response.WriteAsync("<html><head><meta charset=\"utf-8\"/></head><body>");
      await next();
      await context.Response.WriteAsync("</body></html>");
    }
  });

3番目のミドルウェアはRunメソッドで実装されており、最後のミドルウェアになります。HTMLのレスポンスのコンテンツを出力します。
  app.Run(async context =>
  {
    await context.Response.WriteAsync("<div>取得した値:"+qs+"</div>");
  });

実行結果

プロジェクトを実行します。Webブラウザが起動し下図の画面が表示されます。
URLにパラメーターを付与していないため、最初のミドルウェアの判定で404エラーが返っていることが確認できます。


アプリケーションルートのURLの後ろに ?key=Penguin のパラメーターを追加してアクセスします。下図の画面が表示されます。パラメータが設定されたことでHTMLのレスポンスが返ることが確認できます。


続いて、アプリケーションルートのURLの後ろに ?key=json のパラメーターを追加します。パラメーターにjsonを指定した場合は2番目のミドルウェアの判定でJSON形式のレスポンスが戻る動作になります。JSONの値がWebブラウザに表示されていることが確認できます。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2024-01-07
作成日: 2019-10-14
iPentec all rights reserverd.