カスタム ASP.NET Core ミドルウェアを記述する

作成者: Fiyaz HasanRick AndersonSteve Smith

ミドルウェアとは、要求と応答を処理するために、アプリのパイプラインに組み込まれたソフトウェアです。 ASP.NET Core からは、組み込みミドルウェア コンポーネントが豊富に提供されますが、カスタム ミドルウェアを記述したほうが良い場合もあります。

このトピックでは "規約に基づく" ミドルウェアを作成する方法について説明します。 厳密な型指定と要求ごとのアクティベーションを使用したアプローチについては、「ASP.NET Core でのファクトリ ベースのミドルウェアのアクティブ化」を参照してください。

ミドルウェア クラス

ミドルウェアは一般に、クラスにカプセル化され、拡張メソッドを使用して公開されます。 クエリ文字列から現在の要求のカルチャを設定する次のようなインライン ミドルウェアを考慮します。

using System.Globalization;

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

app.UseHttpsRedirection();

app.Use(async (context, next) =>
{
    var cultureQuery = context.Request.Query["culture"];
    if (!string.IsNullOrWhiteSpace(cultureQuery))
    {
        var culture = new CultureInfo(cultureQuery);

        CultureInfo.CurrentCulture = culture;
        CultureInfo.CurrentUICulture = culture;
    }

    // Call the next delegate/middleware in the pipeline.
    await next(context);
});

app.Run(async (context) =>
{
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();

上記で強調表示されているインライン ミドルウェアは、Microsoft.AspNetCore.Builder.UseExtensions.Use を呼び出してミドルウェア コンポーネントを作成する方法を示すために使用されています。 上記の Use 拡張メソッドは、インラインで定義されたミドルウェア デリゲートを、アプリケーションの要求パイプラインに追加します。

Use 拡張機能には、次の 2 つのオーバーロードがあります。

  • 1 つは、HttpContextFunc<Task> を受け取ります。 Func<Task> はパラメーターなしで呼び出します。
  • もう 1 つは、HttpContext および RequestDelegate を受け取ります。 RequestDelegateHttpContext を渡すことで呼び出します。

後者では、他のオーバーロードを使用するときに必要な 2 つの内部要求ごとの割り当てが保存されるので、後者を優先的に使用してください。

カルチャを渡すことによって、ミドルウェアをテストします。 たとえば、https://localhost:5001/?culture=es-es を要求します。

ASP.NET Core の組み込みのローカリゼーション サポートについては、「ASP.NET Core のグローバリゼーションおよびローカリゼーション」をご覧ください。

次のコードは、ミドルウェアのデリゲートをクラスに移動します。

using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

ミドルウェアのクラスには、次のものが含まれる必要があります。

  • RequestDelegate 型のパラメーターを持つパブリック コンストラクター。
  • Invoke または InvokeAsync という名前のパブリック メソッド。 このメソッドでは次のことが必要です。
    • Task を返します。
    • HttpContext 型の最初のパラメーターを受け取ります。

コンストラクターおよび Invoke/InvokeAsync に対する追加のパラメーターは、依存関係の挿入 (DI) によって設定されます。

通常、拡張メソッドは IApplicationBuilder を通じてミドルウェアを公開するために作成されます。

using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

次のコードは、Program.cs からミドルウェアを呼び出します。

using Middleware.Example;
using System.Globalization;

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

app.UseHttpsRedirection();

app.UseRequestCulture();

app.Run(async (context) =>
{
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();

ミドルウェアの依存関係

ミドルウェアは、コンストラクターで依存関係を公開することによって、明示的な依存関係の原則に従う必要があります。 ミドルウェアは、"アプリケーションの有効期間" ごとに 1 回構築されます。

ミドルウェア コンポーネントは、コンストラクター パラメーターにより、依存関係の挿入 (DI) から依存関係を解決できます。 UseMiddleware は、追加パラメーターを直接受け入れることもできます。

要求ごとのミドルウェアの依存関係

ミドルウェアはアプリの起動時に構築されるため、アプリケーションの有効期間があります。 ミドルウェアのコンストラクターによって使われる "スコープ有効期間" のサービスは、各要求の間に、依存関係によって挿入される他の種類と共有されません。 ミドルウェアとその他の種類の間で "スコープ" サービスを共有するには、これらのサービスを InvokeAsync メソッドのシグネチャに追加します。 InvokeAsync メソッドは、DI によって設定される追加のパラメーターを受け取ることができます。

namespace Middleware.Example;

public class MyCustomMiddleware
{
    private readonly RequestDelegate _next;

    public MyCustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // IMessageWriter is injected into InvokeAsync
    public async Task InvokeAsync(HttpContext httpContext, IMessageWriter svc)
    {
        svc.Write(DateTime.Now.Ticks.ToString());
        await _next(httpContext);
    }
}

public static class MyCustomMiddlewareExtensions
{
    public static IApplicationBuilder UseMyCustomMiddleware(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyCustomMiddleware>();
    }
}

有効期間と登録のオプションには、"スコープ付き" 有効期間サービスを含むミドルウェアの完全なサンプルが含まれています。

次のコードは、前述のミドルウェアをテストするために使用されます。

using Middleware.Example;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IMessageWriter, LoggingMessageWriter>();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseMyCustomMiddleware();

app.MapGet("/", () => "Hello World!");

app.Run();

IMessageWriter インターフェイスと実装:

namespace Middleware.Example;

public interface IMessageWriter
{
    void Write(string message);
}

public class LoggingMessageWriter : IMessageWriter
{

    private readonly ILogger<LoggingMessageWriter> _logger;

    public LoggingMessageWriter(ILogger<LoggingMessageWriter> logger) =>
        _logger = logger;

    public void Write(string message) =>
        _logger.LogInformation(message);
}

その他のリソース

作成者: Rick AndersonSteve Smith

ミドルウェアとは、要求と応答を処理するために、アプリのパイプラインに組み込まれたソフトウェアです。 ASP.NET Core からは、組み込みミドルウェア コンポーネントが豊富に提供されますが、カスタム ミドルウェアを記述したほうが良い場合もあります。

Note

このトピックでは "規約に基づく" ミドルウェアを作成する方法について説明します。 厳密な型指定と要求ごとのアクティベーションを使用したアプローチについては、「ASP.NET Core でのファクトリ ベースのミドルウェアのアクティブ化」を参照してください。

ミドルウェア クラス

ミドルウェアは一般に、クラスにカプセル化され、拡張メソッドを使用して公開されます。 クエリ文字列から現在の要求のカルチャを設定する次のようなミドルウェアを考慮します。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;
            }

            // Call the next delegate/middleware in the pipeline
            await next();
        });

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });

    }
}

