Aracılığıyla paylaş


HTTP modüllerini ASP.NET Core ara yazılımına geçirme

Bu makalede, mevcut ASP.NET HTTP modüllerini system.webserver'dan ASP.NET Core ara yazılımına geçirme işlemi gösterilmektedir.

Modüller yeniden ziyaret edildi

ASP.NET Core ara yazılımına geçmeden önce HTTP modüllerinin nasıl çalıştığını özetleyelim:

Modül İşleyicisi

Modüller şunlardır:

  • Uygulayan sınıflar IHttpModule

  • Her istek için çağrılır

  • İsteğin daha fazla işlenmesini durdurabilen kısa devre yapabilme özelliği

  • HTTP yanıtına ekleme yapabilir veya kendi yanıtlarını oluşturabilir

  • Web.config'de yapılandırıldı

Modüllerin gelen istekleri işleme sırası şu şekilde belirlenir:

  1. ASP.NET tarafından, BeginRequest ve AuthenticateRequest gibi tetiklenen bir dizi olay. Tam liste için bkz System.Web.HttpApplication. . Her modül bir veya daha fazla olay için bir işleyici oluşturabilir.

  2. Aynı olay için, içinde yapılandırıldıkları sıra Web.config.

Modüllere ek olarak, dosyanıza Global.asax.cs yaşam döngüsü olayları için işleyiciler ekleyebilirsiniz. Bu işleyiciler, yapılandırılan modüllerdeki işleyicilerden sonra çalışır.

Modüllerden ara yazılıma

Ara yazılım HTTP modüllerinden daha basittir:

  • Modüller, Global.asax.cs, Web.config (IIS yapılandırması hariç) ve uygulama yaşam döngüsü kaldırıldı.

  • Modüllerin rolleri ara yazılım tarafından devralındı

  • Ara yazılım ,Web.config yerine kod kullanılarak yapılandırılır

  • İşlem hattı dallanma , yalnızca URL'ye değil, aynı zamanda istek üst bilgilerine, sorgu dizelerine vb. göre belirli ara yazılımlara istek göndermenizi sağlar.
  • İşlem hattı dallanma , yalnızca URL'ye değil, aynı zamanda istek üst bilgilerine, sorgu dizelerine vb. göre belirli ara yazılımlara istek göndermenizi sağlar.

Ara yazılım modüllere çok benzer:

  • Temel olarak her istek için çağrılır.

  • İsteği bir sonraki ara yazılıma geçirmeyerek kısa devre yapabiliyor.

  • Kendi HTTP yanıtlarını oluşturabiliyor

Ara yazılım ve modüller farklı bir sırayla işlenir:

Yetkilendirme Ara Yazılımı, yetkilendirilmemiş bir kullanıcı için kısa devre oluşturur. Dizin sayfası için bir istek MVC Ara Yazılımı tarafından izin verilir ve işlenir. Satış raporu isteğine özel bir rapor Ara Yazılımı tarafından izin verilir ve işlenir.

Yukarıdaki görüntüde, kimlik doğrulama ara yazılımının isteği nasıl kısa yoldan geçirdiğine dikkat edin.

Modül kodunu ara yazılıma geçirme

Mevcut bir HTTP modülü şuna benzer olacaktır:

// ASP.NET 4 module

using System;
using System.Web;

namespace MyApp.Modules
{
    public class MyModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
            application.EndRequest += (new EventHandler(this.Application_EndRequest));
        }

        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the beginning of request processing.
        }

        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the end of request processing.
        }
    }
}

Middleware sayfasında gösterildiği gibi, ASP.NET Core middleware bir HttpContext alıp bir Task döndüren bir Invoke yöntemi kullanıma sunan bir sınıftır. Yeni ara yazılımınız şöyle görünür:

// ASP.NET Core middleware

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

namespace MyApp.Middleware
{
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;

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

        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing.

            await _next.Invoke(context);

            // Clean up.
        }
    }

    public static class MyMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddleware>();
        }
    }
}

Ara yazılım yazma bölümünden alınan önceki ara yazılım şablonu.

MyMiddlewareExtensions yardımcı sınıfı, sınıfınızda ara yazılımınızı Startup yapılandırmayı kolaylaştırır. yöntemi, UseMyMiddleware ara yazılım sınıfınızı istek işlem hattına ekler. Ara yazılımın ihtiyaç duyduğu hizmetler, ara yazılımın oluşturucusuna enjekte edilir.

