Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek ukazuje, jak migrovat existující moduly ASP.NET HTTP ze system.webserveru do middlewaru ASP.NET Core.
Moduly znovu prozkoumány
Než se pustíme do ASP.NET základního middlewaru, pojďme nejprve zrekapitulovat, jak moduly HTTP fungují:
Moduly jsou:
Třídy, které implementují IHttpModule
Vyvoláno pro každý požadavek
Možnost zkratování (zastavení dalšího zpracování žádosti)
Možnost přidat k odpovědi HTTP nebo vytvořit vlastní
Nakonfigurováno v Web.config
Pořadí, ve kterém moduly zpracovávají příchozí požadavky, určuje:
Řada událostí vygenerovaných ASP.NET, například BeginRequest a AuthenticateRequest. Úplný seznam viz System.Web.HttpApplication. Každý modul může vytvořit obslužnou rutinu pro jednu nebo více událostí.
U stejné události je pořadí, ve kterém jsou nakonfigurované v Web.config.
Kromě modulů můžete do Global.asax.cs
souboru přidat obslužné rutiny pro události životního cyklu. Tyto obslužné rutiny se spouští po obslužných rutinách v nakonfigurovaných modulech.
Z modulů do middlewaru
Middleware je jednodušší než moduly HTTP:
Moduly,
Global.asax.cs
Web.config (s výjimkou konfigurace služby IIS) a životní cyklus aplikace jsou pryč.Role modulů převzal middleware.
Middleware se konfigurují pomocí kódu místo vWeb.config
- Větvení kanálů umožňuje odesílat požadavky do konkrétního middlewaru, a to na základě adresy URL, ale také hlaviček požadavků, řetězců dotazů atd.
- Větvení kanálů umožňuje odesílat požadavky do konkrétního middlewaru, a to na základě adresy URL, ale také hlaviček požadavků, řetězců dotazů atd.
Middleware je velmi podobný modulům:
Vyvolána v zásadě pro každou žádost
Schopnost přerušit požadavek tím, že požadavek nepředáte dalšímu middlewaru
Možnost vytvořit vlastní odpověď HTTP
Middleware a moduly se zpracovávají v jiném pořadí:
Pořadí middlewaru je založené na pořadí, ve kterém jsou vloženy do kanálu požadavku, zatímco pořadí modulů je založené hlavně na System.Web.HttpApplication událostech.
Pořadí middlewaru pro odpovědi je opačné než u požadavků, zatímco pořadí modulů je stejné pro požadavky a odpovědi.
Viz Vytvoření kanálu middlewaru pomocí nástroje IApplicationBuilder
Všimněte si, jak na obrázku výše ověřovací middleware přerušil zpracování požadavku.
Migrace kódu modulu do middlewaru
Existující modul HTTP bude vypadat nějak takto:
// 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.
}
}
}
Jak je znázorněno na stránce Middleware, middleware v ASP.NET Core je třída, která zveřejňuje metodu Invoke
, která přijímá HttpContext
a vrací Task
. Váš nový middleware bude vypadat takto:
// 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>();
}
}
}
Předchozí šablona middlewaru byla převzata z části věnované psaní middlewaru.
Pomocná třída MyMiddlewareExtensions usnadňuje konfiguraci middlewaru ve třídě Startup
. Metoda UseMyMiddleware
přidá třídu middlewaru do kanálu požadavku. Služby vyžadované middlewarem se vloží do konstruktoru middlewaru.
Váš modul může žádost ukončit, například pokud uživatel nemá oprávnění:
// 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;
}
}
Middleware to zpracovává tak, že Invoke
nevolá další middleware v potrubí. Mějte na paměti, že se tím požadavek úplně neukončí, protože předchozí middleware budou stále vyvolány, když odpověď prochází zpět v potrubí.
// 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.
}
Když migrujete funkce modulu do nového middlewaru, můžete zjistit, že se váš kód nekompiluje, protože HttpContext
se třída výrazně změnila v ASP.NET Core. Podívejte se na Migrace z ASP.NET Framework HttpContext na ASP.NET Core, kde se dozvíte, jak migrovat na nový ASP.NET Core HttpContext.
Migrace modulu vložení do kanálu žádosti
Moduly HTTP se obvykle přidávají do kanálu požadavku pomocí Web.config:
<?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>
Konvertujte to přidáním vašeho nového middlewaru do potrubí požadavků ve třídě Startup
.
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?}");
});
}
Přesné místo v kanálu, kam vkládáte nový middleware, závisí na události, která je zpracována jako modul (BeginRequest
, EndRequest
, atd.) a na jeho pořadí v seznamu modulů v Web.config.
Jak jsme uvedli dříve, v ASP.NET Core neexistuje žádný životní cyklus aplikace a pořadí, ve kterém se odpovědi zpracovávají middlewarem, se liší od pořadí používaného moduly. To by mohlo znamenat, že vaše rozhodnutí o objednávání bude náročnější.
Pokud se řazení stane problémem, můžete modul rozdělit na několik middlewareových komponent, které lze řadit nezávisle.
Načítání možností middlewaru pomocí vzoru možností
Některé moduly mají možnosti konfigurace, které jsou uloženy v Web.config. V ASP.NET Core se však místo Web.configpoužívá nový konfigurační model .
Nový konfigurační systém nabízí tyto možnosti řešení:
Přímo vložte možnosti do middlewaru, jak je znázorněno v další části.
Použijte vzor možností:
Vytvořte třídu pro uložení možností middlewaru, například:
public class MyMiddlewareOptions { public string Param1 { get; set; } public string Param2 { get; set; } }
Uložení hodnot možností
Konfigurační systém umožňuje ukládat hodnoty možností kdekoli, kde chcete. Většina webů však používá
appsettings.json
, a proto tento přístup zvolíme:{ "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
MyMiddlewareOptionsSection je název oddílu. Nemusí se shodovat s názvem třídy možností.
Přiřaďte hodnoty možností ke třídě možností
Model možností používá architekturu injektáže závislostí ASP.NET Core k přidružení typu možností (například
MyMiddlewareOptions
) kMyMiddlewareOptions
objektu, který má skutečné možnosti.Aktualizujte svoji
Startup
třídu:Pokud používáte
appsettings.json
, přidejte ho do tvůrce konfigurace v konstruktoruStartup
: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(); }
Nakonfigurujte službu možností:
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(); }
Přidružte možnosti ke třídě možností:
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(); }
Vložte možnosti do konstruktoru middlewaru. Podobá se vkládání možností do kontroleru.
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 } }
Metoda rozšíření UseMiddleware, která přidává váš middleware do
IApplicationBuilder
, se postará o injektáž závislostí.To není omezeno na
IOptions
objekty. Jakýkoli jiný objekt, který middleware vyžaduje, je možné vložit tímto způsobem.
Načítání možností middleware prostřednictvím přímé injekce
Vzor možností má výhodu, že vytváří volné spojení mezi hodnotami možností a jejich spotřebiteli. Jakmile přidružíte třídu možností se skutečnými hodnotami možností, může každá jiná třída získat přístup k možnostem prostřednictvím architektury injektáže závislostí. Není potřeba předávat hodnoty možností.
Toto se však zkomplikuje, pokud chcete stejný middleware použít dvakrát s různými nastaveními. Například autorizační middleware používaný v různých větvích, který umožňuje různé role. Nelze přidružit dva různé objekty možností k jedné třídě možností.
Řešením je získat objekty možností se skutečnými hodnotami možností ve vaší Startup
třídě a předat je přímo do každé instance middlewaru.
Přidání druhého klíče do
appsettings.json
Pokud chcete do
appsettings.json
souboru přidat druhou sadu možností, použijte k jedinečné identifikaci nový klíč:{ "MyMiddlewareOptionsSection2": { "Param1": "Param1Value2", "Param2": "Param2Value2" }, "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
Načtěte hodnoty možností a předejte je middlewaru. Metoda
Use...
rozšíření (která přidává váš middleware do pipeline) je logické místo pro předávání hodnot parametrů: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?}"); }); }
Povolte middleware, abyste mohli použít parametr možností. Zadejte přetížení
Use...
rozšiřující metody (která přebírá parametr možností a předává hoUseMiddleware
). Při zavoláníUseMiddleware
s parametry jsou tyto parametry předány do konstruktoru middlewaru při instanciaci objektu middlewaru.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)); } }
Všimněte si, jak se tím zalomí objekt možností v objektu
OptionsWrapper
. Tím se implementujeIOptions
, jak očekává konstruktor middlewaru.
Přírůstková migrace IHttpModule
Jsou chvíle, kdy převod modulů na middleware nelze snadno provést. Aby bylo možné podporovat scénáře migrace, ve kterých se vyžadují moduly a nelze je přesunout do middlewaru, podporují adaptéry System.Web jejich přidání do ASP.NET Core.
Příklad IHttpModule
Aby bylo možné podporovat moduly, musí být instance HttpApplication dostupná. Pokud se nepoužívá žádný vlastní HttpApplication, použije se výchozí k přidání modulů. Události deklarované ve vlastní aplikaci (včetně Application_Start
) se zaregistrují a spustí odpovídajícím způsobem.
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()
{
}
}
Migrace Global.asax
Tuto infrastrukturu lze v případě potřeby použít k migraci použití Global.asax
. Zdroj z Global.asax
je vlastní HttpApplication a soubor lze zahrnout do aplikace ASP.NET Core. Vzhledem k tomu, že je pojmenovaná Global
, můžete k registraci použít následující kód:
builder.Services.AddSystemWebAdapters()
.AddHttpApplication<Global>();
Pokud je logika v ASP.NET Core dostupná, lze tento přístup použít k postupné migraci závislosti na Global.asax
do ASP.NET Core.
Události ověřování/autorizace
Aby se události ověřování a autorizace spouštěly v požadované době, měli byste použít následující vzor:
app.UseAuthentication();
app.UseAuthenticationEvents();
app.UseAuthorization();
app.UseAuthorizationEvents();
Pokud to neuděláte, události se budou dál spouštět. Bude však během volání .UseSystemWebAdapters()
.
Sdružování modulů HTTP
Vzhledem k tomu, že se k požadavku přiřadily moduly a aplikace v ASP.NET Framework, je pro každý požadavek potřeba nová instance. Vzhledem k tomu, že jejich vytvoření může být nákladné, jsou sdružovány pomocí ObjectPool<T>. Pokud chcete přizpůsobit skutečnou dobu života HttpApplication instancí, můžete použít vlastní fond:
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);
});