Middleware di ASP.NET Core
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere Criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Il middleware è un software che viene assemblato in una pipeline dell'app per gestire richieste e risposte. Ogni componente:
- Sceglie se passare la richiesta al componente successivo nella pipeline.
- Può eseguire le operazioni prima e dopo il componente successivo nella pipeline.
Per compilare la pipeline delle richieste vengono usati i delegati di richiesta. I delegati di richiesta gestiscono ogni richiesta HTTP.
I delegati di richiesta vengono configurati tramite i metodi di estensione Run, Map e Use. È possibile specificare un singolo delegato di richiesta inline come metodo anonimo (chiamato middleware inline) o definirlo in una classe riutilizzabile. Le classi riutilizzabili o i metodi anonimi inline costituiscono il middleware o sono noti anche come componenti middleware. Ogni componente middleware è responsabile della chiamata del componente seguente nella pipeline o del corto circuito della pipeline. Quando un middleware esegue un corto circuito, viene denominato middleware terminale perché impedisce all'ulteriore middleware di elaborare la richiesta.
In Eseguire la migrazione di moduli HTTP in middleware vengono spiegate le differenze tra le pipeline delle richieste in ASP.NET Core e in ASP.NET 4.x e sono riportati altri esempi di middleware.
Analisi del codice middleware
ASP.NET Core include molti analizzatori della piattaforma del compilatore che controllano la qualità del codice dell'applicazione. Per altre informazioni, vedere Analisi del codice nelle app di ASP.NET Core.
Creare una pipeline middleware con WebApplication
La pipeline delle richieste ASP.NET Core è costituita da una sequenza di delegati di richiesta, chiamati uno dopo l'altro. Il diagramma seguente illustra il concetto. Il thread di esecuzione seguente le frecce nere.
I delegati possono eseguire le operazioni prima del delegato successivo e dopo di esso. I delegati che gestiscono le eccezioni devono essere chiamati nella prima parte della pipeline in modo che possano individuare le eccezioni che si verificano nelle parti successive della pipeline.
L'app di ASP.NET Core più semplice imposta un delegato di richiesta singolo che gestisce tutte le richieste. In questo caso non è inclusa una pipeline di richieste effettiva. Al contrario, viene chiamata una singola funzione anonima in risposta a ogni richiesta HTTP.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
È possibile concatenare più delegati di richiesta insieme con Use. Il parametro next
rappresenta il delegato successivo nella pipeline. È possibile eseguire il corto circuito della pipeline non chiamando il parametro next
. In genere è possibile eseguire un'azione prima e dopo il delegato next
, come illustra l'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Corto circuito della pipeline di richiesta
Quando un delegato non passa una richiesta al delegato successivo, si crea un cosiddetto corto circuito della pipeline delle richieste. Il corto circuito è spesso opportuno poiché evita l'esecuzione di operazioni non necessarie. Ad esempio, il middleware dei file statici può fungere da middleware terminale elaborando una richiesta per un file statico e corto circuito della rest pipeline. Il middleware aggiunto alla pipeline prima del middleware che termina l'ulteriore elaborazione elabora comunque il codice dopo le istruzioni next.Invoke
. Vedere comunque l'avviso seguente sul tentativo di scrivere una risposta che è già stata inviata.
Avviso
Non chiamare next.Invoke
durante o dopo l'invio della risposta al client. Dopo l'avvio di un oggetto HttpResponse , le modifiche generano un'eccezione. Ad esempio, l'impostazione di intestazioni e un codice di stato generano un'eccezione dopo l'avvio della risposta. Scrivere nel corpo della risposta dopo aver chiamato next
:
- Può causare una violazione del protocollo, ad esempio la scrittura di più rispetto all'oggetto dichiarato
Content-Length
. - Può danneggiare il formato del corpo, ad esempio la scrittura di un piè di pagina HTML in un file CSS.
HasStarted è un hint utile per indicare se le intestazioni sono state inviate o se è stato scritto contenuto nel corpo.
Per altre informazioni, vedere Middleware a corto circuito dopo il routing.
Run
Delegati
I delegati Run non ricevono un parametro next
. Il primo delegato Run
è sempre terminale e termina la pipeline. Run
è una convenzione. Alcuni componenti del middleware possono esporre metodi Run[Middleware]
che vengono eseguiti al termine della pipeline:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Per visualizzare i commenti del codice tradotti in lingue diverse dall'inglese, segnalarlo in questo problema di discussione su GitHub.
Nell'esempio precedente il delegato Run
scrive "Hello from 2nd delegate."
nella risposta e quindi termina la pipeline. Se un altro delegato Use
o Run
viene aggiunto dopo il delegato Run
, non viene chiamato.
Preferire l'overload app.Use che richiede il passaggio del contesto a next
Il metodo di estensione app.Use senza allocazione:
- Richiede il passaggio del contesto a
next
. - Salva due allocazioni interne per richiesta necessarie quando si usa l'altro overload.
Per altre informazioni, vedere questo problema in GitHub.
Ordine del middleware
Il diagramma seguente illustra la pipeline di elaborazione completa delle richieste per app ASP.NET Core MVC e Razor Pages. È possibile vedere come, in un'app tipica, vengono ordinati i middleware esistenti e dove vengono aggiunti i middleware personalizzati. Si ha il controllo completo su come riordinare i middleware esistenti o inserire nuovi middleware personalizzati in base alle esigenze per gli specifici scenari.
Il middleware Endpoint nel diagramma precedente esegue la pipeline di filtro per il tipo di app corrispondente, MVC o Razor Pages.
Il middleware Routing nel diagramma precedente è illustrato dopo File statici. Questo è l'ordine implementato dai modelli di progetto chiamando in modo esplicito app.UseRouting. Se non si chiama app.UseRouting
, il middleware Routing viene eseguito all'inizio della pipeline per impostazione predefinita. Per altre informazioni, vedere Routing.
L'ordine di aggiunta dei componenti middleware nel file Program.cs
definisce l'ordine in cui i componenti middleware vengono richiamati per le richieste e l'ordine inverso per la risposta. Questo ordinamento è fondamentale per la sicurezza, le prestazioni e la funzionalità.
Il codice evidenziato seguente in Program.cs
aggiunge componenti middleware correlati alla sicurezza nell'ordine consigliato tipico:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
Nel codice precedente:
- Il middleware che non viene aggiunto durante la creazione di una nuova app Web con singoli account utente viene impostato come commento.
- Non tutti i middleware vengono visualizzati in questo ordine esatto, ma molti lo fanno. Ad esempio:
UseCors
,UseAuthentication
eUseAuthorization
devono essere visualizzati nell'ordine visualizzato.UseCors
attualmente deve essere visualizzato prima diUseResponseCaching
. Questo requisito è illustrato nel problema dotnet/aspnetcore n. 23218 in GitHub.UseRequestLocalization
deve essere visualizzato prima di qualsiasi middleware che potrebbe controllare le impostazioni cultura della richiesta,app.UseStaticFiles()
ad esempio .- UseRateLimiter deve essere chiamato dopo
UseRouting
l'uso delle API specifiche dell'endpoint di limitazione della frequenza. Ad esempio, se viene usato l'attributo[EnableRateLimiting]
,UseRateLimiter
deve essere chiamato dopoUseRouting
. Quando si chiamano solo i limiter globali,UseRateLimiter
è possibile chiamare primaUseRouting
di .
In alcuni scenari, il middleware ha un ordinamento diverso. Ad esempio, l'ordinamento della memorizzazione nella cache e della compressione è specifico dello scenario e sono disponibili più ordinamenti validi. Ad esempio:
app.UseResponseCaching();
app.UseResponseCompression();
Con il codice precedente, l'utilizzo della CPU può essere ridotto memorizzando nella cache la risposta compressa, ma ci si potrebbe ritrovare a memorizzare nella cache più rappresentazioni di una risorsa usando algoritmi di compressione diversi, ad esempio Gzip o Brotli.
L'ordinamento seguente combina i file statici per consentire la memorizzazione nella cache di file statici compressi:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
Il codice Program.cs
seguente aggiunge componenti del middleware per gli scenari di app comuni:
- Gestione di eccezioni/errori
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware della pagina delle eccezioni per gli sviluppatori (UseDeveloperExceptionPage) segnala gli errori di runtime delle app.
- Il middleware della pagina degli errori del database (UseDatabaseErrorPage) segnala gli errori di runtime del database.
- Quando l'app viene eseguita nell'ambiente di produzione:
- Il middleware del gestore delle eccezioni (UseExceptionHandler) intercetta le eccezioni generate nei middleware seguenti.
- Il middleware del protocollo HSTS (HTTP Strict Transport Security) (UseHsts) aggiunge l'intestazione
Strict-Transport-Security
.
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware di reindirizzamento HTTPS (UseHttpsRedirection) reindirizza le richieste HTTP a HTTPS.
- Il middleware dei file statici (UseStaticFiles) restituisce i file statici e impedisce ulteriori elaborazioni della richiesta.
- Il middleware dei criteri per i Cookie (UseCookiePolicy) rende l'app conforme al Regolamento generale sulla protezione dei dati (GDPR).
- Il middleware di routing (UseRouting) instrada le richieste.
- Il middleware di autenticazione (UseAuthentication) tenta di autenticare l'utente prima che sia autorizzato ad accedere a risorse protette.
- Il middleware di autorizzazione (UseAuthorization) autorizza un utente ad accedere alle risorse protette.
- Il middleware di sessione (UseSession) stabilisce e mantiene aggiornato lo stato sessione. Se l'app usa lo stato sessione, chiamare il middleware di sessione dopo il middleware dei criteri per i Cookie e prima del middleware MVC.
- Middleware di routing degli endpoint (UseEndpoints con MapRazorPages) per aggiungere endpoint Razor Pages alla pipeline di richiesta.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
Nell'esempio di codice precedente, ogni metodo di estensione del middleware viene esposto in WebApplicationBuilder tramite lo spazio dei nomi Microsoft.AspNetCore.Builder.
UseExceptionHandler è il primo componente middleware aggiunto alla pipeline. Pertanto, il middleware del gestore di eccezioni intercetta le eccezioni che si verificano nelle chiamate successive.
Il middleware dei file statici viene chiamato nella prima parte della pipeline in moda che possa gestire le richieste ed eseguire un corto circuito senza passare attraverso i componenti rimanenti. Il middleware dei file statici non offre controlli di autorizzazione. I file serviti dal middleware per i file statici, inclusi i file in wwwroot, sono disponibili pubblicamente. Per informazioni su un approccio alla protezione dei file statici, vedere File statici in ASP.NET Core.
Se la richiesta non è gestita dal middleware dei file statici, viene passata al middleware di autenticazione (UseAuthentication) che esegue l'autenticazione. L'autenticazione non esegue il corto circuito di richieste non autenticate. Sebbene il middleware di autenticazione esegua l'autenticazione delle richieste, l'autorizzazione (e il rifiuto) si verifica solo dopo che MVC seleziona una pagina Razor o un controller MVC specifico e un'azione.
L'esempio seguente illustra un ordinamento del middleware nel quale le richieste dei file statici vengono gestite dal middleware dei file statici prima del middleware di compressione delle risposte. I file statici non vengono compressi con questo ordine di middleware. Le risposte Razor Pages possono essere compresse.
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
Per informazioni sulle applicazioni a pagina singola, vedere Panoramica delle app a pagina singola in ASP.NET Core.
Ordine UseCors e UseStaticFiles
L'ordine per la chiamata di UseCors
e UseStaticFiles
dipende dall'app. Per altre informazioni, vedere Ordine UseCors e UseStaticFiles
Ordine del middleware delle intestazioni inoltrate
Il middleware delle intestazioni inoltrate deve essere eseguito prima di altro middleware. Questo ordine garantisce che il middleware basato sulle intestazioni inoltrate possa usare i valori di intestazione per l'elaborazione. Per eseguire il middleware delle intestazioni inoltrate dopo il middleware di diagnostica e gestione degli errori, vedere Ordine del middleware delle intestazioni inoltrate.
Creare un ramo nella pipeline del middleware
Le estensioni Map vengono usate come convenzione per la diramazione della pipeline. Map
crea un ramo nella pipeline delle richieste in base alle corrispondenze del percorso della richiesta specificato. Se il percorso della richiesta inizia con il percorso specificato, il ramo viene eseguito.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente.
Richiedi | Risposta |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
Quando si usa Map
, i segmenti di percorso corrispondenti vengono rimossi da HttpRequest.Path
e aggiunti a HttpRequest.PathBase
per ogni richiesta.
Map
supporta l'annidamento, ad esempio:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
può anche trovare la corrispondenza di più segmenti contemporaneamente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen crea un ramo nella pipeline delle richieste in base al risultato del predicato specificato. È possibile usare qualsiasi predicato di tipo Func<HttpContext, bool>
per mappare le richieste a un nuovo ramo della pipeline. Nell'esempio seguente viene usato un predicato per rilevare la presenza di una variabile di stringa di query branch
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente:
Richiedi | Risposta |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen crea anche un ramo nella pipeline delle richieste in base al risultato del predicato specificato. Diversamente da MapWhen
, questo ramo viene ricongiunto alla pipeline principale se non esegue il corto circuito o contiene un middleware terminale:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
Nell'esempio precedente viene scritta una risposta Hello from non-Map delegate.
per tutte le richieste. Se la richiesta include una variabile di stringa di query branch
, il relativo valore viene registrato prima del ricongiungimento con la pipeline principale.
Middleware incorporato
ASP.NET Core include i componenti middleware seguenti. Nella colonna Ordinamento sono disponibili note sul posizionamento del middleware nella pipeline di elaborazione delle richieste e indicazioni sulle condizioni nelle quali il middleware può terminare l'elaborazione delle richieste. Quando un middleware esegue un corto circuito della pipeline di elaborazione delle richieste e impedisce al middleware a valle di elaborare una richiesta, viene denominato middleware terminale. Per altre informazioni sul corto circuito, vedere la sezione Creare una pipeline middleware con WebApplication .
Middleware | Descrizione | Ordinamento |
---|---|---|
Autenticazione | Offre il supporto dell'autenticazione. | Prima di HttpContext.User . Terminale per i callback OAuth. |
Autorizzazione | Fornisce il supporto per l'autorizzazione. | Subito dopo il middleware di autenticazione. |
Criteri per i Cookie | Registra il consenso degli utenti per l'archiviazione delle informazioni personali e applica gli standard minimi per i campi dei cookie, come secure e SameSite . |
Prima del middleware che emette i cookie. Esempi: Autenticazione, Sessione, MVC (TempData). |
CORS | Configura la condivisione di risorse tra le origini (CORS). | Prima dei componenti che usano CORS. UseCors attualmente deve precedere UseResponseCaching a causa di questo bug. |
DeveloperExceptionPage | Genera una pagina con informazioni sugli errori destinate all'uso solo nell'ambiente di sviluppo. | Prima dei componenti che generano errori. I modelli di progetto registrano automaticamente questo middleware come primo middleware nella pipeline quando l'ambiente è di sviluppo. |
Diagnostica | Diversi middleware separati che forniscono una pagina delle eccezioni per sviluppatori, la gestione delle eccezioni, pagine di codici di stato e la pagina Web predefinita per le nuove app. | Prima dei componenti che generano errori. Terminale per le eccezioni o per la gestione della pagina Web predefinita per le nuove app. |
Intestazioni inoltrate | Inoltra le intestazioni proxy nella richiesta corrente. | Prima dei componenti che usano i campi aggiornati. Esempi: schema, host, IP client, metodo. |
Controllo integrità | Controlla l'integrità di un'app ASP.NET Core e le relative dipendenze, come il controllo della disponibilità del database. | Terminale se una richiesta corrisponde a un endpoint di controllo di integrità. |
Propagazione delle intestazioni | Propaga le intestazioni HTTP dalla richiesta in ingresso alle richieste client HTTP in uscita. | |
Registrazione HTTP | Registra le richieste e risposte HTTP. | All'inizio della pipeline del middleware. |
Override del metodo HTTP | Consente a una richiesta POST in arrivo di eseguire l'override del metodo. | Prima dei componenti che usano il metodo aggiornato. |
Reindirizzamento HTTPS | Reindirizzare tutte le richieste HTTP a HTTPS. | Prima dei componenti che usano l'URL. |
Protocollo HTTP Strict Transport Security (HSTS) | Middleware di ottimizzazione della sicurezza che aggiunge un'intestazione della risposta speciale. | Prima dell'invio delle risposte e dopo i componenti che modificano le richieste. Esempi: intestazioni inoltrate, riscrittura dell'URL. |
MVC | Elabora le richieste con MVC/Razor Pages. | Terminale se una richiesta corrisponde a una route. |
OWIN | Interoperabilità con app, server e middleware basati su OWIN. | Terminale se la richiesta viene elaborata completamente dal middleware OWIN. |
Memorizzazione nella cache dell'output | Fornisce supporto per la memorizzazione nella cache delle risposte in base alla configurazione. | Prima dei componenti che richiedono la memorizzazione nella cache. UseRouting deve precedere UseOutputCaching . UseCORS deve precedere UseOutputCaching . |
Memorizzazione nella cache delle risposte | Offre il supporto per la memorizzazione delle risposte nella cache. Ciò richiede la partecipazione dei clienti al lavoro. Usare la memorizzazione nella cache dell'output per il controllo server completo. | Prima dei componenti che richiedono la memorizzazione nella cache. UseCORS deve precedere UseResponseCaching . In genere non è utile per le app dell'interfaccia utente, ad Razor esempio Pagine, perché i browser impostano in genere intestazioni di richiesta che impediscono la memorizzazione nella cache. La memorizzazione nella cache dell'output offre vantaggi alle app dell'interfaccia utente. |
Decompressione richieste | Offre il supporto per la decompressione delle richieste. | Prima dei componenti che leggono il corpo della richiesta. |
Compressione delle risposte | Offre il supporto per la compressione delle risposte. | Prima dei componenti che richiedono la compressione. |
Localizzazione della richiesta | Offre il supporto per la localizzazione. | Prima dei componenti sensibili alla localizzazione. Deve comparire dopo il middleware di routing quando si usa RouteDataRequestCultureProvider. |
Timeout delle richieste | Fornisce supporto per la configurazione dei timeout delle richieste, globali e per endpoint. | UseRequestTimeouts deve venire dopo UseExceptionHandler , UseDeveloperExceptionPage e UseRouting . |
Endpoint Routing | Definisce e vincola le route di richiesta. | Terminale per le route corrispondenti. |
SPA | Gestisce tutte le richieste da questo punto nella catena del middleware restituendo la pagina predefinita per l'applicazione a pagina singola | In posizione avanzata nella catena, in modo che abbiano la precedenza altri middleware per la gestione di file statici, azioni MVC e così via. |
Sessione | Offre il supporto per la gestione delle sessioni utente. | Prima dei componenti che richiedono la Sessione. |
File statici | Offre il supporto per la gestione di file statici e l'esplorazione directory. | Terminale se una richiesta corrisponde a un file. |
URL Rewrite (Riscrittura URL) | Offre il supporto per la riscrittura degli URL e il reindirizzamento delle richieste. | Prima dei componenti che usano l'URL. |
W3CLogging | Genera i log di accesso al server nel formato di file di log esteso W3C. | All'inizio della pipeline del middleware. |
WebSocket | Abilita il protocollo WebSocket. | Prima dei componenti necessari per accettare le richieste WebSocket. |
Risorse aggiuntive
- Le opzioni di durata e registrazione contengono un esempio completo di middleware con servizi di durata con ambito, temporanei e singleton.
- Scrivere middleware di ASP.NET Core personalizzato
- Testare middleware di ASP.NET Core
- Configurare gRPC-Web in ASP.NET Core
- Eseguire la migrazione di gestori e moduli HTTP a middleware di ASP.NET Core
- Avvio dell'app in ASP.NET Core
- Funzionalità di richiesta in ASP.NET Core
- Attivazione middleware basata su factory in ASP.NET Core
- Attivazione del middleware con un contenitore di terze parti in ASP.NET Core
Il middleware è un software che viene assemblato in una pipeline dell'app per gestire richieste e risposte. Ogni componente:
- Sceglie se passare la richiesta al componente successivo nella pipeline.
- Può eseguire le operazioni prima e dopo il componente successivo nella pipeline.
Per compilare la pipeline delle richieste vengono usati i delegati di richiesta. I delegati di richiesta gestiscono ogni richiesta HTTP.
I delegati di richiesta vengono configurati tramite i metodi di estensione Run, Map e Use. È possibile specificare un singolo delegato di richiesta inline come metodo anonimo (chiamato middleware inline) o definirlo in una classe riutilizzabile. Le classi riutilizzabili o i metodi anonimi inline costituiscono il middleware o sono noti anche come componenti middleware. Ogni componente middleware è responsabile della chiamata del componente seguente nella pipeline o del corto circuito della pipeline. Quando un middleware esegue un corto circuito, viene denominato middleware terminale perché impedisce all'ulteriore middleware di elaborare la richiesta.
In Eseguire la migrazione di moduli HTTP in middleware vengono spiegate le differenze tra le pipeline delle richieste in ASP.NET Core e in ASP.NET 4.x e sono riportati altri esempi di middleware.
Analisi del codice middleware
ASP.NET Core include molti analizzatori della piattaforma del compilatore che controllano la qualità del codice dell'applicazione. Per altre informazioni, vedere Analisi del codice nelle app di ASP.NET Core.
Creare una pipeline middleware con WebApplication
La pipeline delle richieste ASP.NET Core è costituita da una sequenza di delegati di richiesta, chiamati uno dopo l'altro. Il diagramma seguente illustra il concetto. Il thread di esecuzione seguente le frecce nere.
I delegati possono eseguire le operazioni prima del delegato successivo e dopo di esso. I delegati che gestiscono le eccezioni devono essere chiamati nella prima parte della pipeline in modo che possano individuare le eccezioni che si verificano nelle parti successive della pipeline.
L'app di ASP.NET Core più semplice imposta un delegato di richiesta singolo che gestisce tutte le richieste. In questo caso non è inclusa una pipeline di richieste effettiva. Al contrario, viene chiamata una singola funzione anonima in risposta a ogni richiesta HTTP.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
È possibile concatenare più delegati di richiesta insieme con Use. Il parametro next
rappresenta il delegato successivo nella pipeline. È possibile eseguire il corto circuito della pipeline non chiamando il parametro next
. In genere è possibile eseguire un'azione prima e dopo il delegato next
, come illustra l'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Quando un delegato non passa una richiesta al delegato successivo, si crea un cosiddetto corto circuito della pipeline delle richieste. Il corto circuito è spesso opportuno poiché evita l'esecuzione di operazioni non necessarie. Ad esempio, il middleware dei file statici può fungere da middleware terminale elaborando una richiesta per un file statico e corto circuito della rest pipeline. Il middleware aggiunto alla pipeline prima del middleware che termina l'ulteriore elaborazione elabora comunque il codice dopo le istruzioni next.Invoke
. Vedere comunque l'avviso seguente sul tentativo di scrivere una risposta che è già stata inviata.
Avviso
Non chiamare next.Invoke
dopo aver inviato la risposta al client. Le modifiche apportate a HttpResponse dopo l'avvio della risposta generano un'eccezione. Ad esempio, l'impostazione delle intestazioni e di un codice di stato generano un'eccezione. Scrivere nel corpo della risposta dopo aver chiamato next
:
- Può causare una violazione del protocollo. Ad esempio, scrivere un contenuto che supera il valore
Content-Length
specificato. - Può danneggiare il formato del corpo. Ad esempio, scrivere un piè di pagina HTML in un file CSS.
HasStarted è un hint utile per indicare se le intestazioni sono state inviate o se è stato scritto contenuto nel corpo.
I delegati Run non ricevono un parametro next
. Il primo delegato Run
è sempre terminale e termina la pipeline. Run
è una convenzione. Alcuni componenti del middleware possono esporre metodi Run[Middleware]
che vengono eseguiti al termine della pipeline:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Per visualizzare i commenti del codice tradotti in lingue diverse dall'inglese, segnalarlo in questo problema di discussione su GitHub.
Nell'esempio precedente il delegato Run
scrive "Hello from 2nd delegate."
nella risposta e quindi termina la pipeline. Se un altro delegato Use
o Run
viene aggiunto dopo il delegato Run
, non viene chiamato.
Preferire l'overload app.Use che richiede il passaggio del contesto a next
Il metodo di estensione app.Use senza allocazione:
- Richiede il passaggio del contesto a
next
. - Salva due allocazioni interne per richiesta necessarie quando si usa l'altro overload.
Per altre informazioni, vedere questo problema in GitHub.
Ordine del middleware
Il diagramma seguente illustra la pipeline di elaborazione completa delle richieste per app ASP.NET Core MVC e Razor Pages. È possibile vedere come, in un'app tipica, vengono ordinati i middleware esistenti e dove vengono aggiunti i middleware personalizzati. Si ha il controllo completo su come riordinare i middleware esistenti o inserire nuovi middleware personalizzati in base alle esigenze per gli specifici scenari.
Il middleware Endpoint nel diagramma precedente esegue la pipeline di filtro per il tipo di app corrispondente, MVC o Razor Pages.
Il middleware Routing nel diagramma precedente è illustrato dopo File statici. Questo è l'ordine implementato dai modelli di progetto chiamando in modo esplicito app.UseRouting. Se non si chiama app.UseRouting
, il middleware Routing viene eseguito all'inizio della pipeline per impostazione predefinita. Per altre informazioni, vedere Routing.
L'ordine di aggiunta dei componenti middleware nel file Program.cs
definisce l'ordine in cui i componenti middleware vengono richiamati per le richieste e l'ordine inverso per la risposta. Questo ordinamento è fondamentale per la sicurezza, le prestazioni e la funzionalità.
Il codice evidenziato seguente in Program.cs
aggiunge componenti middleware correlati alla sicurezza nell'ordine consigliato tipico:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebMiddleware.Data;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRateLimiter();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
Nel codice precedente:
- Il middleware che non viene aggiunto durante la creazione di una nuova app Web con singoli account utente viene impostato come commento.
- Non tutti i middleware vengono visualizzati in questo ordine esatto, ma molti lo fanno. Ad esempio:
UseCors
,UseAuthentication
eUseAuthorization
devono essere visualizzati nell'ordine visualizzato.UseCors
attualmente deve essere visualizzato prima diUseResponseCaching
. Questo requisito è illustrato nel problema dotnet/aspnetcore n. 23218 in GitHub.UseRequestLocalization
deve essere visualizzato prima di qualsiasi middleware che potrebbe controllare le impostazioni cultura della richiesta,app.UseStaticFiles()
ad esempio .- UseRateLimiter deve essere chiamato dopo
UseRouting
l'uso delle API specifiche dell'endpoint di limitazione della frequenza. Ad esempio, se viene usato l'attributo[EnableRateLimiting]
,UseRateLimiter
deve essere chiamato dopoUseRouting
. Quando si chiamano solo i limiter globali,UseRateLimiter
è possibile chiamare primaUseRouting
di .
In alcuni scenari, il middleware ha un ordinamento diverso. Ad esempio, l'ordinamento della memorizzazione nella cache e della compressione è specifico dello scenario e sono disponibili più ordinamenti validi. Ad esempio:
app.UseResponseCaching();
app.UseResponseCompression();
Con il codice precedente, l'utilizzo della CPU può essere ridotto memorizzando nella cache la risposta compressa, ma ci si potrebbe ritrovare a memorizzare nella cache più rappresentazioni di una risorsa usando algoritmi di compressione diversi, ad esempio Gzip o Brotli.
L'ordinamento seguente combina i file statici per consentire la memorizzazione nella cache di file statici compressi:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
Il codice Program.cs
seguente aggiunge componenti del middleware per gli scenari di app comuni:
- Gestione di eccezioni/errori
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware della pagina delle eccezioni per gli sviluppatori (UseDeveloperExceptionPage) segnala gli errori di runtime delle app.
- Il middleware della pagina degli errori del database (UseDatabaseErrorPage) segnala gli errori di runtime del database.
- Quando l'app viene eseguita nell'ambiente di produzione:
- Il middleware del gestore delle eccezioni (UseExceptionHandler) intercetta le eccezioni generate nei middleware seguenti.
- Il middleware del protocollo HSTS (HTTP Strict Transport Security) (UseHsts) aggiunge l'intestazione
Strict-Transport-Security
.
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware di reindirizzamento HTTPS (UseHttpsRedirection) reindirizza le richieste HTTP a HTTPS.
- Il middleware dei file statici (UseStaticFiles) restituisce i file statici e impedisce ulteriori elaborazioni della richiesta.
- Il middleware dei criteri per i Cookie (UseCookiePolicy) rende l'app conforme al Regolamento generale sulla protezione dei dati (GDPR).
- Il middleware di routing (UseRouting) instrada le richieste.
- Il middleware di autenticazione (UseAuthentication) tenta di autenticare l'utente prima che sia autorizzato ad accedere a risorse protette.
- Il middleware di autorizzazione (UseAuthorization) autorizza un utente ad accedere alle risorse protette.
- Il middleware di sessione (UseSession) stabilisce e mantiene aggiornato lo stato sessione. Se l'app usa lo stato sessione, chiamare il middleware di sessione dopo il middleware dei criteri per i Cookie e prima del middleware MVC.
- Middleware di routing degli endpoint (UseEndpoints con MapRazorPages) per aggiungere endpoint Razor Pages alla pipeline di richiesta.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
Nell'esempio di codice precedente, ogni metodo di estensione del middleware viene esposto in WebApplicationBuilder tramite lo spazio dei nomi Microsoft.AspNetCore.Builder.
UseExceptionHandler è il primo componente middleware aggiunto alla pipeline. Pertanto, il middleware del gestore di eccezioni intercetta le eccezioni che si verificano nelle chiamate successive.
Il middleware dei file statici viene chiamato nella prima parte della pipeline in moda che possa gestire le richieste ed eseguire un corto circuito senza passare attraverso i componenti rimanenti. Il middleware dei file statici non offre controlli di autorizzazione. I file serviti dal middleware per i file statici, inclusi i file in wwwroot, sono disponibili pubblicamente. Per informazioni su un approccio alla protezione dei file statici, vedere File statici in ASP.NET Core.
Se la richiesta non è gestita dal middleware dei file statici, viene passata al middleware di autenticazione (UseAuthentication) che esegue l'autenticazione. L'autenticazione non esegue il corto circuito di richieste non autenticate. Sebbene il middleware di autenticazione esegua l'autenticazione delle richieste, l'autorizzazione (e il rifiuto) si verifica solo dopo che MVC seleziona una pagina Razor o un controller MVC specifico e un'azione.
L'esempio seguente illustra un ordinamento del middleware nel quale le richieste dei file statici vengono gestite dal middleware dei file statici prima del middleware di compressione delle risposte. I file statici non vengono compressi con questo ordine di middleware. Le risposte Razor Pages possono essere compresse.
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
Per informazioni sulle applicazioni a pagina singola, vedere le guide per i modelli di progetto React e Angular.
Ordine UseCors e UseStaticFiles
L'ordine per la chiamata di UseCors
e UseStaticFiles
dipende dall'app. Per altre informazioni, vedere Ordine UseCors e UseStaticFiles
Ordine del middleware delle intestazioni inoltrate
Il middleware delle intestazioni inoltrate deve essere eseguito prima di altro middleware. Questo ordine garantisce che il middleware basato sulle intestazioni inoltrate possa usare i valori di intestazione per l'elaborazione. Per eseguire il middleware delle intestazioni inoltrate dopo il middleware di diagnostica e gestione degli errori, vedere Ordine del middleware delle intestazioni inoltrate.
Creare un ramo nella pipeline del middleware
Le estensioni Map vengono usate come convenzione per la diramazione della pipeline. Map
crea un ramo nella pipeline delle richieste in base alle corrispondenze del percorso della richiesta specificato. Se il percorso della richiesta inizia con il percorso specificato, il ramo viene eseguito.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente.
Richiedi | Risposta |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
Quando si usa Map
, i segmenti di percorso corrispondenti vengono rimossi da HttpRequest.Path
e aggiunti a HttpRequest.PathBase
per ogni richiesta.
Map
supporta l'annidamento, ad esempio:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
può anche trovare la corrispondenza di più segmenti contemporaneamente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen crea un ramo nella pipeline delle richieste in base al risultato del predicato specificato. È possibile usare qualsiasi predicato di tipo Func<HttpContext, bool>
per mappare le richieste a un nuovo ramo della pipeline. Nell'esempio seguente viene usato un predicato per rilevare la presenza di una variabile di stringa di query branch
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente:
Richiedi | Risposta |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen crea anche un ramo nella pipeline delle richieste in base al risultato del predicato specificato. Diversamente da MapWhen
, questo ramo viene ricongiunto alla pipeline principale se non esegue il corto circuito o contiene un middleware terminale:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
Nell'esempio precedente viene scritta una risposta Hello from non-Map delegate.
per tutte le richieste. Se la richiesta include una variabile di stringa di query branch
, il relativo valore viene registrato prima del ricongiungimento con la pipeline principale.
Middleware incorporato
ASP.NET Core include i componenti middleware seguenti. Nella colonna Ordinamento sono disponibili note sul posizionamento del middleware nella pipeline di elaborazione delle richieste e indicazioni sulle condizioni nelle quali il middleware può terminare l'elaborazione delle richieste. Quando un middleware esegue un corto circuito della pipeline di elaborazione delle richieste e impedisce al middleware a valle di elaborare una richiesta, viene denominato middleware terminale. Per altre informazioni sul corto circuito, vedere la sezione Creare una pipeline middleware con WebApplication .
Middleware | Descrizione | Ordinamento |
---|---|---|
Autenticazione | Offre il supporto dell'autenticazione. | Prima di HttpContext.User . Terminale per i callback OAuth. |
Autorizzazione | Fornisce il supporto per l'autorizzazione. | Subito dopo il middleware di autenticazione. |
Criteri per i Cookie | Registra il consenso degli utenti per l'archiviazione delle informazioni personali e applica gli standard minimi per i campi dei cookie, come secure e SameSite . |
Prima del middleware che emette i cookie. Esempi: Autenticazione, Sessione, MVC (TempData). |
CORS | Configura la condivisione di risorse tra le origini (CORS). | Prima dei componenti che usano CORS. UseCors attualmente deve precedere UseResponseCaching a causa di questo bug. |
DeveloperExceptionPage | Genera una pagina con informazioni sugli errori destinate all'uso solo nell'ambiente di sviluppo. | Prima dei componenti che generano errori. I modelli di progetto registrano automaticamente questo middleware come primo middleware nella pipeline quando l'ambiente è di sviluppo. |
Diagnostica | Diversi middleware separati che forniscono una pagina delle eccezioni per sviluppatori, la gestione delle eccezioni, pagine di codici di stato e la pagina Web predefinita per le nuove app. | Prima dei componenti che generano errori. Terminale per le eccezioni o per la gestione della pagina Web predefinita per le nuove app. |
Intestazioni inoltrate | Inoltra le intestazioni proxy nella richiesta corrente. | Prima dei componenti che usano i campi aggiornati. Esempi: schema, host, IP client, metodo. |
Controllo integrità | Controlla l'integrità di un'app ASP.NET Core e le relative dipendenze, come il controllo della disponibilità del database. | Terminale se una richiesta corrisponde a un endpoint di controllo di integrità. |
Propagazione delle intestazioni | Propaga le intestazioni HTTP dalla richiesta in ingresso alle richieste client HTTP in uscita. | |
Registrazione HTTP | Registra le richieste e risposte HTTP. | All'inizio della pipeline del middleware. |
Override del metodo HTTP | Consente a una richiesta POST in arrivo di eseguire l'override del metodo. | Prima dei componenti che usano il metodo aggiornato. |
Reindirizzamento HTTPS | Reindirizzare tutte le richieste HTTP a HTTPS. | Prima dei componenti che usano l'URL. |
Protocollo HTTP Strict Transport Security (HSTS) | Middleware di ottimizzazione della sicurezza che aggiunge un'intestazione della risposta speciale. | Prima dell'invio delle risposte e dopo i componenti che modificano le richieste. Esempi: intestazioni inoltrate, riscrittura dell'URL. |
MVC | Elabora le richieste con MVC/Razor Pages. | Terminale se una richiesta corrisponde a una route. |
OWIN | Interoperabilità con app, server e middleware basati su OWIN. | Terminale se la richiesta viene elaborata completamente dal middleware OWIN. |
Memorizzazione nella cache dell'output | Fornisce supporto per la memorizzazione nella cache delle risposte in base alla configurazione. | Prima dei componenti che richiedono la memorizzazione nella cache. UseRouting deve precedere UseOutputCaching . UseCORS deve precedere UseOutputCaching . |
Memorizzazione nella cache delle risposte | Offre il supporto per la memorizzazione delle risposte nella cache. Ciò richiede la partecipazione dei clienti al lavoro. Usare la memorizzazione nella cache dell'output per il controllo server completo. | Prima dei componenti che richiedono la memorizzazione nella cache. UseCORS deve precedere UseResponseCaching . In genere non è utile per le app dell'interfaccia utente, ad Razor esempio Pagine, perché i browser impostano in genere intestazioni di richiesta che impediscono la memorizzazione nella cache. La memorizzazione nella cache dell'output offre vantaggi alle app dell'interfaccia utente. |
Decompressione richieste | Offre il supporto per la decompressione delle richieste. | Prima dei componenti che leggono il corpo della richiesta. |
Compressione delle risposte | Offre il supporto per la compressione delle risposte. | Prima dei componenti che richiedono la compressione. |
Localizzazione della richiesta | Offre il supporto per la localizzazione. | Prima dei componenti sensibili alla localizzazione. Deve comparire dopo il middleware di routing quando si usa RouteDataRequestCultureProvider. |
Endpoint Routing | Definisce e vincola le route di richiesta. | Terminale per le route corrispondenti. |
SPA | Gestisce tutte le richieste da questo punto nella catena del middleware restituendo la pagina predefinita per l'applicazione a pagina singola | In posizione avanzata nella catena, in modo che abbiano la precedenza altri middleware per la gestione di file statici, azioni MVC e così via. |
Sessione | Offre il supporto per la gestione delle sessioni utente. | Prima dei componenti che richiedono la Sessione. |
File statici | Offre il supporto per la gestione di file statici e l'esplorazione directory. | Terminale se una richiesta corrisponde a un file. |
URL Rewrite (Riscrittura URL) | Offre il supporto per la riscrittura degli URL e il reindirizzamento delle richieste. | Prima dei componenti che usano l'URL. |
W3CLogging | Genera i log di accesso al server nel formato di file di log esteso W3C. | All'inizio della pipeline del middleware. |
WebSocket | Abilita il protocollo WebSocket. | Prima dei componenti necessari per accettare le richieste WebSocket. |
Risorse aggiuntive
- Le opzioni di durata e registrazione contengono un esempio completo di middleware con servizi di durata con ambito, temporanei e singleton.
- Scrivere middleware di ASP.NET Core personalizzato
- Testare middleware di ASP.NET Core
- Configurare gRPC-Web in ASP.NET Core
- Eseguire la migrazione di gestori e moduli HTTP a middleware di ASP.NET Core
- Avvio dell'app in ASP.NET Core
- Funzionalità di richiesta in ASP.NET Core
- Attivazione middleware basata su factory in ASP.NET Core
- Attivazione del middleware con un contenitore di terze parti in ASP.NET Core
Il middleware è un software che viene assemblato in una pipeline dell'app per gestire richieste e risposte. Ogni componente:
- Sceglie se passare la richiesta al componente successivo nella pipeline.
- Può eseguire le operazioni prima e dopo il componente successivo nella pipeline.
Per compilare la pipeline delle richieste vengono usati i delegati di richiesta. I delegati di richiesta gestiscono ogni richiesta HTTP.
I delegati di richiesta vengono configurati tramite i metodi di estensione Run, Map e Use. È possibile specificare un singolo delegato di richiesta inline come metodo anonimo (chiamato middleware inline) o definirlo in una classe riutilizzabile. Le classi riutilizzabili o i metodi anonimi inline costituiscono il middleware o sono noti anche come componenti middleware. Ogni componente middleware è responsabile della chiamata del componente seguente nella pipeline o del corto circuito della pipeline. Quando un middleware esegue un corto circuito, viene denominato middleware terminale perché impedisce all'ulteriore middleware di elaborare la richiesta.
In Eseguire la migrazione di moduli HTTP in middleware vengono spiegate le differenze tra le pipeline delle richieste in ASP.NET Core e in ASP.NET 4.x e sono riportati altri esempi di middleware.
Analisi del codice middleware
ASP.NET Core include molti analizzatori della piattaforma del compilatore che controllano la qualità del codice dell'applicazione. Per altre informazioni, vedere Analisi del codice nelle app di ASP.NET Core.
Creare una pipeline middleware con WebApplication
La pipeline delle richieste ASP.NET Core è costituita da una sequenza di delegati di richiesta, chiamati uno dopo l'altro. Il diagramma seguente illustra il concetto. Il thread di esecuzione seguente le frecce nere.
I delegati possono eseguire le operazioni prima del delegato successivo e dopo di esso. I delegati che gestiscono le eccezioni devono essere chiamati nella prima parte della pipeline in modo che possano individuare le eccezioni che si verificano nelle parti successive della pipeline.
L'app di ASP.NET Core più semplice imposta un delegato di richiesta singolo che gestisce tutte le richieste. In questo caso non è inclusa una pipeline di richieste effettiva. Al contrario, viene chiamata una singola funzione anonima in risposta a ogni richiesta HTTP.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world!");
});
app.Run();
È possibile concatenare più delegati di richiesta insieme con Use. Il parametro next
rappresenta il delegato successivo nella pipeline. È possibile eseguire il corto circuito della pipeline non chiamando il parametro next
. In genere è possibile eseguire un'azione prima e dopo il delegato next
, come illustra l'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Quando un delegato non passa una richiesta al delegato successivo, si crea un cosiddetto corto circuito della pipeline delle richieste. Il corto circuito è spesso opportuno poiché evita l'esecuzione di operazioni non necessarie. Ad esempio, il middleware dei file statici può fungere da middleware terminale elaborando una richiesta per un file statico e corto circuito della rest pipeline. Il middleware aggiunto alla pipeline prima del middleware che termina l'ulteriore elaborazione elabora comunque il codice dopo le istruzioni next.Invoke
. Vedere comunque l'avviso seguente sul tentativo di scrivere una risposta che è già stata inviata.
Avviso
Non chiamare next.Invoke
dopo aver inviato la risposta al client. Le modifiche apportate a HttpResponse dopo l'avvio della risposta generano un'eccezione. Ad esempio, l'impostazione delle intestazioni e di un codice di stato generano un'eccezione. Scrivere nel corpo della risposta dopo aver chiamato next
:
- Può causare una violazione del protocollo. Ad esempio, scrivere un contenuto che supera il valore
Content-Length
specificato. - Può danneggiare il formato del corpo. Ad esempio, scrivere un piè di pagina HTML in un file CSS.
HasStarted è un hint utile per indicare se le intestazioni sono state inviate o se è stato scritto contenuto nel corpo.
I delegati Run non ricevono un parametro next
. Il primo delegato Run
è sempre terminale e termina la pipeline. Run
è una convenzione. Alcuni componenti del middleware possono esporre metodi Run[Middleware]
che vengono eseguiti al termine della pipeline:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
// Do work that can write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
app.Run();
Per visualizzare i commenti del codice tradotti in lingue diverse dall'inglese, segnalarlo in questo problema di discussione su GitHub.
Nell'esempio precedente il delegato Run
scrive "Hello from 2nd delegate."
nella risposta e quindi termina la pipeline. Se un altro delegato Use
o Run
viene aggiunto dopo il delegato Run
, non viene chiamato.
Preferire l'overload app.Use che richiede il passaggio del contesto a next
Il metodo di estensione app.Use senza allocazione:
- Richiede il passaggio del contesto a
next
. - Salva due allocazioni interne per richiesta necessarie quando si usa l'altro overload.
Per altre informazioni, vedere questo problema in GitHub.
Ordine del middleware
Il diagramma seguente illustra la pipeline di elaborazione completa delle richieste per app ASP.NET Core MVC e Razor Pages. È possibile vedere come, in un'app tipica, vengono ordinati i middleware esistenti e dove vengono aggiunti i middleware personalizzati. Si ha il controllo completo su come riordinare i middleware esistenti o inserire nuovi middleware personalizzati in base alle esigenze per gli specifici scenari.
Il middleware Endpoint nel diagramma precedente esegue la pipeline di filtro per il tipo di app corrispondente, MVC o Razor Pages.
Il middleware Routing nel diagramma precedente è illustrato dopo File statici. Questo è l'ordine implementato dai modelli di progetto chiamando in modo esplicito app.UseRouting. Se non si chiama app.UseRouting
, il middleware Routing viene eseguito all'inizio della pipeline per impostazione predefinita. Per altre informazioni, vedere Routing.
L'ordine di aggiunta dei componenti middleware nel file Program.cs
definisce l'ordine in cui i componenti middleware vengono richiamati per le richieste e l'ordine inverso per la risposta. Questo ordinamento è fondamentale per la sicurezza, le prestazioni e la funzionalità.
Il codice evidenziato seguente in Program.cs
aggiunge componenti middleware correlati alla sicurezza nell'ordine consigliato tipico:
using IndividualAccountsExample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Nel codice precedente:
- Il middleware che non viene aggiunto durante la creazione di una nuova app Web con singoli account utente viene impostato come commento.
- Non tutti i middleware vengono visualizzati in questo ordine esatto, ma molti lo fanno. Ad esempio:
UseCors
,UseAuthentication
eUseAuthorization
devono essere visualizzati nell'ordine visualizzato.UseCors
attualmente deve essere visualizzato prima diUseResponseCaching
. Questo requisito è illustrato nel problema dotnet/aspnetcore n. 23218 in GitHub.UseRequestLocalization
deve comparire prima di qualsiasi middleware che potrebbe controllare le impostazioni cultura della richiesta (ad esempio,app.UseMvcWithDefaultRoute()
).
In alcuni scenari, il middleware ha un ordinamento diverso. Ad esempio, l'ordinamento della memorizzazione nella cache e della compressione è specifico dello scenario e sono disponibili più ordinamenti validi. Ad esempio:
app.UseResponseCaching();
app.UseResponseCompression();
Con il codice precedente, l'utilizzo della CPU può essere ridotto memorizzando nella cache la risposta compressa, ma ci si potrebbe ritrovare a memorizzare nella cache più rappresentazioni di una risorsa usando algoritmi di compressione diversi, ad esempio Gzip o Brotli.
L'ordinamento seguente combina i file statici per consentire la memorizzazione nella cache di file statici compressi:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
Il codice Program.cs
seguente aggiunge componenti del middleware per gli scenari di app comuni:
- Gestione di eccezioni/errori
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware della pagina delle eccezioni per gli sviluppatori (UseDeveloperExceptionPage) segnala gli errori di runtime delle app.
- Il middleware della pagina degli errori del database (UseDatabaseErrorPage) segnala gli errori di runtime del database.
- Quando l'app viene eseguita nell'ambiente di produzione:
- Il middleware del gestore delle eccezioni (UseExceptionHandler) intercetta le eccezioni generate nei middleware seguenti.
- Il middleware del protocollo HSTS (HTTP Strict Transport Security) (UseHsts) aggiunge l'intestazione
Strict-Transport-Security
.
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware di reindirizzamento HTTPS (UseHttpsRedirection) reindirizza le richieste HTTP a HTTPS.
- Il middleware dei file statici (UseStaticFiles) restituisce i file statici e impedisce ulteriori elaborazioni della richiesta.
- Il middleware dei criteri per i Cookie (UseCookiePolicy) rende l'app conforme al Regolamento generale sulla protezione dei dati (GDPR).
- Il middleware di routing (UseRouting) instrada le richieste.
- Il middleware di autenticazione (UseAuthentication) tenta di autenticare l'utente prima che sia autorizzato ad accedere a risorse protette.
- Il middleware di autorizzazione (UseAuthorization) autorizza un utente ad accedere alle risorse protette.
- Il middleware di sessione (UseSession) stabilisce e mantiene aggiornato lo stato sessione. Se l'app usa lo stato sessione, chiamare il middleware di sessione dopo il middleware dei criteri per i Cookie e prima del middleware MVC.
- Middleware di routing degli endpoint (UseEndpoints con MapRazorPages) per aggiungere endpoint Razor Pages alla pipeline di richiesta.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
Nell'esempio di codice precedente, ogni metodo di estensione del middleware viene esposto in WebApplicationBuilder tramite lo spazio dei nomi Microsoft.AspNetCore.Builder.
UseExceptionHandler è il primo componente middleware aggiunto alla pipeline. Pertanto, il middleware del gestore di eccezioni intercetta le eccezioni che si verificano nelle chiamate successive.
Il middleware dei file statici viene chiamato nella prima parte della pipeline in moda che possa gestire le richieste ed eseguire un corto circuito senza passare attraverso i componenti rimanenti. Il middleware dei file statici non offre controlli di autorizzazione. I file serviti dal middleware per i file statici, inclusi i file in wwwroot, sono disponibili pubblicamente. Per informazioni su un approccio alla protezione dei file statici, vedere File statici in ASP.NET Core.
Se la richiesta non è gestita dal middleware dei file statici, viene passata al middleware di autenticazione (UseAuthentication) che esegue l'autenticazione. L'autenticazione non esegue il corto circuito di richieste non autenticate. Sebbene il middleware di autenticazione esegua l'autenticazione delle richieste, l'autorizzazione (e il rifiuto) si verifica solo dopo che MVC seleziona una pagina Razor o un controller MVC specifico e un'azione.
L'esempio seguente illustra un ordinamento del middleware nel quale le richieste dei file statici vengono gestite dal middleware dei file statici prima del middleware di compressione delle risposte. I file statici non vengono compressi con questo ordine di middleware. Le risposte Razor Pages possono essere compresse.
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.MapRazorPages();
Per informazioni sulle applicazioni a pagina singola, vedere le guide per i modelli di progetto React e Angular.
Ordine UseCors e UseStaticFiles
L'ordine per la chiamata di UseCors
e UseStaticFiles
dipende dall'app. Per altre informazioni, vedere Ordine UseCors e UseStaticFiles
Ordine del middleware delle intestazioni inoltrate
Il middleware delle intestazioni inoltrate deve essere eseguito prima di altro middleware. Questo ordine garantisce che il middleware basato sulle intestazioni inoltrate possa usare i valori di intestazione per l'elaborazione. Per eseguire il middleware delle intestazioni inoltrate dopo il middleware di diagnostica e gestione degli errori, vedere Ordine del middleware delle intestazioni inoltrate.
Creare un ramo nella pipeline del middleware
Le estensioni Map vengono usate come convenzione per la diramazione della pipeline. Map
crea un ramo nella pipeline delle richieste in base alle corrispondenze del percorso della richiesta specificato. Se il percorso della richiesta inizia con il percorso specificato, il ramo viene eseguito.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente.
Richiedi | Risposta |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
Quando si usa Map
, i segmenti di percorso corrispondenti vengono rimossi da HttpRequest.Path
e aggiunti a HttpRequest.PathBase
per ogni richiesta.
Map
supporta l'annidamento, ad esempio:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
può anche trovare la corrispondenza di più segmenti contemporaneamente:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
MapWhen crea un ramo nella pipeline delle richieste in base al risultato del predicato specificato. È possibile usare qualsiasi predicato di tipo Func<HttpContext, bool>
per mappare le richieste a un nuovo ramo della pipeline. Nell'esempio seguente viene usato un predicato per rilevare la presenza di una variabile di stringa di query branch
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente:
Richiedi | Risposta |
---|---|
localhost:1234 |
Hello from non-Map delegate. |
localhost:1234/?branch=main |
Branch used = main |
UseWhen crea anche un ramo nella pipeline delle richieste in base al risultato del predicato specificato. Diversamente da MapWhen
, questo ramo viene ricongiunto alla pipeline principale se non esegue il corto circuito o contiene un middleware terminale:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
app.Run();
void HandleBranchAndRejoin(IApplicationBuilder app)
{
var logger = app.ApplicationServices.GetRequiredService<ILogger<Program>>();
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
Nell'esempio precedente viene scritta una risposta Hello from non-Map delegate.
per tutte le richieste. Se la richiesta include una variabile di stringa di query branch
, il relativo valore viene registrato prima del ricongiungimento con la pipeline principale.
Middleware incorporato
ASP.NET Core include i componenti middleware seguenti. Nella colonna Ordinamento sono disponibili note sul posizionamento del middleware nella pipeline di elaborazione delle richieste e indicazioni sulle condizioni nelle quali il middleware può terminare l'elaborazione delle richieste. Quando un middleware esegue un corto circuito della pipeline di elaborazione delle richieste e impedisce al middleware a valle di elaborare una richiesta, viene denominato middleware terminale. Per altre informazioni sul corto circuito, vedere la sezione Creare una pipeline middleware con WebApplication .
Middleware | Descrizione | Ordinamento |
---|---|---|
Autenticazione | Offre il supporto dell'autenticazione. | Prima di HttpContext.User . Terminale per i callback OAuth. |
Autorizzazione | Fornisce il supporto per l'autorizzazione. | Subito dopo il middleware di autenticazione. |
Criteri per i Cookie | Registra il consenso degli utenti per l'archiviazione delle informazioni personali e applica gli standard minimi per i campi dei cookie, come secure e SameSite . |
Prima del middleware che emette i cookie. Esempi: Autenticazione, Sessione, MVC (TempData). |
CORS | Configura la condivisione di risorse tra le origini (CORS). | Prima dei componenti che usano CORS. UseCors attualmente deve precedere UseResponseCaching a causa di questo bug. |
DeveloperExceptionPage | Genera una pagina con informazioni sugli errori destinate all'uso solo nell'ambiente di sviluppo. | Prima dei componenti che generano errori. I modelli di progetto registrano automaticamente questo middleware come primo middleware nella pipeline quando l'ambiente è di sviluppo. |
Diagnostica | Diversi middleware separati che forniscono una pagina delle eccezioni per sviluppatori, la gestione delle eccezioni, pagine di codici di stato e la pagina Web predefinita per le nuove app. | Prima dei componenti che generano errori. Terminale per le eccezioni o per la gestione della pagina Web predefinita per le nuove app. |
Intestazioni inoltrate | Inoltra le intestazioni proxy nella richiesta corrente. | Prima dei componenti che usano i campi aggiornati. Esempi: schema, host, IP client, metodo. |
Controllo integrità | Controlla l'integrità di un'app ASP.NET Core e le relative dipendenze, come il controllo della disponibilità del database. | Terminale se una richiesta corrisponde a un endpoint di controllo di integrità. |
Propagazione delle intestazioni | Propaga le intestazioni HTTP dalla richiesta in ingresso alle richieste client HTTP in uscita. | |
Registrazione HTTP | Registra le richieste e risposte HTTP. | All'inizio della pipeline del middleware. |
Override del metodo HTTP | Consente a una richiesta POST in arrivo di eseguire l'override del metodo. | Prima dei componenti che usano il metodo aggiornato. |
Reindirizzamento HTTPS | Reindirizzare tutte le richieste HTTP a HTTPS. | Prima dei componenti che usano l'URL. |
Protocollo HTTP Strict Transport Security (HSTS) | Middleware di ottimizzazione della sicurezza che aggiunge un'intestazione della risposta speciale. | Prima dell'invio delle risposte e dopo i componenti che modificano le richieste. Esempi: intestazioni inoltrate, riscrittura dell'URL. |
MVC | Elabora le richieste con MVC/Razor Pages. | Terminale se una richiesta corrisponde a una route. |
OWIN | Interoperabilità con app, server e middleware basati su OWIN. | Terminale se la richiesta viene elaborata completamente dal middleware OWIN. |
Decompressione richieste | Offre il supporto per la decompressione delle richieste. | Prima dei componenti che leggono il corpo della richiesta. |
Memorizzazione nella cache delle risposte | Offre il supporto per la memorizzazione delle risposte nella cache. | Prima dei componenti che richiedono la memorizzazione nella cache. UseCORS deve precedere UseResponseCaching . |
Compressione delle risposte | Offre il supporto per la compressione delle risposte. | Prima dei componenti che richiedono la compressione. |
Localizzazione della richiesta | Offre il supporto per la localizzazione. | Prima dei componenti sensibili alla localizzazione. Deve comparire dopo il middleware di routing quando si usa RouteDataRequestCultureProvider. |
Endpoint Routing | Definisce e vincola le route di richiesta. | Terminale per le route corrispondenti. |
SPA | Gestisce tutte le richieste da questo punto nella catena del middleware restituendo la pagina predefinita per l'applicazione a pagina singola | In posizione avanzata nella catena, in modo che abbiano la precedenza altri middleware per la gestione di file statici, azioni MVC e così via. |
Sessione | Offre il supporto per la gestione delle sessioni utente. | Prima dei componenti che richiedono la Sessione. |
File statici | Offre il supporto per la gestione di file statici e l'esplorazione directory. | Terminale se una richiesta corrisponde a un file. |
URL Rewrite (Riscrittura URL) | Offre il supporto per la riscrittura degli URL e il reindirizzamento delle richieste. | Prima dei componenti che usano l'URL. |
W3CLogging | Genera i log di accesso al server nel formato di file di log esteso W3C. | All'inizio della pipeline del middleware. |
WebSocket | Abilita il protocollo WebSocket. | Prima dei componenti necessari per accettare le richieste WebSocket. |
Risorse aggiuntive
- Le opzioni di durata e registrazione contengono un esempio completo di middleware con servizi di durata con ambito, temporanei e singleton.
- Scrivere middleware di ASP.NET Core personalizzato
- Testare middleware di ASP.NET Core
- Configurare gRPC-Web in ASP.NET Core
- Eseguire la migrazione di gestori e moduli HTTP a middleware di ASP.NET Core
- Avvio dell'app in ASP.NET Core
- Funzionalità di richiesta in ASP.NET Core
- Attivazione middleware basata su factory in ASP.NET Core
- Attivazione del middleware con un contenitore di terze parti in ASP.NET Core
Il middleware è un software che viene assemblato in una pipeline dell'app per gestire richieste e risposte. Ogni componente:
- Sceglie se passare la richiesta al componente successivo nella pipeline.
- Può eseguire le operazioni prima e dopo il componente successivo nella pipeline.
Per compilare la pipeline delle richieste vengono usati i delegati di richiesta. I delegati di richiesta gestiscono ogni richiesta HTTP.
I delegati di richiesta vengono configurati tramite i metodi di estensione Run, Map e Use. È possibile specificare un singolo delegato di richiesta inline come metodo anonimo (chiamato middleware inline) o definirlo in una classe riutilizzabile. Le classi riutilizzabili o i metodi anonimi inline costituiscono il middleware o sono noti anche come componenti middleware. Ogni componente middleware è responsabile della chiamata del componente seguente nella pipeline o del corto circuito della pipeline. Quando un middleware esegue un corto circuito, viene denominato middleware terminale perché impedisce all'ulteriore middleware di elaborare la richiesta.
In Eseguire la migrazione di moduli HTTP in middleware vengono spiegate le differenze tra le pipeline delle richieste in ASP.NET Core e in ASP.NET 4.x e sono riportati altri esempi di middleware.
Creare una pipeline middleware con IApplicationBuilder
La pipeline delle richieste ASP.NET Core è costituita da una sequenza di delegati di richiesta, chiamati uno dopo l'altro. Il diagramma seguente illustra il concetto. Il thread di esecuzione seguente le frecce nere.
I delegati possono eseguire le operazioni prima del delegato successivo e dopo di esso. I delegati che gestiscono le eccezioni devono essere chiamati nella prima parte della pipeline in modo che possano individuare le eccezioni che si verificano nelle parti successive della pipeline.
L'app di ASP.NET Core più semplice imposta un delegato di richiesta singolo che gestisce tutte le richieste. In questo caso non è inclusa una pipeline di richieste effettiva. Al contrario, viene chiamata una singola funzione anonima in risposta a ogni richiesta HTTP.
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}
È possibile concatenare più delegati di richiesta insieme con Use. Il parametro next
rappresenta il delegato successivo nella pipeline. È possibile eseguire il corto circuito della pipeline non chiamando il parametro next. In genere è possibile eseguire un'azione prima e dopo il delegato successivo, come illustra l'esempio seguente:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}
Quando un delegato non passa una richiesta al delegato successivo, si crea un cosiddetto corto circuito della pipeline delle richieste. Il corto circuito è spesso opportuno poiché evita l'esecuzione di operazioni non necessarie. Ad esempio, il middleware dei file statici può fungere da middleware terminale elaborando una richiesta per un file statico e corto circuito della rest pipeline. Il middleware aggiunto alla pipeline prima del middleware che termina l'ulteriore elaborazione elabora comunque il codice dopo le istruzioni next.Invoke
. Vedere comunque l'avviso seguente sul tentativo di scrivere una risposta che è già stata inviata.
Avviso
Non chiamare next.Invoke
dopo aver inviato la risposta al client. Le modifiche apportate a HttpResponse dopo l'avvio della risposta generano un'eccezione. Ad esempio, l'impostazione delle intestazioni e di un codice di stato generano un'eccezione. Scrivere nel corpo della risposta dopo aver chiamato next
:
- Può causare una violazione del protocollo. Ad esempio, scrivere un contenuto che supera il valore
Content-Length
specificato. - Può danneggiare il formato del corpo. Ad esempio, scrivere un piè di pagina HTML in un file CSS.
HasStarted è un hint utile per indicare se le intestazioni sono state inviate o se è stato scritto contenuto nel corpo.
I delegati Run non ricevono un parametro next
. Il primo delegato Run
è sempre terminale e termina la pipeline. Run
è una convenzione. Alcuni componenti del middleware possono esporre metodi Run[Middleware]
che vengono eseguiti al termine della pipeline:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}
Per visualizzare i commenti del codice tradotti in lingue diverse dall'inglese, segnalarlo in questo problema di discussione su GitHub.
Nell'esempio precedente il delegato Run
scrive "Hello from 2nd delegate."
nella risposta e quindi termina la pipeline. Se un altro delegato Use
o Run
viene aggiunto dopo il delegato Run
, non viene chiamato.
Ordine del middleware
Il diagramma seguente illustra la pipeline di elaborazione completa delle richieste per app ASP.NET Core MVC e Razor Pages. È possibile vedere come, in un'app tipica, vengono ordinati i middleware esistenti e dove vengono aggiunti i middleware personalizzati. Si ha il controllo completo su come riordinare i middleware esistenti o inserire nuovi middleware personalizzati in base alle esigenze per gli specifici scenari.
Il middleware Endpoint nel diagramma precedente esegue la pipeline di filtro per il tipo di app corrispondente, MVC o Razor Pages.
L'ordine in cui vengono aggiunti i componenti middleware nel metodo Startup.Configure
definisce l'ordine in cui i componenti middleware vengono richiamati per le richieste e l'ordine inverso per la risposta. Questo ordinamento è fondamentale per la sicurezza, le prestazioni e la funzionalità.
Il metodo Startup.Configure
seguente aggiunge componenti middleware correlati alla sicurezza nell'ordine consigliato tipico:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// app.UseCookiePolicy();
app.UseRouting();
// app.UseRequestLocalization();
// app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
// app.UseSession();
// app.UseResponseCompression();
// app.UseResponseCaching();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Nel codice precedente:
- Il middleware che non viene aggiunto durante la creazione di una nuova app Web con singoli account utente viene impostato come commento.
- Non tutti i middleware vengono visualizzati in questo ordine esatto, ma molti lo fanno. Ad esempio:
UseCors
,UseAuthentication
eUseAuthorization
devono essere visualizzati nell'ordine visualizzato.UseCors
attualmente deve precedereUseResponseCaching
a causa di questo bug.UseRequestLocalization
deve comparire prima di qualsiasi middleware che potrebbe controllare le impostazioni cultura della richiesta (ad esempio,app.UseMvcWithDefaultRoute()
).
In alcuni scenari, il middleware ha un ordinamento diverso. Ad esempio, l'ordinamento della memorizzazione nella cache e della compressione è specifico dello scenario e sono disponibili più ordinamenti validi. Ad esempio:
app.UseResponseCaching();
app.UseResponseCompression();
Con il codice precedente, si potrebbe risparmiare CPU memorizzando nella cache la risposta compressa, ma ci si potrebbe ritrovare a memorizzare nella cache più rappresentazioni di una risorsa usando algoritmi di compressione diversi, ad esempio Gzip o Brotli.
L'ordinamento seguente combina i file statici per consentire la memorizzazione nella cache di file statici compressi:
app.UseResponseCaching();
app.UseResponseCompression();
app.UseStaticFiles();
Il metodo Startup.Configure
seguente aggiunge componenti del middleware per gli scenari di app comuni:
- Gestione di eccezioni/errori
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware della pagina delle eccezioni per gli sviluppatori (UseDeveloperExceptionPage) segnala gli errori di runtime delle app.
- Il middleware della pagina degli errori del database segnala gli errori di runtime del database.
- Quando l'app viene eseguita nell'ambiente di produzione:
- Il middleware del gestore delle eccezioni (UseExceptionHandler) intercetta le eccezioni generate nei middleware seguenti.
- Il middleware del protocollo HSTS (HTTP Strict Transport Security) (UseHsts) aggiunge l'intestazione
Strict-Transport-Security
.
- Quando l'app viene eseguita nell'ambiente di sviluppo:
- Il middleware di reindirizzamento HTTPS (UseHttpsRedirection) reindirizza le richieste HTTP a HTTPS.
- Il middleware dei file statici (UseStaticFiles) restituisce i file statici e impedisce ulteriori elaborazioni della richiesta.
- Il middleware dei criteri per i Cookie (UseCookiePolicy) rende l'app conforme al Regolamento generale sulla protezione dei dati (GDPR).
- Il middleware di routing (UseRouting) instrada le richieste.
- Il middleware di autenticazione (UseAuthentication) tenta di autenticare l'utente prima che sia autorizzato ad accedere a risorse protette.
- Il middleware di autorizzazione (UseAuthorization) autorizza un utente ad accedere alle risorse protette.
- Il middleware di sessione (UseSession) stabilisce e mantiene aggiornato lo stato sessione. Se l'app usa lo stato sessione, chiamare il middleware di sessione dopo il middleware dei criteri per i Cookie e prima del middleware MVC.
- Middleware di routing degli endpoint (UseEndpoints con MapRazorPages) per aggiungere endpoint Razor Pages alla pipeline di richiesta.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Nell'esempio di codice precedente, ogni metodo di estensione del middleware viene esposto in IApplicationBuilder tramite lo spazio dei nomi Microsoft.AspNetCore.Builder.
UseExceptionHandler è il primo componente middleware aggiunto alla pipeline. Pertanto, il middleware del gestore di eccezioni intercetta le eccezioni che si verificano nelle chiamate successive.
Il middleware dei file statici viene chiamato nella prima parte della pipeline in moda che possa gestire le richieste ed eseguire un corto circuito senza passare attraverso i componenti rimanenti. Il middleware dei file statici non offre controlli di autorizzazione. I file serviti dal middleware per i file statici, inclusi i file in wwwroot, sono disponibili pubblicamente. Per informazioni su un approccio alla protezione dei file statici, vedere File statici in ASP.NET Core.
Se la richiesta non è gestita dal middleware dei file statici, viene passata al middleware di autenticazione (UseAuthentication) che esegue l'autenticazione. L'autenticazione non esegue il corto circuito di richieste non autenticate. Sebbene il middleware di autenticazione esegua l'autenticazione delle richieste, l'autorizzazione (e il rifiuto) si verifica solo dopo che MVC seleziona una pagina Razor o un controller MVC specifico e un'azione.
L'esempio seguente illustra un ordinamento del middleware nel quale le richieste dei file statici vengono gestite dal middleware dei file statici prima del middleware di compressione delle risposte. I file statici non vengono compressi con questo ordine di middleware. Le risposte Razor Pages possono essere compresse.
public void Configure(IApplicationBuilder app)
{
// Static files aren't compressed by Static File Middleware.
app.UseStaticFiles();
app.UseRouting();
app.UseResponseCompression();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Per le applicazioni a pagina singola, il middleware delle applicazioni a pagina singola UseSpaStaticFiles è in genere l'ultimo nella pipeline del middleware. Il middleware delle applicazioni a pagina singola è l'ultimo:
- Per consentire a tutti gli altri middleware di rispondere prima alle richieste corrispondenti.
- Per consentire l'esecuzione di applicazioni a pagina singola con routing lato client per tutte le route non riconosciute dall'app server.
Per altri dettagli sulle applicazioni a pagina singola, vedere le guide per i modelli di progetto React e Angular.
Ordine del middleware delle intestazioni inoltrate
Il middleware delle intestazioni inoltrate deve essere eseguito prima di altro middleware. Questo ordine garantisce che il middleware basato sulle intestazioni inoltrate possa usare i valori di intestazione per l'elaborazione. Per eseguire il middleware delle intestazioni inoltrate dopo il middleware di diagnostica e gestione degli errori, vedere Ordine del middleware delle intestazioni inoltrate.
Creare un ramo nella pipeline del middleware
Le estensioni Map vengono usate come convenzione per la diramazione della pipeline. Map
crea un ramo nella pipeline delle richieste in base alle corrispondenze del percorso della richiesta specificato. Se il percorso della richiesta inizia con il percorso specificato, il ramo viene eseguito.
public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente.
Richiedi | Risposta |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
Quando si usa Map
, i segmenti di percorso corrispondenti vengono rimossi da HttpRequest.Path
e aggiunti a HttpRequest.PathBase
per ogni richiesta.
Map
supporta l'annidamento, ad esempio:
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map("/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
Map
può anche trovare la corrispondenza di più segmenti contemporaneamente:
public class Startup
{
private static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map multiple segments.");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
MapWhen crea un ramo nella pipeline delle richieste in base al risultato del predicato specificato. È possibile usare qualsiasi predicato di tipo Func<HttpContext, bool>
per mappare le richieste a un nuovo ramo della pipeline. Nell'esempio seguente viene usato un predicato per rilevare la presenza di una variabile di stringa di query branch
:
public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}
La tabella seguente visualizza le richieste e le risposte da http://localhost:1234
usando il codice precedente:
Richiedi | Risposta |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=main | Ramo usato = main |
UseWhen crea anche un ramo nella pipeline delle richieste in base al risultato del predicato specificato. Diversamente da MapWhen
, questo ramo viene ricongiunto alla pipeline principale se non esegue il corto circuito o contiene un middleware terminale:
public class Startup
{
private void HandleBranchAndRejoin(IApplicationBuilder app, ILogger<Startup> logger)
{
app.Use(async (context, next) =>
{
var branchVer = context.Request.Query["branch"];
logger.LogInformation("Branch used = {branchVer}", branchVer);
// Do work that doesn't write to the Response.
await next();
// Do other work that doesn't write to the Response.
});
}
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
appBuilder => HandleBranchAndRejoin(appBuilder, logger));
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from main pipeline.");
});
}
}
Nell'esempio precedente viene scritta la risposta "Hello from main pipeline" per tutte le richieste. Se la richiesta include una variabile di stringa di query branch
, il relativo valore viene registrato prima del ricongiungimento con la pipeline principale.
Middleware incorporato
ASP.NET Core include i componenti middleware seguenti. Nella colonna Ordinamento sono disponibili note sul posizionamento del middleware nella pipeline di elaborazione delle richieste e indicazioni sulle condizioni nelle quali il middleware può terminare l'elaborazione delle richieste. Quando un middleware esegue un corto circuito della pipeline di elaborazione delle richieste e impedisce al middleware a valle di elaborare una richiesta, viene denominato middleware terminale. Per altre informazioni sul corto circuito, vedere la sezione Creare una pipeline middleware con IApplicationBuilder.
Middleware | Descrizione | Ordinamento |
---|---|---|
Autenticazione | Offre il supporto dell'autenticazione. | Prima di HttpContext.User . Terminale per i callback OAuth. |
Autorizzazione | Fornisce il supporto per l'autorizzazione. | Subito dopo il middleware di autenticazione. |
Criteri per i Cookie | Registra il consenso degli utenti per l'archiviazione delle informazioni personali e applica gli standard minimi per i campi dei cookie, come secure e SameSite . |
Prima del middleware che emette i cookie. Esempi: Autenticazione, Sessione, MVC (TempData). |
CORS | Configura la condivisione di risorse tra le origini (CORS). | Prima dei componenti che usano CORS. UseCors attualmente deve precedere UseResponseCaching a causa di questo bug. |
Diagnostica | Diversi middleware separati che forniscono una pagina delle eccezioni per sviluppatori, la gestione delle eccezioni, pagine di codici di stato e la pagina Web predefinita per le nuove app. | Prima dei componenti che generano errori. Terminale per le eccezioni o per la gestione della pagina Web predefinita per le nuove app. |
Intestazioni inoltrate | Inoltra le intestazioni proxy nella richiesta corrente. | Prima dei componenti che usano i campi aggiornati. Esempi: schema, host, IP client, metodo. |
Controllo integrità | Controlla l'integrità di un'app ASP.NET Core e le relative dipendenze, come il controllo della disponibilità del database. | Terminale se una richiesta corrisponde a un endpoint di controllo di integrità. |
Propagazione delle intestazioni | Propaga le intestazioni HTTP dalla richiesta in ingresso alle richieste client HTTP in uscita. | |
Override del metodo HTTP | Consente a una richiesta POST in arrivo di eseguire l'override del metodo. | Prima dei componenti che usano il metodo aggiornato. |
Reindirizzamento HTTPS | Reindirizzare tutte le richieste HTTP a HTTPS. | Prima dei componenti che usano l'URL. |
Protocollo HTTP Strict Transport Security (HSTS) | Middleware di ottimizzazione della sicurezza che aggiunge un'intestazione della risposta speciale. | Prima dell'invio delle risposte e dopo i componenti che modificano le richieste. Esempi: intestazioni inoltrate, riscrittura dell'URL. |
MVC | Elabora le richieste con MVC/Razor Pages. | Terminale se una richiesta corrisponde a una route. |
OWIN | Interoperabilità con app, server e middleware basati su OWIN. | Terminale se la richiesta viene elaborata completamente dal middleware OWIN. |
Memorizzazione nella cache delle risposte | Offre il supporto per la memorizzazione delle risposte nella cache. | Prima dei componenti che richiedono la memorizzazione nella cache. UseCORS deve precedere UseResponseCaching . |
Compressione delle risposte | Offre il supporto per la compressione delle risposte. | Prima dei componenti che richiedono la compressione. |
Localizzazione della richiesta | Offre il supporto per la localizzazione. | Prima dei componenti sensibili alla localizzazione. Deve comparire dopo il middleware di routing quando si usa RouteDataRequestCultureProvider. |
Endpoint Routing | Definisce e vincola le route di richiesta. | Terminale per le route corrispondenti. |
SPA | Gestisce tutte le richieste da questo punto nella catena del middleware restituendo la pagina predefinita per l'applicazione a pagina singola | In posizione avanzata nella catena, in modo che abbiano la precedenza altri middleware per la gestione di file statici, azioni MVC e così via. |
Sessione | Offre il supporto per la gestione delle sessioni utente. | Prima dei componenti che richiedono la Sessione. |
File statici | Offre il supporto per la gestione di file statici e l'esplorazione directory. | Terminale se una richiesta corrisponde a un file. |
URL Rewrite (Riscrittura URL) | Offre il supporto per la riscrittura degli URL e il reindirizzamento delle richieste. | Prima dei componenti che usano l'URL. |
WebSocket | Abilita il protocollo WebSocket. | Prima dei componenti necessari per accettare le richieste WebSocket. |
Risorse aggiuntive
- Le opzioni di durata e registrazione contengono un esempio completo di middleware con servizi di durata con ambito, temporanei e singleton.
- Scrivere middleware di ASP.NET Core personalizzato
- Testare middleware di ASP.NET Core
- Eseguire la migrazione di gestori e moduli HTTP a middleware di ASP.NET Core
- Avvio dell'app in ASP.NET Core
- Funzionalità di richiesta in ASP.NET Core
- Attivazione middleware basata su factory in ASP.NET Core
- Attivazione del middleware con un contenitore di terze parti in ASP.NET Core