Modülünüz, örneğin kullanıcı yetkili değilse bir isteği sonlandırabilir:

// ASP.NET 4 module that may terminate the request

private void Application_BeginRequest(Object source, EventArgs e)
{
    HttpContext context = ((HttpApplication)source).Context;

    // Do something with context near the beginning of request processing.

    if (TerminateRequest())
    {
        context.Response.End();
        return;
    }
}

Ara yazılım, işlem hattındaki sonraki ara yazılımı çağırmayarak Invoke bunu işler. Yanıt işlem hattından geri döndüğünde önceki ara yazılımların çağrılmaya devam edeceğinden, bunun isteği tam olarak sonlandırmadığını unutmayın.

// ASP.NET Core middleware that may terminate the request

public async Task Invoke(HttpContext context)
{
    // Do something with context near the beginning of request processing.

    if (!TerminateRequest())
        await _next.Invoke(context);

    // Clean up.
}

Yeni ara yazılımınıza modülünüzün işlevselliğini aktarırken, ASP.NET Core'da HttpContext sınıfı önemli ölçüde değiştiği için kodunuzun derlenmediğini fark edebilirsiniz. Yeni ASP.NET Core HttpContext'e nasıl geçiş yapılacağını öğrenmek için bkz. ASP.NET Framework HttpContext'ten ASP.NET Core'a geçiş.

Modül eklemeyi istek işlem hattına geçirme

HTTP modülleri genellikle Web.configkullanılarak istek işlem hattına eklenir:

<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
  <system.webServer>
    <modules>
      <add name="MyModule" type="MyApp.Modules.MyModule"/>
    </modules>
  </system.webServer>
</configuration>

Sınıfınızdaki istek işlem hattına yeni ara yazılımınızıStartup ekleyerek bunu dönüştürün:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMyMiddleware();

    app.UseMyMiddlewareWithParams();

    var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
    var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
    app.UseMyMiddlewareWithParams(myMiddlewareOptions);
    app.UseMyMiddlewareWithParams(myMiddlewareOptions2);

    app.UseMyTerminatingMiddleware();

    // Create branch to the MyHandlerMiddleware. 
    // All requests ending in .report will follow this branch.
    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".report"),
        appBranch => {
            // ... optionally add more middleware to this branch
            appBranch.UseMyHandler();
        });

    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".context"),
        appBranch => {
            appBranch.UseHttpContextDemoMiddleware();
        });

    app.UseStaticFiles();

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

Yeni ara yazılımınızı eklediğiniz işlem hattındaki tam nokta, modül olarak işlediği olaya (BeginRequest, EndRequestvb.) ve Web.configmodüller listenizdeki sırasına bağlıdır.

Daha önce belirtildiği gibi, ASP.NET Core'da uygulama yaşam döngüsü yoktur ve yanıtların ara yazılım tarafından işlenme sırası modüller tarafından kullanılan sıraylan farklıdır. Bu, sipariş kararınızı daha zor hale getirebilir.

Sıralama bir sorun haline gelirse modülünüzü bağımsız olarak sıralanabilir birden çok ara yazılım bileşenine bölebilirsiniz.

Seçenekler desenini kullanarak ara yazılım seçeneklerini yükleme

Bazı modüllerin Web.configiçinde depolanan yapılandırma seçenekleri vardır. Ancak ASP.NET Core'da Web.configyerine yeni bir yapılandırma modeli kullanılır.

