Bagikan melalui


Menulis middleware ASP.NET Core kustom

Oleh Fiyaz Hasan, Rick Anderson, dan Steve Smith

Middleware adalah perangkat lunak yang dirakit menjadi alur aplikasi untuk menangani permintaan dan respons. ASP.NET Core menyediakan serangkaian komponen middleware bawaan yang kaya, tetapi dalam beberapa skenario Anda mungkin ingin menulis middleware kustom.

Topik ini menjelaskan cara menulis middleware berbasis konvensi. Untuk pendekatan yang menggunakan pengetikan yang kuat dan aktivasi per permintaan, lihat Aktivasi middleware berbasis pabrik di ASP.NET Core.

Kelas middleware

Middleware umumnya dienkapsulasi di kelas dan diekspos dengan metode ekstensi. Pertimbangkan middleware sebaris berikut, yang mengatur budaya untuk permintaan saat ini dari string kueri:

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();

Middleware sebaris yang disorot sebelumnya digunakan untuk menunjukkan pembuatan komponen middleware dengan memanggil Microsoft.AspNetCore.Builder.UseExtensions.Use. Metode ekstensi sebelumnya Use menambahkan delegasi middleware yang ditentukan sebaris ke alur permintaan aplikasi.

Ada dua kelebihan beban yang tersedia untuk Use ekstensi:

  • Satu mengambil HttpContext dan Func<Task>. Panggil Func<Task> tanpa parameter apa pun.
  • Yang lain mengambil HttpContext dan RequestDelegate. RequestDelegate Panggil dengan melewati HttpContext.

Lebih suka menggunakan kelebihan beban yang lebih baru karena menyimpan dua alokasi internal per permintaan yang diperlukan saat menggunakan kelebihan beban lainnya.

Uji middleware dengan melewati budaya. Misalnya, minta https://localhost:5001/?culture=es-es.

Untuk dukungan pelokalan bawaan ASP.NET Core, lihat Globalisasi dan pelokalan di ASP.NET Core.

Kode berikut memindahkan delegasi middleware ke kelas:

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);
    }
}

Kelas middleware harus mencakup:

  • Konstruktor publik dengan parameter jenis RequestDelegate.
  • Metode publik bernama Invoke atau InvokeAsync. Metode ini harus:
    • TaskMengembalikan .
    • Terima parameter pertama jenis HttpContext.

Parameter tambahan untuk konstruktor dan Invoke/InvokeAsync diisi oleh injeksi dependensi (DI).

Biasanya, metode ekstensi dibuat untuk mengekspos middleware melalui 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>();
    }
}

Kode berikut memanggil middleware dari 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();

Dependensi middleware

Middleware harus mengikuti Prinsip Dependensi Eksplisit dengan mengekspos dependensinya dalam konstruktornya. Middleware dibangun sekali per masa pakai aplikasi.

Komponen middleware dapat menyelesaikan dependensinya dari injeksi dependensi (DI) melalui parameter konstruktor. UseMiddleware juga dapat menerima parameter tambahan secara langsung.

Dependensi middleware per permintaan

Middleware dibangun pada startup aplikasi dan karenanya memiliki masa pakai aplikasi. Layanan masa pakai terlingkup yang digunakan oleh konstruktor middleware tidak dibagikan dengan jenis lain yang disuntikkan dependensi selama setiap permintaan. Untuk berbagi layanan terlingkup antara middleware dan jenis lainnya, tambahkan layanan ini ke InvokeAsync tanda tangan metode. Metode ini InvokeAsync dapat menerima parameter tambahan yang diisi oleh 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>();
    }
}

Opsi seumur hidup dan pendaftaran berisi sampel lengkap middleware dengan layanan seumur hidup terlingkup .

Kode berikut digunakan untuk menguji middleware sebelumnya:

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();

Antarmuka IMessageWriter dan implementasi:

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);
}

Sumber Daya Tambahan:

Oleh Rick Anderson dan Steve Smith

Middleware adalah perangkat lunak yang dirakit menjadi alur aplikasi untuk menangani permintaan dan respons. ASP.NET Core menyediakan serangkaian komponen middleware bawaan yang kaya, tetapi dalam beberapa skenario Anda mungkin ingin menulis middleware kustom.

Catatan

Topik ini menjelaskan cara menulis middleware berbasis konvensi. Untuk pendekatan yang menggunakan pengetikan yang kuat dan aktivasi per permintaan, lihat Aktivasi middleware berbasis pabrik di ASP.NET Core.

Kelas middleware

Middleware umumnya dienkapsulasi di kelas dan diekspos dengan metode ekstensi. Pertimbangkan middleware berikut, yang mengatur budaya untuk permintaan saat ini dari string kueri:

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}");
        });

    }
}

Kode sampel sebelumnya digunakan untuk menunjukkan pembuatan komponen middleware. Untuk dukungan pelokalan bawaan ASP.NET Core, lihat Globalisasi dan pelokalan di ASP.NET Core.

Uji middleware dengan melewati budaya. Misalnya, minta https://localhost:5001/?culture=no.

Kode berikut memindahkan delegasi middleware ke kelas:

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);
        }
    }
}

Kelas middleware harus mencakup:

  • Konstruktor publik dengan parameter jenis RequestDelegate.
  • Metode publik bernama Invoke atau InvokeAsync. Metode ini harus:
    • TaskMengembalikan .
    • Terima parameter pertama jenis HttpContext.

Parameter tambahan untuk konstruktor dan Invoke/InvokeAsync diisi oleh injeksi dependensi (DI).

Dependensi middleware

Middleware harus mengikuti Prinsip Dependensi Eksplisit dengan mengekspos dependensinya dalam konstruktornya. Middleware dibangun sekali per masa pakai aplikasi. Lihat bagian Dependensi middleware per permintaan jika Anda perlu berbagi layanan dengan middleware dalam permintaan.

Komponen middleware dapat menyelesaikan dependensinya dari injeksi dependensi (DI) melalui parameter konstruktor. UseMiddleware juga dapat menerima parameter tambahan secara langsung.

Dependensi middleware per permintaan

Karena middleware dibangun pada startup aplikasi, bukan per permintaan, layanan masa pakai terlingkup yang digunakan oleh konstruktor middleware tidak dibagikan dengan jenis lain yang disuntikkan dependensi selama setiap permintaan. Jika Anda harus berbagi layanan terlingkup antara middleware Anda dan jenis lainnya, tambahkan layanan ini ke InvokeAsync tanda tangan metode . Metode ini InvokeAsync dapat menerima parameter tambahan yang diisi oleh 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);
    }
}

Opsi seumur hidup dan pendaftaran berisi sampel lengkap middleware dengan layanan seumur hidup terlingkup .

Metode ekstensi middleware

Metode ekstensi berikut mengekspos middleware melalui IApplicationBuilder:

using Microsoft.AspNetCore.Builder;

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

Kode berikut memanggil middleware dari Startup.Configure:

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

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

Sumber Daya Tambahan: