Novità di ASP.NET Core 9.0
Questo articolo illustra le modifiche più significative in ASP.NET Core 9.0 con collegamenti alla documentazione pertinente.
Ottimizzazione della distribuzione di asset statici
MapStaticAssets
le convenzioni degli endpoint di routing sono una nuova funzionalità che ottimizza la distribuzione di asset statici nelle app ASP.NET Core.
Per informazioni sulla distribuzione di asset statici per Blazor le app, vedere Blazor di base.
Seguendo le procedure consigliate di produzione per gestire gli asset statici, è necessaria una notevole quantità di lavoro e competenze tecniche. Senza ottimizzazioni come compressione, memorizzazione nella cache e impronte digitali:
- Il browser deve effettuare richieste aggiuntive in ogni caricamento della pagina.
- Più byte del necessario vengono trasferiti attraverso la rete.
- A volte le versioni non aggiornate dei file vengono servite ai client.
La creazione di app Web con prestazioni elevate richiede l'ottimizzazione della distribuzione degli asset nel browser. Le possibili ottimizzazioni includono:
- Servire un determinato asset una volta fino a quando il file non cambia o il browser cancella la cache. Impostare l'intestazione ETag .
- Impedire al browser di usare asset obsoleti o non aggiornati dopo l'aggiornamento di un'app. Impostare l'intestazione Ultima modifica .
- Configurare le intestazioni di memorizzazione nella cache appropriate.
- Usare il middleware di memorizzazione nella cache.
- Gestire le versioni compresse degli asset, quando possibile.
- Usare una rete CDN per gestire gli asset più vicini all'utente.
- Ridurre al minimo le dimensioni degli asset serviti al browser. Questa ottimizzazione non include la minificazione.
MapStaticAssets è una nuova funzionalità che ottimizza la distribuzione di asset statici in un'app. È progettato per funzionare con tutti i framework dell'interfaccia utente, tra cui Blazor, Razor Pages e MVC. In genere è un'eliminazione sostitutiva di UseStaticFiles
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
+app.MapStaticAssets();
-app.UseStaticFiles();
app.MapRazorPages();
app.Run();
MapStaticAssets
opera combinando processi di compilazione e di pubblicazione per raccogliere informazioni su tutte le risorse statiche in un'app. Queste informazioni vengono quindi utilizzate dalla libreria di runtime per gestire in modo efficiente questi file nel browser.
MapStaticAssets
può sostituire UseStaticFiles
nella maggior parte delle situazioni, tuttavia, è ottimizzato per gestire gli asset di cui l'app ha conoscenza in fase di compilazione e pubblicazione. Se l'app serve asset da altre posizioni, ad esempio risorse disco o incorporate, UseStaticFiles
deve essere usata.
MapStaticAssets
offre i vantaggi seguenti non trovati con UseStaticFiles
:
- Compressione del tempo di compilazione per tutti gli asset nell'app:
-
gzip
durante lo sviluppo egzip + brotli
durante la pubblicazione. - Tutti gli asset vengono compressi con l'obiettivo di ridurre al minimo le dimensioni degli asset.
-
- Basato sul
ETags
contenuto:Etags
per ogni risorsa è la stringa con codifica Base64 dell'hash SHA-256 del contenuto. In questo modo, il browser scarica nuovamente un file solo se il contenuto è stato modificato.
La tabella seguente mostra le dimensioni originali e compresse dei file e JS CSS nel modello Pages predefinito Razor :
file | Originale | Compresso | % riduzione |
---|---|---|---|
bootstrap.min.css | 163 | 17.5 | 89.26% |
jquery.js | 89.6 | 28 | 68.75% |
bootstrap.min.js | 78,5 | 20 | 74.52% |
Totali | 331.1 | 65.5 | 80.20% |
La tabella seguente illustra le dimensioni originali e compresse usando la libreriaBlazorutente Fluent:
file | Originale | Compresso | % riduzione |
---|---|---|---|
fluente.js | 384 | 73 | 80.99% |
fluent.css | 94 | 11 | 88.30% |
Totali | 478 | 84 | 82.43% |
Per un totale di 478 KB non compressi a 84 KB compressi.
La tabella seguente illustra le dimensioni originali e compresse usando la libreria dei componenti MudBlazorBlazor :
file | Originale | Compresso | Riduzione |
---|---|---|---|
MudBlazor.min.css | 541 | 37.5 | 93.07% |
MudBlazor.min.js | 47.4 | 9.2 | 80.59% |
Totali | 588.4 | 46,7 | 92.07% |
L'ottimizzazione viene eseguita automaticamente quando si usa MapStaticAssets
. Quando una libreria viene aggiunta o aggiornata, ad esempio con un nuovo codice JavaScript o CSS, gli asset vengono ottimizzati come parte della compilazione. L'ottimizzazione è particolarmente utile per gli ambienti mobili che possono avere una larghezza di banda inferiore o connessioni inaffidabili.
Per altre informazioni sulle nuove funzionalità di recapito dei file, vedere le risorse seguenti:
Abilitazione della compressione dinamica nel server rispetto all'uso MapStaticAssets
MapStaticAssets
presenta i vantaggi seguenti rispetto alla compressione dinamica nel server:
- È più semplice perché non esiste una configurazione specifica del server.
- È più efficiente perché gli asset vengono compressi in fase di compilazione.
- Consente allo sviluppatore di dedicare più tempo durante il processo di compilazione per garantire che le risorse siano le dimensioni minime.
Si consideri la tabella seguente che confronta la compressione MudBlazor con la compressione dinamica IIS e MapStaticAssets
:
IIS gzip | MapStaticAssets |
MapStaticAssets riduzione |
---|---|---|
≅ 90 | 37.5 | 59% |
Blazor
In questa sezione vengono descritte le nuove funzionalità per Blazor.
.NET MAUI Blazor Hybrid e modello di soluzione app Web
Un nuovo modello di soluzione semplifica la creazione .NET MAUI di app client native e Blazor Web che condividono la stessa interfaccia utente. Questo modello illustra come creare app client che ottimizzano il riutilizzo del codice e come destinazione Android, iOS, Mac, Windows e Web.
Le funzionalità principali di questo modello includono:
- Possibilità di scegliere una Blazor modalità di rendering interattiva per l'app Web.
- Creazione automatica dei progetti appropriati, inclusi un Blazor Web App (rendering interattivo globale) e un'app .NET MAUIBlazor Hybrid .
- I progetti creati usano una libreria di classi condivisa Razor (RCL) per gestire i componenti dell'interfaccia Razor utente.
- Il codice di esempio è incluso che illustra come usare l'inserimento delle dipendenze per fornire implementazioni di interfaccia diverse per l'app Blazor Hybrid e .Blazor Web App
Per iniziare, installare .NET 9 SDK e installare il .NET MAUI carico di lavoro, che contiene il modello:
dotnet workload install maui
Creare una soluzione dal modello di progetto in una shell dei comandi usando il comando seguente:
dotnet new maui-blazor-web
Il modello è disponibile anche in Visual Studio.
Nota
Attualmente, si verifica un'eccezione se Blazor le modalità di rendering vengono definite a livello di pagina/componente. Per altre informazioni, vedere BlazorWebView richiede un modo per abilitare l'override di ResolveComponentForRenderMode (dotnet/aspnetcore
#51235).
Per altre informazioni, vedere Creare un'app .NET MAUIBlazor Hybrid con un oggetto Blazor Web App.
Rilevare la posizione di rendering, l'interattività e la modalità di rendering assegnata in fase di esecuzione
È stata introdotta una nuova API progettata per semplificare il processo di esecuzione di query degli stati dei componenti in fase di esecuzione. Questa API offre le funzionalità seguenti:
- Determinare il percorso di esecuzione corrente del componente: può essere utile per il debug e l'ottimizzazione delle prestazioni dei componenti.
- Controllare se il componente è in esecuzione in un ambiente interattivo: può essere utile per i componenti con comportamenti diversi in base all'interattività del proprio ambiente.
- Recuperare la modalità di rendering assegnata per il componente: comprendere la modalità di rendering può essere utile per ottimizzare il processo di rendering e migliorare le prestazioni complessive di un componente.
Per altre informazioni, vedere Blazor di rendering core.
Esperienza di riconnessione lato server migliorata:
Sono stati apportati i miglioramenti seguenti all'esperienza di riconnessione lato server predefinita:
Quando l'utente torna a un'app con un circuito disconnesso, la riconnessione viene tentata immediatamente anziché attendere la durata dell'intervallo di riconnessione successivo. In questo modo si migliora l'esperienza utente quando si passa a un'app in una scheda del browser che non è più in stato di sospensione.
Quando un tentativo di riconnessione raggiunge il server, ma il server ha già rilasciato il circuito, viene eseguito automaticamente un aggiornamento della pagina. Ciò impedisce all'utente di dover aggiornare manualmente la pagina se è probabile che si verifichi una riconnessione corretta.
La riconnessione usa una strategia di backoff calcolata. Per impostazione predefinita, i primi tentativi di riconnessione si verificano in rapida successione senza un intervallo di tentativi prima che vengano introdotti ritardi calcolati tra i tentativi. È possibile personalizzare il comportamento dell'intervallo di ripetizione dei tentativi specificando una funzione per calcolare l'intervallo di ripetizione dei tentativi, come illustrato nell'esempio di backoff esponenziale seguente:
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
Lo stile dell'interfaccia utente di riconnessione predefinita è stato modernizzato.
Per altre informazioni, vedere Blazor di baseSignalR.
Serializzazione semplificata dello stato di autenticazione per Blazor Web Apps
Le nuove API semplificano l'aggiunta dell'autenticazione a un oggetto esistente Blazor Web App. Quando si crea un nuovo Blazor Web App con l'autenticazione usando singoli account e si abilita l'interattività basata su WebAssembly, il progetto include un oggetto personalizzato AuthenticationStateProvider nei progetti server e client.
Questi provider passano allo stato di autenticazione dell'utente nel browser. L'autenticazione nel server anziché nel client consente all'app di accedere allo stato di autenticazione durante la pre-esecuzione e prima dell'inizializzazione del runtime WebAssembly .NET.
Le implementazioni personalizzate AuthenticationStateProvider usano il servizio Stato componente persistente (PersistentComponentState) per serializzare lo stato di autenticazione in commenti HTML e leggerlo da WebAssembly per creare una nuova AuthenticationState istanza.
Ciò funziona bene se si è iniziato dal Blazor Web App modello di progetto e si è selezionata l'opzione Account singoli , ma è molto codice da implementare o copiare se si sta provando ad aggiungere l'autenticazione a un progetto esistente. Sono ora disponibili API, che fanno ora parte del Blazor Web App modello di progetto, che possono essere chiamate nei progetti server e client per aggiungere questa funzionalità:
- AddAuthenticationStateSerialization: aggiunge i servizi necessari per serializzare lo stato di autenticazione nel server.
- AddAuthenticationStateDeserialization: aggiunge i servizi necessari per deserializzare lo stato di autenticazione nel browser.
Per impostazione predefinita, l'API serializza solo il nome lato server e le attestazioni del ruolo per l'accesso nel browser. È possibile passare un'opzione a per AddAuthenticationStateSerialization includere tutte le attestazioni.
Per altre informazioni, vedere le sezioni seguenti di Blazor di base:
- Blazor Identity Interfaccia utente (singoli account)
- Gestire lo stato di autenticazione in Blazor Web Apps
Aggiungere pagine di rendering lato server statico (SSR) a un oggetto interattivo a livello globale Blazor Web App
Con il rilascio di .NET 9, è ora più semplice aggiungere pagine SSR statiche alle app che adottano interattività globale.
Questo approccio è utile solo quando l'app ha pagine specifiche che non possono funzionare con il rendering interattivo server o WebAssembly. Ad esempio, adottare questo approccio per le pagine che dipendono dalla lettura/scrittura di cookie HTTP e possono funzionare solo in un ciclo di richiesta/risposta invece del rendering interattivo. Per le pagine che funzionano con il rendering interattivo, non è consigliabile forzarle a usare il rendering statico di SSR, perché è meno efficiente e meno reattivo per l'utente finale.
Contrassegnare qualsiasi Razor pagina del componente con il nuovo [ExcludeFromInteractiveRouting]
attributo assegnato con la @attribute
Razor direttiva :
@attribute [ExcludeFromInteractiveRouting]
L'applicazione dell'attributo fa sì che la navigazione alla pagina esesce dal routing interattivo. Lo spostamento in ingresso è costretto a eseguire un ricaricamento a pagina intera, risolvendo invece la pagina tramite routing interattivo. Il ricaricamento a pagina intera forza il componente radice di primo livello, in genere il App
componente (App.razor
), a eseguire il rerender dal server, consentendo all'app di passare a una diversa modalità di rendering di primo livello.
Il RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting metodo di estensione consente al componente di rilevare se l'attributo [ExcludeFromInteractiveRouting]
viene applicato alla pagina corrente.
App
Nel componente usare il modello nell'esempio seguente:
- Le pagine che non sono annotate con l'attributo predefinito per la
[ExcludeFromInteractiveRouting]
InteractiveServer
modalità di rendering con interattività globale. È possibile sostituireInteractiveServer
conInteractiveWebAssembly
oInteractiveAuto
per specificare una modalità di rendering globale predefinita diversa. - Le pagine annotate con l'attributo
[ExcludeFromInteractiveRouting]
adopt static SSR (PageRenderMode
è assegnatonull
).
<!DOCTYPE html>
<html>
<head>
...
<HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
<Routes @rendermode="@PageRenderMode" />
...
</body>
</html>
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? PageRenderMode
=> HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
Un'alternativa all'uso del metodo di estensione consiste nel RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting leggere manualmente i metadati dell'endpoint usando HttpContext.GetEndpoint()?.Metadata
.
Questa funzionalità è descritta nella documentazione di riferimento nelle Blazor di rendering di ASP.NET Core.
Inserimento del costruttore
Razor i componenti supportano l'inserimento del costruttore.
Nell'esempio seguente la classe parziale (code-behind) inserisce il NavigationManager
servizio usando un costruttore primario:
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Per altre informazioni, vedere ASP.NET Core Blazor dependency injection.
Compressione Websocket per componenti Interactive Server
Per impostazione predefinita, i componenti Interactive Server abilitano la compressione per le connessioni WebSocket e impostano una frame-ancestors
direttiva CSP (Content Security Policy) impostata su 'self'
, che consente solo l'incorporamento dell'app in un'origine <iframe>
da cui viene servita l'app quando è abilitata la compressione o quando viene fornita una configurazione per il contesto WebSocket.
La compressione può essere disabilitata impostando ConfigureWebSocketOptions
su null
, che riduce la vulnerabilità dell'app per l'attacco , ma può comportare una riduzione delle prestazioni:
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Configurare un provider di servizi di frame-ancestors
configurazione più 'none'
rigoroso con il valore (virgolette singole necessarie), che consente la compressione WebSocket, ma impedisce ai browser di incorporare l'app in qualsiasi <iframe>
:
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Per ulteriori informazioni, vedi le seguenti risorse:
- Linee guida per ASP.NET Core BlazorSignalR
- Linee guida per la mitigazione delle minacce per ASP.NET rendering lato server interattivo Core Blazor
Gestire gli eventi di composizione della tastiera in Blazor
La nuova KeyboardEventArgs.IsComposing
proprietà indica se l'evento della tastiera fa parte di una sessione di composizione. Tenere traccia dello stato di composizione degli eventi della tastiera è fondamentale per la gestione dei metodi di input dei caratteri internazionali.
Aggiunta del parametro OverscanCount
a QuickGrid
Il QuickGrid
componente espone ora una OverscanCount
proprietà che specifica il numero di righe aggiuntive di cui viene eseguito il rendering prima e dopo l'area visibile quando la virtualizzazione è abilitata.
Il valore predefinito OverscanCount
è 3. L'esempio seguente aumenta a OverscanCount
4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumber
il componente supporta l'attributo type="range"
Il InputNumber<TValue> componente supporta ora l'attributotype="range"
del modello e la convalida del modulo, in genere sottoposto a rendering come dispositivo di scorrimento o controllo di composizione anziché come casella di testo:
<EditForm Model="Model" OnSubmit="Submit" FormName="EngineForm">
<div>
<label>
Nacelle Count (2-6):
<InputNumber @bind-Value="Model!.NacelleCount" max="6" min="2"
step="1" type="range" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private EngineSpecifications? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() {}
public class EngineSpecifications
{
[Required, Range(minimum: 2, maximum: 6)]
public int NacelleCount { get; set; }
}
}
Nuovi eventi di navigazione avanzati
Attivare i callback JavaScript prima o dopo la navigazione avanzata utilizzando nuovi listener di eventi:
blazor.addEventListener("enhancednavigationstart", {CALLBACK})
blazor.addEventListener("enhancednavigationend", {CALLBACK})
Per altre informazioni, vedere ASP.NET Core Blazor JavaScript con rendering statico lato server (SSR statico).
SignalR
In questa sezione vengono descritte le nuove funzionalità per SignalR.
Supporto dei tipi polimorfici in SignalR Hub
I metodi hub ora possono accettare una classe base anziché la classe derivata per abilitare scenari polimorfici. Il tipo di base deve essere annotato per consentire il polimorfismo.
public class MyHub : Hub
{
public void Method(JsonPerson person)
{
if (person is JsonPersonExtended)
{
}
else if (person is JsonPersonExtended2)
{
}
else
{
}
}
}
[JsonPolymorphic]
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
private class JsonPerson
{
public string Name { get; set; }
public Person Child { get; set; }
public Person Parent { get; set; }
}
private class JsonPersonExtended : JsonPerson
{
public int Age { get; set; }
}
private class JsonPersonExtended2 : JsonPerson
{
public string Location { get; set; }
}
Attività migliorate per SignalR
SignalR ora dispone di ActivitySource sia per il server hub che per il client.
ActivitySource del server .NET SignalR
Il SignalR ActivitySource denominato Microsoft.AspNetCore.SignalR.Server
genera eventi per le chiamate al metodo hub:
- Ogni metodo è una propria attività, quindi qualsiasi elemento che genera un'attività durante la chiamata al metodo hub si trova nell'attività del metodo hub.
- Le attività del metodo hub non hanno un elemento padre. Ciò significa che non sono raggruppati nella connessione a esecuzione SignalR prolungata.
L'esempio seguente usa il .NET Aspire dashboard e i pacchetti OpenTelemetry :
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
Aggiungere il codice di avvio seguente al Program.cs
file:
using OpenTelemetry.Trace;
using SignalRChat.Hubs;
// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
if (builder.Environment.IsDevelopment())
{
// View all traces only in development environment.
tracing.SetSampler(new AlwaysOnSampler());
}
tracing.AddAspNetCoreInstrumentation();
tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
});
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
var app = builder.Build();
Di seguito è riportato l'output di esempio del dashboard aspirare:
ActivitySource client di .NET SignalR
Il SignalR ActivitySource denominato Microsoft.AspNetCore.SignalR.Client
genera eventi per un client SignalR:
- Il client di SignalR .NET ha un
ActivitySource
denominatoMicrosoft.AspNetCore.SignalR.Client
. Le chiamate all'hub ora creano un intervallo client. Si noti che altri client SignalR, ad esempio il client JavaScript, non supportano il tracciamento. Questa funzionalità verrà aggiunta a più client nelle versioni future. - Le chiamate hub nel client e nel server supportano la propagazione del contesto . La propagazione del contesto di traccia abilita la traccia distribuita vera. È ora possibile visualizzare il flusso delle chiamate dal client al server e di nuovo.
Ecco come vengono esaminate queste nuove attività nel dashboard .NET Aspire:
SignalR supporta il taglio e native AOT
Continuando il percorso AOT nativo avviato in .NET 8, è stato abilitato il taglio e il supporto nativo per la compilazione in anticipo (AOT) per SignalR scenari client e server. È ora possibile sfruttare i vantaggi in termini di prestazioni dell'uso di AOT nativo nelle applicazioni che usano SignalR per le comunicazioni Web in tempo reale.
Introduzione
Installare la versione più recente di .NET 9 SDK.
Creare una soluzione dal webapiaot
modello in una shell dei comandi usando il comando seguente:
dotnet new webapiaot -o SignalRChatAOTExample
Sostituire il contenuto del Program.cs
file con il codice seguente SignalR :
using Microsoft.AspNetCore.SignalR;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddSignalR();
builder.Services.Configure<JsonHubProtocolOptions>(o =>
{
o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.MapGet("/", () => Results.Content("""
<!DOCTYPE html>
<html>
<head>
<title>SignalR Chat</title>
</head>
<body>
<input id="userInput" placeholder="Enter your name" />
<input id="messageInput" placeholder="Type a message" />
<button onclick="sendMessage()">Send</button>
<ul id="messages"></ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messages").appendChild(li);
});
async function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
await connection.invoke("SendMessage", user, message);
}
connection.start().catch(err => console.error(err));
</script>
</body>
</html>
""", "text/html"));
app.Run();
[JsonSerializable(typeof(string))]
internal partial class AppJsonSerializerContext : JsonSerializerContext { }
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
L'esempio precedente produce un eseguibile windows nativo di 10 MB e un eseguibile Linux di 10,9 MB.
Limiti
- Attualmente è supportato solo il protocollo JSON:
- Come illustrato nel codice precedente, le app che usano la serializzazione JSON e Native AOT devono usare il
System.Text.Json
generatore di origine. - Questo approccio segue lo stesso approccio delle API minime.
- Come illustrato nel codice precedente, le app che usano la serializzazione JSON e Native AOT devono usare il
- Nel server i parametri del SignalR metodo Hub di tipo
IAsyncEnumerable<T>
eChannelReader<T>
doveT
è un ValueType (struct
) non sono supportati. L'uso di questi tipi comporta un'eccezione di runtime all'avvio in fase di sviluppo e nell'app pubblicata. Per altre informazioni, vedere SignalR: Uso di IAsyncEnumerable<T> e ChannelReader<T> con ValueTypes in AOT nativo (dotnet/aspnetcore
#56179). -
Gli hub fortemente tipizzato non sono supportati con AOT nativo (
PublishAot
). L'uso di hub fortemente tipizzato con AOT nativo genererà avvisi durante la compilazione e la pubblicazione e un'eccezione di runtime. L'uso di hub fortemente tipizzato con taglio (PublishedTrimmed
) è supportato. - Solo
Task
,Task<T>
,ValueTask
oValueTask<T>
sono supportati per i tipi restituiti asincroni.
API minime
Questa sezione descrive le nuove funzionalità per le API minime.
Aggiunta di InternalServerError
e InternalServerError<TValue>
a TypedResults
La TypedResults classe è un veicolo utile per restituire risposte basate su codice di stato HTTP fortemente tipizzato da un'API minima.
TypedResults
include ora i metodi e i tipi factory per restituire le risposte "500 Internal Server Error" dagli endpoint. Ecco un esempio che restituisce una risposta 500:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Chiamare ProducesProblem
e ProducesValidationProblem
sui gruppi di route
I ProducesProblem
metodi di estensione e ProducesValidationProblem
sono stati aggiornati per supportare l'uso nei gruppi di route. Questi metodi indicano che tutti gli endpoint in un gruppo di route possono restituire ProblemDetails
o ValidationProblemDetails
risposte ai fini dei metadati OpenAPI.
var app = WebApplication.Create();
var todos = app.MapGroup("/todos")
.ProducesProblem();
todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));
app.Run();
record Todo(int Id, string Title, boolean IsCompleted);
Problem
e ValidationProblem
i tipi di risultati supportano la costruzione con IEnumerable<KeyValuePair<string, object?>>
valori
Prima di .NET 9, la creazione di tipi di risultati Problem e ValidationProblem in API minime richiedeva l'inizializzazione delle errors
proprietà e extensions
con un'implementazione di IDictionary<string, object?>
. In questa versione, queste API di costruzione supportano overload che usano IEnumerable<KeyValuePair<string, object?>>
.
var app = WebApplication.Create();
app.MapGet("/", () =>
{
var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
return TypedResults.Problem("This is an error with extensions",
extensions: extensions);
});
Grazie all'utente di GitHub joegoldman2 per questo contributo.
OpenAPI
Questa sezione descrive le nuove funzionalità per OpenAPI
Supporto predefinito per la generazione di documenti OpenAPI
La specifica OpenAPI è uno standard per descrivere le API HTTP. Lo standard consente agli sviluppatori di definire la forma delle API che possono essere collegate a generatori client, generatori di server, strumenti di test, documentazione e altro ancora. In .NET 9, ASP.NET Core offre il supporto predefinito per la generazione di documenti OpenAPI che rappresentano API minime o basate su controller tramite il pacchetto Microsoft.AspNetCore.OpenApi .
Le chiamate di codice evidenziate seguenti:
-
AddOpenApi
per registrare le dipendenze necessarie nel contenitore di inserimento delle dipendenze dell'app. -
MapOpenApi
per registrare gli endpoint OpenAPI necessari nelle route dell'app.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);
app.Run();
Installare il Microsoft.AspNetCore.OpenApi
pacchetto nel progetto usando il comando seguente:
dotnet add package Microsoft.AspNetCore.OpenApi
Eseguire l'app e passare a per openapi/v1.json
visualizzare il documento OpenAPI generato:
I documenti OpenAPI possono anche essere generati in fase di compilazione aggiungendo il Microsoft.Extensions.ApiDescription.Server
pacchetto:
dotnet add package Microsoft.Extensions.ApiDescription.Server
Per modificare il percorso dei documenti OpenAPI generati, impostare il percorso di destinazione nella proprietà OpenApiDocumentsDirectory nel file di progetto dell'app:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>
Eseguire dotnet build
ed esaminare il file JSON generato nella directory del progetto.
ASP.NET generazione di documenti OpenAPI predefinita di Core offre supporto per varie personalizzazioni e opzioni. Fornisce trasformatori di documenti, operazioni e schemi e ha la possibilità di gestire più documenti OpenAPI per la stessa applicazione.
Per altre informazioni sulle nuove funzionalità del documento OpenAPI di ASP.NET Core, vedere la nuova documentazione di Microsoft.AspNetCore.OpenApi.
Microsoft.AspNetCore.OpenApi supporta il taglio e L'AOT nativo
OpenAPI in ASP.NET Core supporta il taglio e l'AOT nativo. La procedura seguente consente di creare e pubblicare un'app OpenAPI con taglio e AOT nativo:
Creare un nuovo progetto api Web di base (AOT) di ASP.NET.
dotnet new webapiaot
Aggiungere il pacchetto Microsoft.AspNetCore.OpenAPI.
dotnet add package Microsoft.AspNetCore.OpenApi
Aggiornare Program.cs
per abilitare la generazione di documenti OpenAPI.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Pubblica l'app.
dotnet publish
Autenticazione e autorizzazione
In questa sezione vengono descritte le nuove funzionalità per l'autenticazione e l'autorizzazione.
OpenIdConnectHandler aggiunge il supporto per le richieste di autorizzazione push (PAR)
Vorremmo ringraziare Joe DeCock da Duende Software per l'aggiunta di richieste di autorizzazione push (PAR) a ASP.NET Core OpenIdConnectHandler. Joe ha descritto il background e la motivazione per l'abilitazione di PAR nella sua proposta di API come indicato di seguito:
Le richieste di autorizzazione push (PAR) sono uno standard OAuth relativamente nuovo che migliora la sicurezza dei flussi OAuth e OIDC spostando i parametri di autorizzazione dal canale anteriore al canale back. Ovvero, spostare i parametri di autorizzazione dagli URL di reindirizzamento nel browser per indirizzare il computer alle chiamate HTTP del computer sul back-end.
Ciò impedisce a un cyberattacker nel browser di:
- Visualizzazione dei parametri di autorizzazione, che potrebbero causare la perdita di informazioni personali.
- Manomissione di tali parametri. Ad esempio, il cyberattacker potrebbe modificare l'ambito di accesso richiesto.
Il push dei parametri di autorizzazione mantiene anche gli URL delle richieste brevi. I parametri di autorizzazione possono ottenere molto tempo quando si usano funzionalità OAuth e OIDC più complesse, ad esempio richieste di autorizzazione avanzata. Gli URL che causano lunghi problemi in molti browser e infrastrutture di rete.
L'uso di PAR è incoraggiato dal gruppo di lavoro FAPI all'interno della OpenID Foundation. Ad esempio, il profilo di sicurezza FAPI2.0 richiede l'uso di PAR. Questo profilo di sicurezza viene utilizzato da molti dei gruppi che lavorano su open banking (principalmente in Europa), nell'assistenza sanitaria e in altri settori con requisiti di sicurezza elevati.
PAR è supportato da diversi identity provider, tra cui
Per .NET 9, è stato deciso di abilitare PAR per impostazione predefinita se il identity documento di individuazione del provider annuncia il supporto per PAR, perché dovrebbe fornire una sicurezza avanzata per i provider che lo supportano. Il identity documento di individuazione del provider si trova in genere in .well-known/openid-configuration
. Se ciò causa problemi, è possibile disabilitare PAR tramite OpenIdConnectOptions.PushedAuthorizationBehavior come indicato di seguito:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("oidc", oidcOptions =>
{
// Other provider-specific configuration goes here.
// The default value is PushedAuthorizationBehavior.UseIfAvailable.
// 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
// and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
// of type 'OpenIdConnectOptions' could be found
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
});
Per assicurarsi che l'autenticazione abbia esito positivo solo se si usa PAR, usare invece PushedAuthorizationBehavior.Require . Questa modifica introduce anche un nuovo evento OnPushAuthorization in OpenIdConnectEvents che può essere usato per personalizzare la richiesta di autorizzazione push o gestirla manualmente. Per altri dettagli, vedere la proposta dell'API.
Personalizzazione dei parametri OIDC e OAuth
I gestori di autenticazione OAuth e OIDC ora hanno un'opzione AdditionalAuthorizationParameters
per semplificare la personalizzazione dei parametri dei messaggi di autorizzazione che vengono in genere inclusi come parte della stringa di query di reindirizzamento. In .NET 8 e versioni precedenti, è necessario un callback personalizzato OnRedirectToIdentityProvider o un metodo sottoposto BuildChallengeUrl a override in un gestore personalizzato. Ecco un esempio di codice .NET 8:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("prompt", "login");
context.ProtocolMessage.SetParameter("audience", "https://api.example.com");
return Task.CompletedTask;
};
});
L'esempio precedente può ora essere semplificato con il codice seguente:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Configurare HTTP.sys flag di autenticazione estesa
È ora possibile configurare i HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
flag e HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
HTTP.sys usando le nuove EnableKerberosCredentialCaching
proprietà e CaptureCredentials
nel HTTP.sys per ottimizzare la modalità di gestione delle autenticazione di WindowsAuthenticationManager. Ad esempio:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Varie
Le sezioni seguenti descrivono varie nuove funzionalità.
Nuova HybridCache
libreria
Importante
HybridCache
è attualmente ancora in anteprima, ma verrà completamente rilasciato dopo .NET 9.0 in una versione secondaria futura delle estensioni .NET.
L'API HybridCache
consente di colmare alcune lacune nelle API e IDistributedCache esistentiIMemoryCache. Aggiunge anche nuove funzionalità, ad esempio:
- Protezione "stampede" per impedire il recupero parallelo dello stesso lavoro.
- Serializzazione configurabile.
HybridCache
è progettato per essere una sostituzione dell'eliminazione per l'uso e IDistributedCache
esistente IMemoryCache
e offre una semplice API per l'aggiunta di nuovo codice di memorizzazione nella cache. Fornisce un'API unificata per la memorizzazione nella cache in-process e out-of-process.
Per vedere come l'API HybridCache
è semplificata, confrontarla con il codice che usa IDistributedCache
. Di seguito è riportato un esempio dell'aspetto dell'uso IDistributedCache
:
public class SomeService(IDistributedCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
var bytes = await cache.GetAsync(key, token); // Try to get from cache.
SomeInformation info;
if (bytes is null)
{
// Cache miss; get the data from the real source.
info = await SomeExpensiveOperationAsync(name, id, token);
// Serialize and cache it.
bytes = SomeSerializer.Serialize(info);
await cache.SetAsync(key, bytes, token);
}
else
{
// Cache hit; deserialize it.
info = SomeSerializer.Deserialize<SomeInformation>(bytes);
}
return info;
}
// This is the work we're trying to cache.
private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
CancellationToken token = default)
{ /* ... */ }
}
Questo è un sacco di lavoro per ottenere il giusto ogni volta, inclusi elementi come la serializzazione. Inoltre, nello scenario di mancata memorizzazione nella cache, è possibile terminare con più thread simultanei, ottenere tutti un mancato riscontro nella cache, recuperare tutti i dati sottostanti, serializzarli e tutti gli invii tali dati alla cache.
Per semplificare e migliorare questo codice con HybridCache
, è prima necessario aggiungere la nuova libreria Microsoft.Extensions.Caching.Hybrid
:
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Registrare il HybridCache
servizio, come si vuole registrare un'implementazione IDistributedCache
:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
È ora possibile eseguire l'offload della maggior parte dei problemi di memorizzazione nella cache in HybridCache
:
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync
(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // Unique key for this combination.
async cancel => await SomeExpensiveOperationAsync(name, id, cancel),
token: token
);
}
}
Viene fornito un'implementazione concreta della HybridCache
classe astratta tramite l'inserimento delle dipendenze, ma è previsto che gli sviluppatori possano fornire implementazioni personalizzate dell'API. L'implementazione HybridCache
gestisce tutti gli elementi correlati alla memorizzazione nella cache, inclusa la gestione simultanea delle operazioni. Il cancel
token qui rappresenta l'annullamento combinato di tutti i chiamanti simultanei, non solo l'annullamento del chiamante che è possibile vedere ( ovvero token
).
Gli scenari con velocità effettiva elevata possono essere ulteriormente ottimizzati usando il TState
modello, per evitare un sovraccarico dovuto alle variabili acquisite e ai callback per istanza:
public class SomeService(HybridCache cache)
{
public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
{
return await cache.GetOrCreateAsync(
$"someinfo:{name}:{id}", // unique key for this combination
(name, id), // all of the state we need for the final call, if needed
static async (state, token) =>
await SomeExpensiveOperationAsync(state.name, state.id, token),
token: token
);
}
}
HybridCache
usa l'implementazione configurata IDistributedCache
, se presente, per la memorizzazione nella cache out-of-process secondaria, ad esempio usando Redis. Ma anche senza , IDistributedCache
il HybridCache
servizio fornirà comunque la protezione nella cache in-process e "stampede".
Nota sul riutilizzo degli oggetti
Nel codice esistente tipico che usa IDistributedCache
, ogni recupero di un oggetto dalla cache comporta la deserializzazione. Questo comportamento significa che ogni chiamante simultaneo ottiene un'istanza separata dell'oggetto, che non può interagire con altre istanze. Il risultato è thread safety, poiché non esiste alcun rischio di modifiche simultanee alla stessa istanza dell'oggetto.
Poiché un sacco di HybridCache
utilizzo verrà adattato dal codice esistente IDistributedCache
, HybridCache
mantiene questo comportamento per impostazione predefinita per evitare di introdurre bug di concorrenza. Tuttavia, un determinato caso d'uso è intrinsecamente thread-safe:
- Se i tipi memorizzati nella cache non sono modificabili.
- Se il codice non li modifica.
In questi casi, informare HybridCache
che è sicuro riutilizzare le istanze in base a:
- Contrassegnare il tipo come
sealed
. Lasealed
parola chiave in C# indica che la classe non può essere ereditata. - Applicazione dell'attributo
[ImmutableObject(true)]
. L'attributo[ImmutableObject(true)]
indica che lo stato dell'oggetto non può essere modificato dopo la creazione.
Riutilizzando istanze, HybridCache
è possibile ridurre il sovraccarico delle allocazioni di CPU e oggetti associate alla deserializzazione per chiamata. Ciò può comportare miglioramenti delle prestazioni negli scenari in cui gli oggetti memorizzati nella cache sono di grandi dimensioni o a cui si accede di frequente.
Altre HybridCache
funzionalità
Come IDistributedCache
, HybridCache
supporta la rimozione tramite chiave con un RemoveKeyAsync
metodo .
HybridCache
fornisce anche API facoltative per IDistributedCache
le implementazioni, per evitare byte[]
allocazioni. Questa funzionalità viene implementata dalle versioni di anteprima dei Microsoft.Extensions.Caching.StackExchangeRedis
pacchetti e Microsoft.Extensions.Caching.SqlServer
.
La serializzazione viene configurata come parte della registrazione del servizio, con supporto per serializzatori specifici del tipo e generalizzati tramite i WithSerializer
metodi e .WithSerializerFactory
concatenati dalla AddHybridCache
chiamata. Per impostazione predefinita, la libreria gestisce string
e byte[]
internamente e usa System.Text.Json
per tutto il resto, ma è possibile usare protobuf, xml o qualsiasi altro elemento.
HybridCache
supporta runtime .NET meno recenti, fino a .NET Framework 4.7.2 e .NET Standard 2.0.
Per altre informazioni su HybridCache
, vedere Libreria HybridCache in ASP.NET Core
Miglioramenti alla pagina delle eccezioni per gli sviluppatori
La pagina delle eccezioni dello sviluppatore ASP.NET Core viene visualizzata quando un'app genera un'eccezione non gestita durante lo sviluppo. La pagina delle eccezioni per sviluppatori fornisce informazioni dettagliate sull'eccezione e sulla richiesta.
Anteprima 3 aggiunta dei metadati dell'endpoint alla pagina delle eccezioni dello sviluppatore. ASP.NET Core usa i metadati dell'endpoint per controllare il comportamento dell'endpoint, ad esempio routing, memorizzazione nella cache delle risposte, limitazione della frequenza, generazione OpenAPI e altro ancora. L'immagine seguente mostra le nuove informazioni sui metadati nella Routing
sezione della pagina delle eccezioni per sviluppatori:
Durante il test della pagina delle eccezioni dello sviluppatore, sono stati identificati piccoli miglioramenti della qualità della vita. Sono stati spediti in Anteprima 4:
- Migliore disposizione del testo. I cookie lunghi, i valori delle stringhe di query e i nomi dei metodi non aggiungono più barre di scorrimento orizzontale del browser.
- Testo più grande che si trova nei disegni moderni.
- Dimensioni di tabella più coerenti.
L'immagine animata seguente mostra la nuova pagina delle eccezioni per sviluppatori:
Miglioramenti del debug del dizionario
La visualizzazione del debug di dizionari e altre raccolte chiave-valore ha un layout migliorato. La chiave viene visualizzata nella colonna chiave del debugger invece di essere concatenata con il valore . Le immagini seguenti mostrano la visualizzazione precedente e nuova di un dizionario nel debugger.
Prima:
Dopo:
ASP.NET Core include molte raccolte chiave-valore. Questa esperienza di debug migliorata si applica a:
- Intestazioni HTTP
- Stringhe di query
- Form
- Cookie
- Visualizzare i dati
- Dati route
- Funzionalità
Correzione di 503 durante il riciclo dell'app in IIS
Per impostazione predefinita, è ora presente un ritardo di 1 secondo tra quando IIS riceve una notifica di riciclo o arresto e quando ANCM indica al server gestito di avviare l'arresto. Il ritardo è configurabile tramite la ANCM_shutdownDelay
variabile di ambiente o impostando l'impostazione del shutdownDelay
gestore. Entrambi i valori sono in millisecondi. Il ritardo consiste principalmente nel ridurre la probabilità di una gara in cui:
- IIS non ha avviato l'accodamento delle richieste per passare alla nuova app.
- ANCM inizia a rifiutare nuove richieste che vengono inserite nell'app precedente.
I computer o i computer più lenti con un utilizzo più elevato della CPU possono voler regolare questo valore per ridurre la probabilità di 503.
Esempio di impostazione shutdownDelay
:
<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
<handlerSettings>
<!-- Milliseconds to delay shutdown by.
this doesn't mean incoming requests will be delayed by this amount,
but the old app instance will start shutting down after this timeout occurs -->
<handlerSetting name="shutdownDelay" value="5000" />
</handlerSettings>
</aspNetCore>
La correzione si trova nel modulo ANCM installato a livello globale proveniente dal bundle di hosting.
ASP0026: Analizzatore da avvisare quando [Authorize] viene sottoposto a override da [AllowAnonymous] da "lontano"
Sembra intuitivo che un [Authorize]
attributo posizionato "più vicino" a un'azione MVC rispetto a un [AllowAnonymous]
attributo eseguirà l'override dell'attributo e forza l'autorizzazione [AllowAnonymous]
. Tuttavia, questo non è necessariamente il caso. Ciò che conta è l'ordine relativo degli attributi.
Il codice seguente mostra esempi in cui un attributo più vicino [Authorize]
viene sottoposto a override da un [AllowAnonymous]
attributo più lontano.
[AllowAnonymous]
public class MyController
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on the class
public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}
public class MyControllerInherited2 : MyControllerAnon
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}
In .NET 9 Preview 6 è stato introdotto un analizzatore che evidenzia istanze come queste in cui un attributo più vicino [Authorize]
viene sottoposto a override da un [AllowAnonymous]
attributo che è più lontano da un'azione MVC. L'avviso punta all'attributo sottoposto [Authorize]
a override con il messaggio seguente:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
L'azione corretta da eseguire se viene visualizzato questo avviso dipende dall'intenzione alla base degli attributi. L'attributo più lontano [AllowAnonymous]
deve essere rimosso se non espone involontariamente l'endpoint agli utenti anonimi. Se l'attributo è stato progettato per eseguire l'override [AllowAnonymous]
di un attributo più vicino [Authorize]
, è possibile ripetere l'attributo [AllowAnonymous]
dopo l'attributo [Authorize]
per chiarire la finalità.
[AllowAnonymous]
public class MyController
{
// This produces no warning because the second, "closer" [AllowAnonymous]
// clarifies that [Authorize] is intentionally overridden.
// Specifying AuthenticationSchemes can still be useful
// for endpoints that allow but don't require authenticated users.
[Authorize(AuthenticationSchemes = "Cookies")]
[AllowAnonymous]
public IActionResult Privacy() => null;
}
Metriche di connessione migliorate Kestrel
Sono stati apportati miglioramenti significativi alle metriche di connessione includendo i metadati relativi al Kestrelmotivo per cui una connessione non è riuscita. La kestrel.connection.duration
metrica include ora il motivo di chiusura della connessione nell'attributo error.type
.
Di seguito è riportato un piccolo esempio dei error.type
valori:
-
tls_handshake_failed
- La connessione richiede TLS e l'handshake TLS non è riuscito. -
connection_reset
- La connessione è stata chiusa in modo imprevisto dal client mentre le richieste erano in corso. -
request_headers_timeout
- Kestrel ha chiuso la connessione perché non ha ricevuto intestazioni di richiesta nel tempo. -
max_request_body_size_exceeded
- Kestrel ha chiuso la connessione perché i dati caricati hanno superato le dimensioni massime.
In precedenza, la diagnosi dei Kestrel problemi di connessione richiedeva a un server di registrare la registrazione dettagliata e di basso livello. Tuttavia, i log possono essere costosi da generare e archiviare e può essere difficile trovare le informazioni corrette tra il rumore.
Le metriche sono un'alternativa molto più economica che può essere lasciata in un ambiente di produzione con un impatto minimo. Le metriche raccolte possono guidare dashboard e avvisi. Una volta identificato un problema a livello generale con le metriche, è possibile iniziare ulteriori indagini usando la registrazione e altri strumenti.
Si prevede che le metriche di connessione migliorate siano utili in molti scenari:
- Analisi dei problemi di prestazioni causati da brevi durate delle connessioni.
- Osservare gli attacchi esterni in corso su che influiscono sulle Kestrel prestazioni e sulla stabilità.
- La registrazione di tentativi di attacchi esterni sulla KestrelKestrelprotezione avanzata predefinita ha impedito la protezione avanzata.
Per altre informazioni, vedere ASP.NET Metriche principali.
Personalizzare Kestrel gli endpoint named pipe
KestrelIl supporto della named pipe è stato migliorato con opzioni di personalizzazione avanzate. Il nuovo CreateNamedPipeServerStream
metodo nelle opzioni named pipe consente di personalizzare le pipe per endpoint.
Un esempio di dove questo è utile è un'app Kestrel che richiede due endpoint di pipe con sicurezza di accesso diversa. L'opzione CreateNamedPipeServerStream
può essere usata per creare pipe con impostazioni di sicurezza personalizzate, a seconda del nome della pipe.
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenNamedPipe("pipe1");
options.ListenNamedPipe("pipe2");
});
builder.WebHost.UseNamedPipes(options =>
{
options.CreateNamedPipeServerStream = (context) =>
{
var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);
return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
};
});
ExceptionHandlerMiddleware
opzione per scegliere il codice di stato in base al tipo di eccezione
Una nuova opzione durante la configurazione di consente agli sviluppatori di app di scegliere il codice di stato da restituire quando si verifica un'eccezione durante la ExceptionHandlerMiddleware
gestione delle richieste. La nuova opzione modifica il codice di stato impostato nella ProblemDetails
risposta da ExceptionHandlerMiddleware
.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Rifiutare esplicitamente le metriche HTTP in determinati endpoint e richieste
.NET 9 introduce la possibilità di rifiutare esplicitamente le metriche HTTP per endpoint e richieste specifici. Rifiutare esplicitamente le metriche di registrazione è utile per gli endpoint spesso chiamati da sistemi automatizzati, ad esempio i controlli di integrità. La registrazione delle metriche per queste richieste è in genere non necessaria.
Le richieste HTTP a un endpoint possono essere escluse dalle metriche aggiungendo metadati. Uno dei seguenti:
- Aggiungere l'attributo
[DisableHttpMetrics]
al controller api Web, SignalR all'hub o al servizio gRPC. - Chiamare DisableHttpMetrics quando si esegue il mapping degli endpoint all'avvio dell'app:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
La MetricsDisabled
proprietà è stata aggiunta a IHttpMetricsTagsFeature
per:
- Scenari avanzati in cui una richiesta non esegue il mapping a un endpoint.
- Disabilitazione dinamica della raccolta di metriche per richieste HTTP specifiche.
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null &&
context.Request.Headers.ContainsKey("x-disable-metrics"))
{
metricsFeature.MetricsDisabled = true;
}
await next(context);
});
Supporto per la protezione dei dati per l'eliminazione di chiavi
Prima di .NET 9, le chiavi di protezione dei dati non erano eliminabili per impostazione predefinita, per evitare la perdita di dati. L'eliminazione di una chiave rende irretrievabili i dati protetti. Data la loro piccola dimensione, l'accumulo di queste chiavi ha in genere rappresentato un impatto minimo. Tuttavia, per supportare servizi estremamente a esecuzione prolungata, è stata introdotta l'opzione per eliminare le chiavi. In genere, è consigliabile eliminare solo le chiavi precedenti. Eliminare chiavi solo quando è possibile accettare il rischio di perdita di dati in cambio di risparmi di archiviazione. È consigliabile non eliminare le chiavi di protezione dei dati.
using Microsoft.AspNetCore.DataProtection.KeyManagement;
var services = new ServiceCollection();
services.AddDataProtection();
var serviceProvider = services.BuildServiceProvider();
var keyManager = serviceProvider.GetService<IKeyManager>();
if (keyManager is IDeletableKeyManager deletableKeyManager)
{
var utcNow = DateTimeOffset.UtcNow;
var yearAgo = utcNow.AddYears(-1);
if (!deletableKeyManager.DeleteKeys(key => key.ExpirationDate < yearAgo))
{
Console.WriteLine("Failed to delete keys.");
}
else
{
Console.WriteLine("Old keys deleted successfully.");
}
}
else
{
Console.WriteLine("Key manager does not support deletion.");
}
Il middleware supporta l'inserimento delle dipendenze con chiave
Il middleware supporta ora l'inserimento delle dipendenze con chiave sia nel costruttore che nel Invoke
/InvokeAsync
metodo :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");
var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();
internal class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next,
[FromKeyedServices("test")] MySingletonClass service)
{
_next = next;
}
public Task Invoke(HttpContext context,
[FromKeyedServices("test2")]
MyScopedClass scopedService) => _next(context);
}
Considerare attendibile il certificato di sviluppo HTTPS di ASP.NET Core in Linux
Nelle distribuzioni Linux basate su Ubuntu e Fedora ora dotnet dev-certs https --trust
configura ASP.NET certificato di sviluppo HTTPS core come certificato attendibile per:
- Browser Chromium, ad esempio Google Chrome, Microsoft Edge e Chromium.
- Mozilla Firefox e i browser derivati da Mozilla.
- API .NET, ad esempio HttpClient
In precedenza, --trust
funzionava solo su Windows e macOS. L'attendibilità del certificato viene applicata per utente.
Per stabilire un trust in OpenSSL, lo dev-certs
strumento:
- Inserisce il certificato in
~/.aspnet/dev-certs/trust
- Esegue una versione semplificata dello strumento di c_rehash openSSL nella directory.
- Chiede all'utente di aggiornare la
SSL_CERT_DIR
variabile di ambiente.
Per stabilire un trust in dotnet, lo strumento inserisce il certificato nell'archivio My/Root
certificati.
Per stabilire l'attendibilità nei database NSS, se presenti, lo strumento cerca nella home directory i profili Firefox, ~/.pki/nssdb
e ~/snap/chromium/current/.pki/nssdb
. Per ogni directory trovata, lo strumento aggiunge una voce a nssdb
.
Modelli aggiornati alle versioni più recenti di Bootstrap, jQuery e convalida jQuery
I modelli di progetto e le librerie core di ASP.NET sono stati aggiornati per usare le versioni più recenti di Bootstrap, jQuery e jQuery Validation, in particolare:
- Bootstrap 5.3.3
- jQuery 3.7.1
- Convalida di jQuery 1.21.0