Yeni yapılandırma sistemi bunu çözmek için size şu seçenekleri sunar:

  1. Ara yazılım seçeneklerinizi barındırmak için bir sınıf oluşturun, örneğin:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Seçenek değerlerini depolama

    Yapılandırma sistemi, seçenek değerlerini istediğiniz yerde depolamanıza olanak tanır. Ancak çoğu site kullanır appsettings.json, bu nedenle bu yaklaşımı kullanacağız:

    {
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    

    MyMiddlewareOptionsSection burada bir bölüm adı verilmiştir. Seçenekler sınıfınızın adıyla aynı olması gerekmez.

  3. Seçenek değerlerini options sınıfıyla ilişkilendirme

    Seçenekler düzeni, ASP.NET Core'un bağımlılık ekleme çerçevesini kullanarak seçenekler türünü (örneğin MyMiddlewareOptions) gerçek seçeneklere sahip bir MyMiddlewareOptions nesneyle ilişkilendirir.

    Sınıfınızı güncelleştirin Startup :

    1. kullanıyorsanız appsettings.jsonoluşturucudaki yapılandırma oluşturucusunun Startup içine ekleyin:

      public Startup(IHostingEnvironment env)
      {
          var builder = new ConfigurationBuilder()
              .SetBasePath(env.ContentRootPath)
              .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
              .AddEnvironmentVariables();
          Configuration = builder.Build();
      }
      
    2. Seçenekler hizmetini yapılandırın:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
    3. Seçeneklerinizi seçenekler sınıfınızla ilişkilendirin:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
  4. Seçenekleri ara yazılım oluşturucunuza ekleme. Bu, bir denetleyiciye seçenek eklemeye benzer.

    public class MyMiddlewareWithParams
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _myMiddlewareOptions;
    
        public MyMiddlewareWithParams(RequestDelegate next,
            IOptions<MyMiddlewareOptions> optionsAccessor)
        {
            _next = next;
            _myMiddlewareOptions = optionsAccessor.Value;
        }
    
        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing
            // using configuration in _myMiddlewareOptions
    
            await _next.Invoke(context);
    
            // Do something with context near the end of request processing
            // using configuration in _myMiddlewareOptions
        }
    }
    

    Ara yazılımınızı IApplicationBuilder öğesine ekleyen UseMiddleware uzantısı yöntemi bağımlılık ekleme işlemini üstlenir.

    Bu, nesnelerle IOptions sınırlı değildir. Ara yazılımınızın gerektirdiği diğer tüm nesneler bu şekilde eklenebilir.

Doğrudan ekleme yoluyla ara yazılım seçeneklerini yükleme

Seçenekler deseni, seçenek değerleriyle tüketicileri arasında gevşek bağlantı oluşturma avantajına sahiptir. Bir seçenek sınıfını gerçek seçenekler değerleriyle ilişkilendirdikten sonra, bağımlılık ekleme çerçevesi aracılığıyla diğer tüm sınıflar seçeneklere erişebilir. Seçenek değerlerini aktarmaya gerek yoktur.

Ancak aynı ara yazılımı farklı seçeneklerle iki kez kullanmak istiyorsanız bu durum bozulur. Örneğin, farklı dallarda kullanılan ve farklı rollere izin veren bir yetkilendirme ara yazılımı. İki farklı seçenek nesnesini tek seçenek sınıfıyla ilişkilendiremezsiniz.

Çözüm, sınıfınızdaki gerçek seçenek değerlerine sahip seçenekler nesnelerini almak ve bunları doğrudan ara yazılımınızın Startup her örneğine geçirmektir.

  1. appsettings.json için ikinci bir anahtar ekleyin

    Dosyaya ikinci bir seçenek kümesi eklemek için appsettings.json , dosyayı benzersiz bir şekilde tanımlamak için yeni bir anahtar kullanın:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. Seçenek değerlerini alın ve ara yazılıma geçirin. Use... Uzantı yöntemi (ara yazılımınızı işlem hattına ekler) seçenek değerlerini geçirmek için mantıksal bir yerdir:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
    
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
    
        app.UseMyMiddleware();
    
        app.UseMyMiddlewareWithParams();
    
        var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
        var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
        app.UseMyMiddlewareWithParams(myMiddlewareOptions);
        app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
    
        app.UseMyTerminatingMiddleware();
    
        // Create branch to the MyHandlerMiddleware. 
        // All requests ending in .report will follow this branch.
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".report"),
            appBranch => {
                // ... optionally add more middleware to this branch
                appBranch.UseMyHandler();
            });
    
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".context"),
            appBranch => {
                appBranch.UseHttpContextDemoMiddleware();
            });
    
        app.UseStaticFiles();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    
  3. Bir seçenekler parametresi almak için ara yazılımı etkinleştirin. Use... uzantı yönteminin aşırı yüklemesini sağlayın (options parametresini alır, ve bunu UseMiddleware öğesine geçirir). parametrelerle çağrıldığında UseMiddleware , ara yazılım nesnesinin örneğini oluşturduğunda parametreleri ara yazılım oluşturucunuza geçirir.

    public static class MyMiddlewareWithParamsExtensions
    {
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>();
        }
    
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder, MyMiddlewareOptions myMiddlewareOptions)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>(
                new OptionsWrapper<MyMiddlewareOptions>(myMiddlewareOptions));
        }
    }
    

    Seçenekler nesnesinin bir OptionsWrapper nesnesi içine nasıl sarıldığını gözlemleyin. Ara yazılım oluşturucu tarafından beklendiği gibi bu, IOptions uygular.