上のサンプル コードを使って、ミドルウェア コンポーネントの作成方法を示します。 ASP.NET Core の組み込みのローカリゼーション サポートについては、「ASP.NET Core のグローバリゼーションおよびローカリゼーション」をご覧ください。

カルチャを渡すことによって、ミドルウェアをテストします。 たとえば、https://localhost:5001/?culture=no を要求します。

次のコードは、ミドルウェアのデリゲートをクラスに移動します。

using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace Culture
{
    public class RequestCultureMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestCultureMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;

            }

            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }
}

ミドルウェアのクラスには、次のものが含まれる必要があります。

  • RequestDelegate 型のパラメーターを持つパブリック コンストラクター。
  • Invoke または InvokeAsync という名前のパブリック メソッド。 このメソッドでは次のことが必要です。
    • Task を返します。
    • HttpContext 型の最初のパラメーターを受け取ります。

コンストラクターおよび Invoke/InvokeAsync に対する追加のパラメーターは、依存関係の挿入 (DI) によって設定されます。

ミドルウェアの依存関係

ミドルウェアは、コンストラクターで依存関係を公開することによって、明示的な依存関係の原則に従う必要があります。 ミドルウェアは、"アプリケーションの有効期間" ごとに 1 回構築されます。 要求内でミドルウェアとサービスを共有する必要がある場合は、「要求ごとのミドルウェアの依存関係」セクションをご覧ください。

ミドルウェア コンポーネントは、コンストラクター パラメーターにより、依存関係の挿入 (DI) から依存関係を解決できます。 UseMiddleware は、追加パラメーターを直接受け入れることもできます。

要求ごとのミドルウェアの依存関係

ミドルウェアは要求ごとではなくアプリの起動時に構築されるため、ミドルウェアのコンストラクターによって使われる "スコープ" 有効期間のサービスは、各要求の間に、依存関係によって挿入される他の種類と共有されません。 ミドルウェアとその他の種類の間で "スコープ" サービスを共有する必要がある場合は、これらのサービスを InvokeAsync メソッドのシグネチャに追加します。 InvokeAsync メソッドは、DI によって設定される追加のパラメーターを受け取ることができます。

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

    public CustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // IMyScopedService is injected into InvokeAsync
    public async Task InvokeAsync(HttpContext httpContext, IMyScopedService svc)
    {
        svc.MyProperty = 1000;
        await _next(httpContext);
    }
}

有効期間と登録のオプションには、"スコープ付き" 有効期間サービスを含むミドルウェアの完全なサンプルが含まれています。

ミドルウェア拡張メソッド

次の拡張メソッドは、IApplicationBuilder を介してミドルウェアを公開します。

using Microsoft.AspNetCore.Builder;

namespace Culture
{
    public static class RequestCultureMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestCulture(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestCultureMiddleware>();
        }
    }
}

次のコードは、Startup.Configure からミドルウェアを呼び出します。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRequestCulture();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

その他の技術情報