Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra le modifiche più significative in ASP.NET Core in .NET 9 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 le app Blazor, vedere Blazor.
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. Imposta l'intestazione ETag.
- Impedire al browser di usare asset obsoleti o non aggiornati dopo l'aggiornamento di un'app. Impostare l'intestazione Ultima modifica .
- Configura le intestazioni appropriate per la memorizzazione nella cache.
- 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 è una sostituzione diretta per 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 fornisce asset da altre posizioni, ad esempio risorse su disco o incorporate, UseStaticFiles deve essere utilizzato.
MapStaticAssets offre i vantaggi seguenti non trovati con UseStaticFiles:
- Compressione del tempo di compilazione per tutti gli asset nell'app:
-
gzipdurante lo sviluppo egzip + brotlidurante la pubblicazione. - Tutti gli asset vengono compressi con l'obiettivo di ridurre al minimo le dimensioni degli asset.
-
- Basato sul
ETagscontenuto:Etagsper 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 CSS e JS 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% |
| Totale | 331,1 | 65.5 | 80.20% |
La tabella seguente illustra le dimensioni originali e compresse usando la libreria dei componenti
| file | Originale | Compresso | % riduzione |
|---|---|---|---|
| fluent.js | 384 | 73 | 80.99% |
| fluent.css | 94 | 11 | 88.30% |
| Totale | 478 | 84 | 82.43% |
Per un totale di 478 KB non compressi, ridotti 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% |
| Totale | 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 di app client native e web .NET MAUIBlazor 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 ulteriori informazioni, vedere modalità di rendering di ASP.NET CoreBlazor.
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 era 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.
Il timing della riconnessione utilizza 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 ulteriori informazioni, vedere le linee guida di ASP.NET Core BlazorSignalR.
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 Account individuali e si abilita l'interattività basata su WebAssembly, il progetto include un elemento personalizzato AuthenticationStateProvider sia nel progetto server che nel progetto client.
Questi provider trasmettono lo stato di autenticazione dell'utente al 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 nuovamente da WebAssembly per creare una nuova istanza di AuthenticationState.
Questo funziona bene se si è iniziato dal Blazor Web App modello di progetto e si è selezionata l'opzione Account individuali, ma è molto codice da implementare o da copiare quando si prova 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 AddAuthenticationStateSerialization per includere tutte le richieste.
Per ulteriori informazioni, vedere le seguenti sezioni di ASP.NET CoreBlazor autenticazione e autorizzazione:
- 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 @attributeRazor direttiva :
@attribute [ExcludeFromInteractiveRouting]
L'applicazione dell'attributo fa sì che, navigando alla pagina, si esca dal routing interattivo. La navigazione in entrata è costretta a eseguire un ricaricamento a pagina intera invece di risolvere 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.
Nel componente App, usare il modello nell'esempio seguente:
- Le pagine che non sono annotate con l'attributo
[ExcludeFromInteractiveRouting]utilizzano di default la modalità di renderingInteractiveServercon interattività globale. È possibile sostituireInteractiveServerconInteractiveWebAssemblyoInteractiveAutoper specificare una modalità di rendering globale predefinita diversa. - Le pagine annotate con l'attributo
[ExcludeFromInteractiveRouting]adottano SSR statico (PageRenderModeviene 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 è leggere i metadati dell'endpoint manualmente usando RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting.
Questa funzionalità è descritta nella documentazione di riferimento sui
Iniezione del costruttore
Razor i componenti supportano l'iniezione del costruttore.
Nell'esempio seguente, la classe parziale (code-behind) inserisce il servizio
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 del Server Interattivo
Per impostazione predefinita, i componenti di Interactive Server abilitano la compressione per le connessioni WebSocket e impostano una frame-ancestors direttiva CSP (Content Security Policy) impostata su 'self' (linee guida di riferimento CSP di MDN), che consente solo di incorporare l'app in un'origine <iframe> da cui viene servita l'app quando la compressione è abilitata 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 una CSP più rigida con un valore di frame-ancestors (virgolette singole necessarie), che consente la compressione WebSocket ma impedisce ai browser di incorporare l'app in qualsiasi 'none':
.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 il rendering interattivo lato server di ASP.NET 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.
Aggiunto il 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 il OverscanCount a 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumberil componente supporta l'attributo type="range"
Il componente InputNumber<TValue> supporta ora l'attributo type="range", che consente la creazione di un input con selezione dell'intervallo che supporta l'associazione al modello e la convalida del modulo, in genere visualizzato come cursore o controllo rotativo 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).
Streaming delle richieste lato client
Il rendering WebAssembly interattivo in Blazor supporta ora lo streaming di richieste lato client con l'opzione request.SetBrowserRequestStreamingEnabled(true) su HttpRequestMessage.
Per ulteriori informazioni, vedi le seguenti risorse:
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 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 Aspire dashboard:
Client di .NET ActivitySource SignalR
Il SignalR ActivitySource denominato Microsoft.AspNetCore.SignalR.Client genera eventi per un client SignalR:
- Il client di SignalR .NET ha un
ActivitySourcedenominatoMicrosoft.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 invocazioni dell'hub sul client e sul 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 Aspire:
SignalR supporta la riduzione e Native AOT
Continuando l'avventura di Native AOT iniziata con .NET 8, abbiamo abilitato l'ottimizzazione e il supporto per la compilazione nativa anticipata (AOT) in entrambi gli 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 modello webapiaot in un terminale 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.Jsongeneratore 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 sia in fase di sviluppo che nell'app pubblicata. Per ulteriori informazioni, consultare SignalR: Utilizzo di IAsyncEnumerable<T> e ChannelReader<T> con ValueTypes nell'ambiente AOT nativo (dotnet/aspnetcore#56179). -
Gli hub fortemente tipizzati non sono supportati con Native AOT (
PublishAot). L'uso di hub fortemente tipizzati con Native AOT genererà avvisi durante la compilazione e la pubblicazione, e durante l'esecuzione si verificherà un'eccezione di runtime. L'uso di hub fortemente tipizzato con taglio (PublishedTrimmed) è supportato. - Solo
Task,Task<T>,ValueTaskoValueTask<T>sono supportati per i tipi restituiti asincroni.
API minimali
Questa sezione descrive le nuove funzionalità per le API minime.
Aggiunto 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 nei gruppi di route
I metodi di estensione ProducesProblem e ProducesValidationProblem sono stati aggiornati per supportare il loro utilizzo 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 sviluppo supportano overload che consumano 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 seguenti chiamate di codice evidenziate:
-
AddOpenApiper registrare le dipendenze necessarie nel contenitore DI dell'app. -
MapOpenApiper 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
Avviare l'app e navigare a openapi/v1.json per 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.
La generazione integrata di documenti OpenAPI in ASP.NET 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 l'ottimizzazione e il Native AOT
OpenAPI in ASP.NET Core supporta la riduzione e la compilazione AOT nativa. La procedura seguente consente di creare e pubblicare un'app OpenAPI con riduzione e AOT nativo.
Creare un nuovo progetto API web Core di ASP.NET (nativo AOT).
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 verso chiamate HTTP dirette da macchina a macchina sul back-end.
Ciò impedisce a un attaccante informatico 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 diventare molto lunghi quando si usano funzionalità OAuth e OIDC più complesse, come Richieste di autorizzazione arricchite. Gli URL lunghi causano problemi in molti browser e nelle 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 fornitori di identità, tra cui
Per .NET 9, è stato deciso di abilitare PAR per impostazione predefinita se il documento di individuazione del provider di identità annuncia il supporto per PAR, perché dovrebbe fornire sicurezza avanzata per i provider che lo supportano. Il documento di individuazione del provider di identità 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 garantire che l'autenticazione vada a buon fine solo se si utilizza PAR, utilizzare 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 o versioni precedenti, è necessario un callback personalizzato OnRedirectToIdentityProvider o un metodo sovrascritto BuildChallengeUrl 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 flag HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING e HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL di HTTP.sys usando le nuove proprietà EnableKerberosCredentialCaching e CaptureCredentials del HTTP.sys AuthenticationManager per ottimizzare la modalità di gestione dell'autenticazione di Windows. 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 in una versione secondaria futura delle estensioni .NET.
L'API HybridCache colma alcune lacune nelle API IDistributedCache e IMemoryCache esistenti. Aggiunge anche nuove funzionalità, ad esempio:
- Protezione "stampede" per impedire il recupero parallelo dello stesso lavoro.
- Serializzazione configurabile.
HybridCache è progettato per essere una sostituzione diretta per l'uso esistente di IDistributedCache e IMemoryCache, e offre una semplice API per aggiungere nuovo codice di caching. Fornisce un'API unificata per la memorizzazione nella cache a livello di processo e a livello esterno.
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 farlo bene ogni volta, inclusi elementi come la serializzazione. Inoltre, nello scenario di mancato accesso alla cache, potresti trovarti con più thread simultanei che subiscono tutti un mancato accesso alla cache, recuperano tutti i dati sottostanti, li serializzano e inviano tutti quei 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 un IDistributedCache, il servizio HybridCache fornirà comunque caching in-process e protezione dallo "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 è la sicurezza dei thread, poiché non esiste alcun rischio di modifiche simultanee alla stessa istanza dell'oggetto.
Poiché molto uso di HybridCache verrà adattato dal codice esistente IDistributedCache, HybridCache mantiene questo comportamento per impostazione predefinita per evitare di introdurre bug di concorrenza. Tuttavia, un caso d'uso specifico è 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 apportando entrambe le modifiche seguenti:
- Contrassegnare il tipo come
sealed. Lasealedparola 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 delle API facoltative per le implementazioni di IDistributedCache, per evitare le allocazioni di byte[]. Questa funzionalità viene implementata dalle versioni di anteprima dei Microsoft.Extensions.Caching.StackExchangeRedispacchetti , Microsoft.Extensions.Caching.SqlServere Microsoft.Extensions.Caching.Postgres .
La serializzazione viene configurata come parte della registrazione del servizio, con supporto per serializzatori specifici del tipo e generalizzati tramite i metodi WithSerializer e .WithSerializerFactory, concatenati dalla chiamata AddHybridCache. 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 ha aggiunto i 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 schermata di 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
- Forme
- Cookie
- Visualizza dati
- Dati percorso
- Funzionalità
Correzione di 503 durante il riciclo dell'app in IIS
Per impostazione predefinita, è ora presente un ritardo di 1 secondo tra il momento in cui IIS riceve una segnalazione di riciclo o arresto e quando ANCM comunica al server gestito di iniziare 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 che avvisa quando [Authorize] viene sovrascritto da [AllowAnonymous] in un contesto più distante
Sembra intuitivo che un attributo [Authorize] posizionato "più vicino" a un'azione MVC di quanto lo sia un attributo [AllowAnonymous], annullerà l'attributo [AllowAnonymous] e forzerà l'autorizzazione. Tuttavia, questo non è necessariamente il caso. Ciò che conta è l'ordine relativo degli attributi.
Nota
L'attributo [AllowAnonymous] non disabilita completamente l'autenticazione. Quando le credenziali vengono inviate a un endpoint con [AllowAnonymous], l'endpoint autentica ancora tali credenziali e stabilisce l'identità dell'utente. L'attributo significa solo che l'autenticazione [AllowAnonymous]. L'endpoint verrà eseguito come anonimo solo quando non vengono fornite credenziali. Questo comportamento può essere utile per gli endpoint che devono funzionare sia per gli utenti autenticati che per gli utenti anonimi.
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 [Authorize] sovrascritto con il seguente messaggio:
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 di distanza maggiore [AllowAnonymous] deve essere rimosso se 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 be useful for endpoints that
// allow but don't require authenticated users. When credentials are sent,
// they will be authenticated; when no credentials are sent, the endpoint
// allows anonymous access.
[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 in 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 log dettagliati 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.
- Osservando gli attacchi esterni in corso su Kestrel che influiscono sulle prestazioni e sulla stabilità.
- Registrazione dei tentativi di attacchi esterni su Kestrel che la protezione avanzata predefinita di Kestrel ha impedito grazie al rafforzamento delle misure di sicurezza integrate.
Per altre informazioni, vedere ASP.NET Metriche principali.
Personalizzare gli endpoint dei named pipe Kestrel
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 risposta ProblemDetails proveniente da ExceptionHandlerMiddleware.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Disattivare le metriche HTTP su 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 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 si mappa su 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 la Dependency Injection 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 fiducia in OpenSSL, lo strumento dev-certs:
- Inserisce il certificato in
~/.aspnet/dev-certs/trust - Esegue una versione semplificata dello strumento c_rehash di OpenSSL nella directory.
- Chiede all'utente di aggiornare la
SSL_CERT_DIRvariabile di ambiente.
Per stabilire fiducia in .NET, lo strumento inserisce il certificato nell'archivio My/Root certificati.
Per stabilire la fiducia nei database NSS , se presenti, lo strumento cerca i profili di Firefox nella directory principale, ~/.pki/nssdbe ~/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 jQuery Validation
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
Modifiche radicali
Usare gli articoli in Modifiche di rilievo in .NET per trovare modifiche di rilievo che potrebbero essere applicate durante l'aggiornamento di un'app a una versione più recente di .NET.