Kademeli IHttpModule geçişi

Modülleri ara yazılıma dönüştürmenin kolayca yapılamayacağı zamanlar vardır. Modüllerin gerekli olduğu ve ara yazılıma taşınamadığı geçiş senaryolarını desteklemek için System.Web bağdaştırıcıları bunları ASP.NET Core'a eklemeyi destekler.

IHttpModule Örneği

Modülleri desteklemek için HttpApplication'nin bir örneği kullanılabilir olmalıdır. Özel HttpApplication kullanılmazsa, modülleri eklemek için varsayılan bir tane kullanılır. Özel bir uygulamada bildirilen olaylar (dahil Application_Start) kaydedilir ve buna göre çalıştırılır.

using System.Web;
using Microsoft.AspNetCore.OutputCaching;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSystemWebAdapters()
    .AddHttpApplication<MyApp>(options =>
    {
        // Size of pool for HttpApplication instances. Should be what the expected concurrent requests will be
        options.PoolSize = 10;

        // Register a module (optionally) by name
        options.RegisterModule<MyModule>("MyModule");
    });

// Only available in .NET 7+
builder.Services.AddOutputCache(options =>
{
    options.AddHttpApplicationBasePolicy(_ => new[] { "browser" });
});

builder.Services.AddAuthentication();
builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthenticationEvents();

app.UseAuthorization();
app.UseAuthorizationEvents();

app.UseSystemWebAdapters();
app.UseOutputCache();

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

app.Run();

class MyApp : HttpApplication
{
    protected void Application_Start()
    {
    }

    public override string? GetVaryByCustomString(System.Web.HttpContext context, string custom)
    {
        // Any custom vary-by string needed

        return base.GetVaryByCustomString(context, custom);
    }
}

class MyModule : IHttpModule
{
    public void Init(HttpApplication application)
    {
        application.BeginRequest += (s, e) =>
        {
            // Handle events at the beginning of a request
        };

        application.AuthorizeRequest += (s, e) =>
        {
            // Handle events that need to be authorized
        };
    }

    public void Dispose()
    {
    }
}

Global.asax geçişi

Gerektiğinde bu altyapı, Global.asax kullanımını devretmek için kullanılabilir. kaynağı Global.asax özeldir HttpApplication ve dosya bir ASP.NET Core uygulamasına eklenebilir. adı Globalolduğundan, kaydetmek için aşağıdaki kod kullanılabilir:

builder.Services.AddSystemWebAdapters()
    .AddHttpApplication<Global>();

İçindeki mantık ASP.NET Core'da kullanılabildiği sürece, bu yaklaşım Global.asax üzerindeki bağımlılığı kademeli olarak ASP.NET Core'a taşımak için kullanılabilir.

Kimlik Doğrulama/Yetkilendirme olayları

Kimlik doğrulaması ve yetkilendirme olaylarının istenen zamanda çalışması için aşağıdaki desen kullanılmalıdır:

app.UseAuthentication();
app.UseAuthenticationEvents();

app.UseAuthorization();
app.UseAuthorizationEvents();

Bu yapılmazsa, olaylar çalışmaya devam eder. Ancak, .UseSystemWebAdapters() çağrısı sırasında olacaktır.

HTTP Modülü havuzu

ASP.NET Framework'teki modüller ve uygulamalar bir isteğe atandığından, her istek için yeni bir örnek gerekir. Ancak, oluşturulmaları pahalı olabileceğinden, ObjectPool<T> kullanılarak bir araya getirilirler. Özel bir havuz kullanılarak HttpApplication örneklerinin gerçek ömrü özelleştirilebilir.

builder.Services.TryAddSingleton<ObjectPool<HttpApplication>>(sp =>
{
    // Recommended to use the in-built policy as that will ensure everything is initialized correctly and is not intended to be replaced
    var policy = sp.GetRequiredService<IPooledObjectPolicy<HttpApplication>>();

    // Can use any provider needed
    var provider = new DefaultObjectPoolProvider();

    // Use the provider to create a custom pool that will then be used for the application.
    return provider.Create(policy);
});

Ek kaynaklar