Novità di ASP.NET Core in .NET 11

Questo articolo illustra le modifiche più significative in ASP.NET Core in .NET 11 con collegamenti alla documentazione pertinente.

Questo articolo verrà aggiornato man mano che vengono rese disponibili nuove versioni di anteprima.

Blazor

In questa sezione vengono descritte le nuove funzionalità per Blazor.

Nuovo DisplayName componente e supporto per gli attributi [Display] e [DisplayName]

Il DisplayName componente può essere usato per visualizzare i nomi delle proprietà dagli attributi dei metadati:

[Required, DisplayName("Production Date")]
public DateTime ProductionDate { get; set; }

L'attributo[Display] nella proprietà della classe del modello è supportato:

[Required, Display(Name = "Production Date")]
public DateTime ProductionDate { get; set; }

Tra i due approcci, è consigliabile l'attributo [Display] , che rende disponibili proprietà aggiuntive. L'attributo abilita anche l'assegnazione [Display] di un tipo di risorsa per la localizzazione. Quando sono presenti entrambi gli attributi, [Display] ha la precedenza su [DisplayName]. Se nessun attributo è presente, il componente passa al nome della proprietà.

Usa il componente DisplayName nelle etichette o nelle intestazioni di tabella.

<label>
    <DisplayName For="@(() => Model!.ProductionDate)" />
    <InputDate @bind-Value="Model!.ProductionDate" />
</label>

Blazor Formato delle opzioni di avvio dello script Web ora supportato per gli script Blazor Server e Blazor WebAssembly

L'oggetto opzioni Blazor Web App (blazor.web.js) passato a Blazor.start() utilizza il seguente formato dalla versione di .NET 8:

Blazor.start({
  ssr: { ... },
  circuit: { ... },
  webAssembly: { ... },
});

Ora, Blazor Server gli script (blazor.server.js) e Blazor WebAssembly (blazor.webassembly.js) possono usare lo stesso formato di opzioni.

L'esempio seguente mostra il formato delle opzioni precedenti, che rimane supportato:

Blazor.start({
  loadBootResource: function (...) {
      ...
    },
  });

Formato delle opzioni appena supportate per l'esempio precedente:

Blazor.start({
  webAssembly: {
    loadBootResource: function (...) {
      ...
    },
  },
});

Per altre informazioni, vedere ASP.NET Core Blazor startup.

Nuovo BasePath componente

Blazor Web Apps può usare il nuovo BasePath componente (<BasePath />) per eseguire automaticamente il rendering del tag HTML del percorso di base dell'app (<base href>). Per altre informazioni, vedere ASP.NET Core Blazor percorso di base dell'app.

Gestore eventi inline JS rimosso dal componente NavMenu

Il gestore eventi inline JS che attiva o disattiva la visualizzazione dei collegamenti di navigazione non è più presente nel componente NavMenu del modello di progetto Blazor Web App. Le app generate dal modello di progetto ora usano un approccio di modulo co-localizzato JS per mostrare o nascondere la barra di navigazione nella pagina sottoposta a rendering. Il nuovo approccio migliora la conformità al Content Security Policy (CSP) perché non richiede di includere un hash non sicuro per l'elemento inline JS.

Per eseguire la migrazione di un'app esistente a .NET 11, compresa l'adozione del nuovo approccio del modulo JS per la barra di navigazione, vedere Migrazione da ASP.NET Core in .NET 10 a ASP.NET Core in .NET 11.

Il nuovo RelativeToCurrentUri parametro (impostazione predefinita: false) per NavigationManager.NavigateTo e il NavLink componente consente di passare agli URI relativi al percorso della pagina corrente anziché all'URI di base dell'app.

Prendere in considerazione i seguenti endpoint annidati:

  • /docs
    • /getting-started
      • /installation
      • /configuration

Quando l'URI del browser è /docs/getting-started/installation e vuoi navigare l'utente verso /docs/getting-started/configuration, NavigateTo("/configuration") reindirizza a /configuration, la radice dell'app, anziché al percorso relativo il /docs/getting-started/configuration. Imposta il RelativeToCurrentUri con NavigateTo o il componente NavLink per la navigazione desiderata.

Navigation.NavigateTo("/configuration", new NavigationOptions
{
    RelativeToCurrentUri = true
});
<NavLink href="configuration" RelativeToCurrentUri="true">Configuration</NavLink>

Rendere persistenti i dati temporanei tra le richieste HTTP durante il rendering statico lato server (static SSR)

Per rendere persistenti i dati temporanei tra le richieste HTTP durante il rendering statico lato server (SSR statico), Blazor supporta TempData. TempData è ideale per scenari come i messaggi flash dopo gli invii di moduli, il passaggio dei dati durante i reindirizzamenti (modello POST-Redirect-GET) e le notifiche monouso.

TempData è disponibile quando AddRazorComponents viene chiamato nel file dell'app Program e viene fornito come valore a cascata con l'attributo [CascadingParameter].

[CascadingParameter]
public ITempData? TempData { get; set; }

Quando viene fornito a un parametro per la lettura/scrittura semplice di un singolo valore, usare l'attributo [SupplyParameterFromTempData] :

[SupplyParameterFromTempData]
public string? Message { get; set; }

Per altre informazioni, vedere ASP.NET Core Blazor gestione dello stato lato server.

Nuovo Blazor modello di web worker (blazorwebworker)

Il modello di progetto .NET Web Worker, che contiene un client Web Worker per delegare attività di lunga durata a un thread in background, è stato rinominato modello di progetto Blazor Web Worker (blazorwebworker). La modifica del nome rende più chiaro che il template fa parte dello stack Blazor per l'uso nelle app Web Blazor WebAssembly e Blazor (per il rendering lato client, CSR).

Sono state aggiunte due funzionalità spesso richieste all'oggetto generato WebWorkerClient:

  • InvokeVoidAsync per le chiamate ai worker fire-and-forget che non restituiscono alcun valore, rispecchiando la struttura di IJSRuntime.
  • Supporto per l'annullamento e per il timeout sia durante la creazione del worker sia nelle invocazioni del worker, in modo che chi effettua la chiamata possa passare un CancellationToken e arrestare correttamente un worker bloccato.

I progetti esistenti creati con il modello precedente continuano a funzionare. La ridenominazione influisce solo sul nome del modello visualizzato in dotnet new list e nell'elenco di Visual Studio di Creare un nuovo progetto modelli.

Per ulteriori informazioni, vedi le seguenti risorse:

Miglioramenti della virtualizzazione

  • Il Virtualize<TItem> componente non presuppone più che ogni elemento abbia la stessa altezza. In precedenza, il componente disabilitava l'ancoraggio dello scorrimento nativo del browser (per evitare un ciclo di rendering infinito), il che significava che qualsiasi variazione di altezza al di sopra dell'area visibile — espansione degli elementi, aggiornamenti dei dati, contenuti caricati in modo differito — provocava uno spostamento improvviso degli elementi visibili sullo schermo. Il Virtualize componente ora si adatta alle dimensioni degli elementi misurate in fase di esecuzione, riducendo la spaziatura non corretta e lo scorrimento quando le altezze degli elementi variano.

    Gli aggiornamenti utilizzano un approccio ibrido: l'ancoraggio dello scorrimento CSS nativo nei browser che lo supportano per i layout non-<table>, con un fallback manuale di compensazione dello scorrimento basato su ResizeObserver per i layout <table> e per Safari, dove l'ancoraggio nativo calcola erroneamente le posizioni sugli elementi <tr>.

    Le app che usano il Virtualize componente ricevono automaticamente i vantaggi di questi aggiornamenti. Non sono necessarie modifiche all'API per sviluppatori.

    Questi aggiornamenti includono un aggiornamento del valore predefinito di Virtualize<TItem>.OverscanCount, che era 3 in .NET 10 o versioni precedenti e ora cambia in 15 in .NET 11 o versione successiva. La modifica nel valore predefinito aumenta la precisione dei calcoli dell'altezza media degli elementi.

    Per ulteriori informazioni, vedi le seguenti risorse:

  • Usare il nuovo AnchorMode parametro per controllare il comportamento del viewport in corrispondenza dei bordi dell'elenco quando gli elementi vengono aggiunti dinamicamente:

    • None: Nessun fissaggio ai bordi. Il riquadro di visualizzazione rimane nella posizione di scorrimento corrente indipendentemente dalle modifiche apportate all'elemento.
    • Beginning: aggiunge il riquadro di visualizzazione all'inizio dell'elenco. Ad esempio, questo comportamento di fissaggio è utile per l'esperienza utente di un feed di notizie.
    • End: aggiunge il riquadro di visualizzazione alla fine dell'elenco. Ad esempio, questo comportamento di fissaggio è utile per un'esperienza utente di chat o di log.

    Nell'esempio seguente il contenuto virtualizzato viene aggiunto all'inizio dell'elenco:

    <Virtualize AnchorMode="Beginning" ...>
        ...
    </Virtualize>
    

    Per ulteriori informazioni, vedi le seguenti risorse:

Nuovo modello di progetto di libreria per i valori predefiniti del servizio per Blazor WebAssembly le app

Il modello di progetto blazor-wasm-servicedefaults crea una libreria delle configurazioni predefinite del servizio per le app Blazor WebAssembly con integrazione Aspire. Per altre informazioni, vedere Strumenti per ASP.NET Core Blazor.

Nuovo server di sviluppo per Blazor WebAssembly le app

Microsoft.AspNetCore.Components.Gateway è un host ASP.NET Core leggero che sostituisce Microsoft.AspNetCore.Components.WebAssembly.DevServer per la gestione di app autonome Blazor WebAssembly durante lo sviluppo e la produzione.

Per adottare il gateway in un'app autonoma esistente Blazor WebAssembly, fare riferimento al pacchetto di anteprima Microsoft.AspNetCore.Components.Gateway nel file di progetto dell'app.

Note

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Verificare le versioni corrette dei pacchetti in NuGet.org.

Il codice di routing personalizzato e il middleware non sono richiesti dall'app. Gli endpoint di fallback provengono dal manifesto degli asset web statici generato da SDK quando la proprietà StaticWebAssetSpaFallbackEnabled è impostata nel file di progetto dell'app; questa proprietà è presente per impostazione predefinita nelle app autonome Blazor WebAssembly create dal modello di progetto:

<StaticWebAssetSpaFallbackEnabled>true</StaticWebAssetSpaFallbackEnabled>

Prima del rilascio di .NET 11, la proprietà inspectUri del file Properties/launchSettings.json:

  • Consente all'IDE di rilevare che l'app è un'app Blazor .
  • Indica all'infrastruttura di debug degli script di connettersi al browser tramite Blazoril proxy di debug.

La proprietà non è più necessaria quando si usa il nuovo server di sviluppo.

Aprire il Properties/launchSettings.json file del progetto di avvio. Rimuovere la proprietà inspectUri in ogni profilo di avvio nel nodo profiles del file:

- "inspectUri": "..."

Per ulteriori informazioni, vedi [Blazor] Sostituire DevServer con BlazorGateway per le app WASM autonome (dotnet/aspnetcore #65982) (Non commentare issue e PR chiuse).

Sospensione del circuito attivato dal server

Questa funzionalità riguarda le applicazioni lato Blazor server.

Blazor supporta già la pausa e la ripresa controllate del circuito con Blazor.pauseCircuit() e Blazor.resumeCircuit(). .NET 11 introduce una funzionalità di sospensione e ripresa sul lato server simmetrica, in cui il server può richiedere che i client connessi inizino il flusso normale di sospensione del circuito.

Circuit.RequestCircuitPauseAsync(CancellationToken) viene usato per richiedere che il client connesso inizi il flusso normale di sospensione del circuito. CancellationToken Annulla la richiesta prima che venga accettata dal framework. Il metodo restituisce true se la richiesta è stata accettata e al client è stato chiesto di iniziare la sospensione.

Questa funzionalità è utile negli scenari seguenti:

  • Interruzioni e rilasci pianificati.
  • Drenaggio delle istanze.
  • Finestre di manutenzione delle app.

Per altre informazioni e un esempio di implementazione per i riavvii del server, vedere ASP.NET Core Blazor gestione dello stato lato server.

Output di pubblicazione più piccolo Blazor WebAssembly

Due modifiche al trimming riducono le dimensioni delle app pubblicate Blazor WebAssembly che non usano OpenTelemetry (OTEL) o Ricaricamento rapido:

  • I tipi ComponentsMetrics e ComponentsActivitySource sono ora subordinati a un attributo [FeatureSwitchDefinition], quindi il trimmer può rimuovere da Renderer e simili i percorsi di chiamata relativi a metriche e tracciamento quando System.Diagnostics.Metrics.Meter.IsSupported è false (impostazione predefinita per le app sottoposte a trimming) [browser][wasm] Implementare il trimming IL per OTEL (dotnet/aspnetcore #65901) (Non commentare issue e PR chiuse).
  • HotReloadManager ora espone una proprietà controllata da un flag di funzionalità IsSupported collegata a System.Reflection.Metadata.MetadataUpdater.IsSupported, in modo che il trimmer possa eliminare le cache di Hot Reload e le registrazioni dei gestori di aggiornamento dei metadati in tutto il renderer in fase di pubblicazione [blazor][wasm] Correzione del trimming IL per Hot Reload (dotnet/aspnetcore #65903) (Non commentare issue e PR chiuse).

Le applicazioni che usano OTEL o Ricaricamento rapido non sono interessate dai precedenti aggiornamenti.

QuickGrid miglioramenti

Il componente QuickGrid riceve diverse nuove funzionalità in .NET 11.

Per altre informazioni sulle funzionalità seguenti, vedere ASP.NET Core Blazor componente 'QuickGrid'.

Modalità di paginazione

Prima del rilascio di .NET 11, lo stato di paginazione e ordinamento viene gestito in memoria all'interno del componente QuickGrid senza modificare l'URL, denominato inner-state navigation. È necessaria una modalità di rendering interattiva.

Con il rilascio di .NET 11, QuickGrid supporta la navigazione basata su URL.

Lo stato di paginazione e ordinamento viene salvato in modo permanente nella stringa di query URL. Quando gli utenti impaginano o ordinano, l'URL viene aggiornato (ad esempio: ?page=2&sort=Name&order=asc). Ciò consente la condivisione dei collegamenti, il back/forward del browser e ssr statico senza interattività.

Intestazioni di colonna ordinabili e controlli di impaginazione sono visualizzati come elementi <a> con attributi href. StaticHtmlRenderer esegue il rendering di queste ancore. In ogni richiesta, il server legge la stringa di query per determinare la pagina corrente e lo stato di ordinamento, senza che sia necessario il runtime JavaScript.

Parametri della stringa di query:

  • page: Numero di pagina a partire da 1. La prima pagina omette il parametro per gli URL puliti.
  • sort: titolo della colonna per l'ordinamento della griglia.
  • order: crescente (asc) o decrescente (desc).

La sort colonna è identificata dalla proprietà della Title colonna. Le colonne senza Title visualizzano un'intestazione <div> non cliccabile.

QuickGrid legge l'URL durante l'inizializzazione e si sottoscrive a NavigationManager.LocationChanged, quindi i pulsanti Indietro/Avanti del browser e l'immissione diretta dell'URL funzionano correttamente. Quando i parametri di ordinamento vengono rimossi dall'URL, si torna alla colonna e alla direzione di ordinamento predefinite.

I collegamenti impaginatori disabilitati usano aria-disabled="true" e pointer-events: none invece dell'attributo HTML disabled , che non esiste negli <a> elementi.

Più griglie nella stessa pagina

Più QuickGrid componenti nella stessa pagina richiedono valori univoci QueryParameterNamePrefix per evitare conflitti di stringhe di query. Il prefisso predefinito è una stringa vuota, producendo parametri denominati page, sort, order. Ad esempio, l'impostazione del prefisso su "cities" produce cities_page, cities_sorte cities_order.

Ognuno QuickGrid deve avere una propria PaginationState istanza. Più griglie non devono condividere un PaginationState se hanno prefissi diversi: quella di cui viene eseguito il rendering per ultima sovrascrive il nome del parametro di query nello stato condiviso, facendo sì che Paginator legga dal parametro sbagliato.

Nelle versioni precedenti a .NET 11, i componenti QuickGrid seguenti hanno funzionato in modo implicito:

<QuickGrid ... Pagination="@pagination1">
    ...
</QuickGrid>

<QuickGrid ... Pagination="@pagination2">
    ...
</QuickGrid>

Con il rilascio di .NET 11, i componenti QuickGrid seguenti richiedono un QueryParameterNamePrefix univoco. Il primo QuickGrid usa il prefisso stringa vuoto predefinito, mentre il secondo imposta cities come prefisso:

<QuickGrid ... Pagination="@pagination1">
    ...
</QuickGrid>

<QuickGrid ... Pagination="@pagination2" QueryParameterNamePrefix="cities">
    ...
</QuickGrid>

Stringa di query di esempio per i componenti precedenti QuickGrid :

?page=2&sort=Name&order=asc&cities_page=3&cities_sort=Country&cities_order=desc

Ordina per colonna

Aggiungere Sortable="true" a un oggetto PropertyColumn. Con la navigazione basata su URL, selezionando un'intestazione si passa a un URL con i parametri sort e order aggiornati. Con la navigazione nello stato interno, la selezione di un header attiva @onclick, che chiama SortByColumnAsync. In entrambi i casi, SortByColumnAsync passa tramite NavigationManager.NavigateTo(GetSortQueryStringUrl(...)), quindi l'URL riflette sempre lo stato di ordinamento.

Identificazione dell'ordinamento in base al titolo

Lo stato di ordinamento nell'URL usa la proprietà della Title colonna come identificatore. Il sort parametro di query è impostato su column.Title , ad esempio per il titolo Namedella colonna : ?sort=Name&order=asc. In caso di modifica dell'URL, QuickGrid associa nuovamente il valore sort a una colonna eseguendo _columns.FirstOrDefault(c => c.Title == sort.ColumnTitle). Se nessun titolo di colonna corrisponde, l'ordinamento viene ignorato e la griglia torna all'ordinamento predefinito.

Rinominare Title di una colonna comporta la rottura dell'URL. Qualsiasi URL salvato nei segnalibri o condiviso che contenga il titolo precedente nel parametro sort non corrisponde più e la griglia torna silenziosamente all’ordinamento predefinito invece di ordinare in base alla colonna desiderata. Per PropertyColumn, il valore predefinito di Title è il nome della proprietà (ad esempio: Property="@(p => p.FirstName)" produce Title="First Name"), quindi sia rinominare la proprietà sia modificare esplicitamente il parametro Title interrompono gli URL esistenti.

Impaginatore

Paginator inserisce NavigationManager, sottoscrive LocationChangede legge l'indice della pagina dalla stringa di query in ogni modifica della posizione. GoToPageAsync passa all'URL di destinazione anziché modificare PaginationStatedirettamente . Lo stato viene aggiornato tramite il flusso di LocationChanged callback.

GetPageUrl restituisce un URL con il numero di pagina in base uno. L'indice di pagina 0 (pagina 1) omette completamente il parametro di query.

Modifica di rilievo CSS

Quando la navigazione basata su URL è abilitata, i selettori che fanno riferimento a button.col-title devono fare riferimento anche a a.col-title, e nav button/nav button:disabled richiedono nav a/nav a[aria-disabled="true"]. Il foglio di stile predefinito QuickGrid fornisce entrambi per impostazione predefinita.

Come disabilitare la navigazione basata su URL

Per disabilitare la navigazione basata su URL, impostare l'opzione AppContext per la funzionalità su false:

AppContext.SetSwitch(
    "Microsoft.AspNetCore.Components.QuickGrid.EnableUrlBasedQuickGridNavigationAndSorting",
    false);

Questo ripristina gli elementi <button> con handler @onclick. È necessaria una modalità di rendering interattiva.

L'interruttore controlla solo l'elemento HTML renderizzato (<a> anziché <button>). Anche se disabilitato, QuickGrid legge e scrive lo stato nella stringa di query URL internamente. SortByColumnAsync e Paginator.GoToPageAsync navigano tramite NavigationManager.NavigateTo indipendentemente dal flag.

Evento clic sulla riga (OnRowClick)

Il QuickGrid componente supporta ora gli eventi di clic di riga tramite il nuovo OnRowClick parametro. Se impostata, la griglia applica automaticamente lo stile appropriato (puntatore del cursore) e richiama il callback con l'elemento selezionato:

@using Microsoft.AspNetCore.Components.QuickGrid
@inject NavigationManager NavigationManager

<QuickGrid Items="@people.AsQueryable()" 
    OnRowClick="@((Person args) => HandleRowClick(args))">
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.Email)" />
</QuickGrid>

@code {
    private List<Person> people = new()
    {
        new(1, "Alice Smith", "alice@example.com", "Engineering"),
        new(2, "Bob Johnson", "bob@example.com", "Marketing"),
        new(3, "Carol Williams", "carol@example.com", "Engineering"),
    };

    private void HandleRowClick(Person person)
    {
        NavigationManager.NavigateTo($"/person/{person.Id}");
    }

    private record Person(int Id, string Name, string Email, string Department);
}

La funzionalità include stili CSS predefiniti che applicano un cursore del puntatore alle righe selezionabili tramite la classe CSS selezionabile per righe, fornendo commenti visivi chiari agli utenti.

Il prerendering sul lato client in un Blazor Web App preserva le impostazioni cultura del server

Per impostazione predefinita, il prerendering lato client sul server (.Client progetto in un Blazor Web App) mantiene i valori del server CurrentCulture e CurrentUICulture nello stato del componente e li applica sul client prima del caricamento degli assembly satellite.

Le applicazioni che richiedono al client di scegliere le impostazioni cultura indipendentemente dal server possono disattivarlo con WebAssemblyComponentsOptions.UseCultureFromServer nel file Blazor Web App di Program:

builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents(options =>
    {
        options.UseCultureFromServer = false;
    });

Mantenere persistenti i dati della sessione tra le richieste HTTP durante il rendering statico lato server (SSR statico)

La persistenza dei dati di sessione consente di leggere e scrivere valori di sessione HTTP basati su cookie durante il rendering statico lato server (SSR statico), ed è utile in scenari come gli ID del carrello o l'avanzamento di un modulo in più passaggi. A differenza della persistenza temporanea dei dati (ITempData), i valori di sessione non vengono cancellati dopo la lettura. I valori persistono tra richieste multiple per la durata della sessione.

La configurazione dell'archiviazione delle sessioni richiede l'aggiunta di servizi tramite la chiamata a AddSession e la configurazione della pipeline di richiesta con UseSession:

builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();
builder.Services.AddRazorComponents();

var app = builder.Build();

app.UseSession();

Quando viene specificato a un parametro, usare l'attributo [SupplyParameterFromSession] senza o con una chiave (stringa):

[SupplyParameterFromSession]
public string? Message { get; set; }

[SupplyParameterFromSession(Name = "flash_message")]
public string? FlashMessage { get; set; }

Per altre informazioni, vedere ASP.NET Core Blazor gestione dello stato lato server.

GetUriWithHash metodo di estensione

Un nuovo GetUriWithHash metodo di estensione consente di NavigationManager costruire facilmente URI con frammenti hash. Questo metodo di supporto fornisce un modo efficiente, senza allocazioni, per aggiungere frammenti hash all'URI corrente. L'esempio seguente illustra due casi d'uso:

  • Chiamata inline che passa alla sezione 1 (id="section-1") della pagina di cui è stato eseguito il rendering.
  • Chiamata di metodo che riceve un ID di sezione (sectionId) e passa alla relativa sezione della pagina.
@inject NavigationManager Navigation

<a href="@Navigation.GetUriWithHash("section-1")">
    Jump to Section 1
</a>

@code {
    private void NavigateToSection(string sectionId)
    {
        var uri = Navigation.GetUriWithHash(sectionId);
        Navigation.NavigateTo(uri);
    }
}

Il metodo usa string.Create per ottenere prestazioni ottimali e funziona correttamente con URI di base non radice, ad esempio quando si usa <base href="/app/">.

Componente EnvironmentBoundary

Blazor include ora un componente predefinito EnvironmentBoundary per il rendering condizionale basato sull'ambiente di hosting. Questo componente offre un modo coerente per eseguire il rendering del contenuto in base all'ambiente corrente in entrambi i modelli di hosting lato server e lato client.

Il componente EnvironmentBoundary accetta i parametri Include e Exclude per specificare i nomi degli ambienti. Il componente esegue la corrispondenza senza distinzione tra maiuscole e minuscole e segue la stessa semantica di MVC.EnvironmentTagHelper

@using Microsoft.AspNetCore.Components.Web

<EnvironmentBoundary Include="Development">
    <div class="alert alert-warning">
        Debug mode enabled
    </div>
</EnvironmentBoundary>

<EnvironmentBoundary Include="Development,Staging">
    <p>Pre-production environment</p>
</EnvironmentBoundary>

<EnvironmentBoundary Exclude="Production">
    <p>@DateTime.Now</p>
</EnvironmentBoundary>

Supporto per il namespace MathML

Blazor supporta ora gli elementi MathML nel rendering interattivo. Gli elementi MathML, ad esempio <math>, <mrow><mi>, e <mn>, vengono creati con lo spazio dei nomi corretto (http://www.w3.org/1998/Math/MathML) usando document.createElementNS(), analogamente al modo in cui vengono gestiti gli elementi SVG:

<math>
    <mrow>
        <mi>x</mi>
        <mo>=</mo>
        <mfrac>
            <mrow>
                <mo>−</mo>
                <mi>b</mi>
                <mo>±</mo>
                <msqrt>
                    <mrow>
                        <msup><mi>b</mi><mn>2</mn></msup>
                        <mo>−</mo>
                        <mn>4</mn>
                        <mi>a</mi>
                        <mi>c</mi>
                    </mrow>
                </msqrt>
            </mrow>
            <mrow>
                <mn>2</mn>
                <mi>a</mi>
            </mrow>
        </mfrac>
    </mrow>
</math>

Questa correzione garantisce che i contenuti MathML vengano visualizzati correttamente nei browser quando vengono aggiunti dinamicamente tramite il renderer di Blazor, risolvendo i problemi per cui gli elementi MathML venivano in precedenza creati come normali elementi HTML senza il namespace corretto.

InvokeVoidAsync() analizzatore

È stato aggiunto un nuovo Blazor analizzatore (BL0010) che consiglia di usare InvokeVoidAsync invece di InvokeAsync<object> chiamare funzioni JavaScript che non restituiscono valori. Questo analizzatore consente agli sviluppatori di scrivere codice JSInterop più efficiente.

Codice problematico:

// ⚠️ BL0010: Use InvokeVoidAsync for JavaScript functions that don't return a value
await JSRuntime.InvokeAsync<object>("console.log", "Hello");

Codice consigliato:

// ✅ Correct: Use InvokeVoidAsync
await JSRuntime.InvokeVoidAsync("console.log", "Hello");

L'analizzatore aiuta a individuare i problemi di prestazioni in cui InvokeAsync viene usato inutilmente con object oppure vengono ignorati i valori restituiti, indirizzando gli sviluppatori verso il metodo InvokeVoidAsync più appropriato.

IComponentPropertyActivator

Blazor ora fornisce IComponentPropertyActivator per personalizzare il modo in cui le proprietà [Inject] vengono popolate nei componenti. Ciò consente scenari avanzati, ad esempio:

  • Fornire un contesto aggiuntivo per la risoluzione delle proprietà.
  • Supporto per contenitori DI personalizzati che devono intercettare l'iniezione di proprietà.
  • Scenari avanzati che richiedono la personalizzazione dell'inserimento delle proprietà.
public interface IComponentPropertyActivator
{
    Action<IServiceProvider, IComponent> GetActivator(
        [DynamicallyAccessedMembers(Component)] Type componentType);
}

L'implementazione predefinita memorizza nella cache gli attivatori per tipo di componente, supporta i servizi con chiave tramite [Inject(Key = "...")], si integra con Ricaricamento rapido per invalidazione della cache e include annotazioni di taglio appropriate per la compatibilità AOT.

SignalR ConfigureConnection per i componenti del server interattivo

Blazor consente ora di configurare le opzioni di connessione SignalR sottostanti quando si utilizzano i componenti di Interactive Server tramite la nuova proprietà ConfigureConnection in ServerComponentsEndpointOptions. In questo modo è possibile configurare HttpConnectionDispatcherOptions le proprietà che in precedenza erano accessibili solo tramite soluzioni alternative.

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode(options =>
    {
        options.ConfigureConnection = dispatcherOptions =>
        {
            dispatcherOptions.CloseOnAuthenticationExpiration = true;
            dispatcherOptions.AllowStatefulReconnects = true;
            dispatcherOptions.ApplicationMaxBufferSize = 1024 * 1024;
        };
    });

In questo modo è disponibile un'API pulita e indipendente dai tipi per configurare SignalR le impostazioni di connessione senza dover esaminare i metadati dell'endpoint.

IHostedService supporto in Blazor WebAssembly

Blazor WebAssembly supporta IHostedService ora per l'esecuzione di servizi in background nel browser. Ciò offre parità di funzionalità con Blazor Server e consente scenari come l'aggiornamento periodico dei dati, gli aggiornamenti in tempo reale e l'elaborazione in background.

public class DataRefreshService : IHostedService
{
    private Timer? _timer;
    
    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(RefreshData, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
        return Task.CompletedTask;
    }

    private void RefreshData(object? state)
    {
        // Refresh data periodically
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Dispose();
        return Task.CompletedTask;
    }
}

// Registration
builder.Services.AddHostedService<DataRefreshService>();

I servizi ospitati vengono avviati quando l'app si avvia e arrestati quando viene chiusa, garantendo un ciclo di vita chiaro per le operazioni in background nelle app Blazor WebAssembly.

Variabili di ambiente nella Blazor WebAssembly configurazione

Blazor WebAssembly le applicazioni possono ora accedere alle variabili di ambiente tramite IConfiguration. Ciò consente la configurazione di runtime senza ricompilare l'applicazione, semplificando la distribuzione della stessa compilazione in ambienti diversi.

Nell'esempio seguente le API_ENDPOINT variabili di ambiente e ENABLE_FEATURE_X vengono incluse automaticamente nella configurazione:

var builder = WebAssemblyHostBuilder.CreateDefault(args);

var apiEndpoint = builder.Configuration["API_ENDPOINT"];
var featureFlag = builder.Configuration["ENABLE_FEATURE_X"];

Le variabili di ambiente vengono caricate nel sistema di configurazione insieme ad altre origini di configurazione, ad esempio le impostazioni dell'app (appsettings.json), fornendo un modo unificato per accedere ai valori di configurazione indipendentemente dall'origine.

Blazor WebAssembly metriche dei componenti e tracciamento

Blazor WebAssembly Le app ora forniscono metriche e traccia specifiche dei componenti quando il supporto per le metriche è stato abilitato nel runtime.

Abilitare il supporto dei contenitori nel Blazor Web App modello

Il modello di progetto Blazor Web App supporta ora l'opzione Enable container support in Visual Studio. In questo modo è più semplice inserire in contenitori Blazor Web App e distribuirli in piattaforme di orchestrazione dei contenitori, ad esempio Kubernetes o App contenitore di Azure.

L'SSR statico supporta la validazione lato client

Blazor i moduli con rendering statico lato server (SSR statico) ora ricevono immediatamente un riscontro di convalida nel browser senza un'andata e ritorno al server, in linea con l’esperienza offerta dalle app interattive Blazor e dalle app MVC con convalida non intrusiva. Il modello .NET rimane l'unica fonte di verità per le regole di convalida. Il server genera i metadati per le regole di convalida, che vengono poi fatte rispettare dal codice BlazorJS lato client.

La funzionalità è abilitata per impostazione predefinita per tutti i moduli SSR statici che includono il DataAnnotationsValidator componente. Sono supportati sia moduli avanzati che non avanzati.

La copertura completa delle funzionalità è disponibile in ASP.NET Core Blazor form validation.

Per ulteriori informazioni, vedi le seguenti risorse:

Per favore, non commentare su issue chiuse e PR chiuse. Se si hanno commenti e suggerimenti su questa funzionalità, aprire un nuovo problema nel repository dotnet/aspnetcore GitHub.

Supporto per la convalida asincrona dei moduli

Blazor I moduli ricevono il supporto per le regole di convalida asincrone, ad esempio ricerche di database o chiamate API remote. In qualsiasi modalità di rendering, EditForm la convalida all'invio ora attende correttamente i validatori asincroni dall'inizio alla fine. Nelle modalità interattive, i componenti del validator possono registrare attività asincrone per campo tramite EditContext.AddValidationTask. Il framework li tiene traccia, annulla le attività sostituite ed espone lo stato di avanzamento tramite IsValidationPending(field) e IsValidationFaulted(field).

Mentre l'anteprima 5 include i blocchi predefiniti per i moduli Blazor, l'esperienza di convalida asincrona completa verrà abilitata quando le nuove API asincrone DataAnnotations vengono rilasciate in un'anteprima successiva .NET. Queste API saranno completamente supportate dal componente esistente DataAnnotationsValidator .

<EditForm EditContext="editContext" OnSubmit="HandleSubmit">
    <InputText @bind-Value="model.Username" />
    @if (editContext.IsValidationPending(() => model.Username))
    {
        <span>Checking availability...</span>
    }
    <ValidationMessage For="() => model.Username" />
    <button type="submit">Register</button>
</EditForm>

@code {
    [Inject] public UserService Users { get; set; } = default!;

    private readonly RegistrationModel model = new();
    private EditContext editContext = default!;
    private ValidationMessageStore messages = default!;

    protected override void OnInitialized()
    {
        editContext = new EditContext(model);
        messages = new ValidationMessageStore(editContext);
        editContext.OnFieldChanged += (_, e) =>
        {
            if (e.FieldIdentifier.FieldName == nameof(model.Username))
            {
                var cts = new CancellationTokenSource();
                editContext.AddValidationTask(e.FieldIdentifier,
                    CheckAsync(e.FieldIdentifier, model.Username, cts.Token), cts);
            }
        };
    }

    private async Task CheckAsync(FieldIdentifier field, string value, CancellationToken ct)
    {
        messages.Clear(field);
        if (await Users.IsUsernameTakenAsync(value, ct))
        {
            messages.Add(field, "Username is taken.");
        }
        editContext.NotifyValidationStateChanged();
    }

    private async Task HandleSubmit() => await editContext.ValidateAsync();
}

La copertura completa delle funzionalità è disponibile in ASP.NET Core Blazor form validation.

Per ulteriori informazioni, vedere Aggiungere il supporto predefinito per la convalida asincrona dei moduli in Blazor (dotnet/aspnetcore #66526).

Per favore, non commentare su issue chiuse e PR chiuse. Se si hanno commenti e suggerimenti su questa funzionalità, aprire un nuovo problema nel repository dotnet/aspnetcore GitHub.

Blazor e le API minimali supportano la localizzazione degli errori

La validazione dei Blazor form e degli endpoint Minimal API riceve un supporto di primo livello per la localizzazione dei messaggi di errore e dei nomi delle proprietà. Per impostazione predefinita, la localizzazione usa file RESX specifici della lingua distribuiti come parte dell'assembly.

builder.Services.AddValidation()
    .AddValidationLocalization<ValidationMessages>();
    // Resolves to ValidationMessages.en.resx, ValidationMessages.es.resx, ...
[ValidatableType]
public class ContactModel
{
    // Values of ErrorMessage are used as localization keys.
    [Required(ErrorMessage = "RequiredError")]
    [EmailAddress(ErrorMessage = "EmailError")]
    [Display(Name = "ContactEmail")]
    public string? Email { get; set; }
}

Le app possono anche registrare implementazioni personalizzate IStringLocalizerFactory per leggere le stringhe localizzate da altre origini, ad esempio database o file JSON. Un tipo registrato dall'utente ha la precedenza sulla localizzazione RESX predefinita.

builder.Services.AddValidation()
    .AddValidationLocalization();
builder.Services.AddSingleton<IStringLocalizerFactory, DbStringLocalizerFactory>();

Le app possono anche configurare una strategia programmatica per la localizzazione, eliminando la necessità di specificare le chiavi di localizzazione in ogni attributo di convalida:

builder.Services.AddValidation()
    .AddValidationLocalization<ValidationMessages>(options =>
    {
        options.ErrorMessageKeyProvider = ctx =>
            ctx.Attribute.ErrorMessage ?? $"{ctx.Attribute.GetType().Name}_Error";
    });
[ValidatableType]
public class ContactModel
{
    // Looks-up localized string for 'RequiredAttribute_Error' automatically.
    [Required]
    public string? Username { get; set; }
}

La copertura completa delle funzionalità è disponibile negli articoli seguenti:

Per altre informazioni, vedere Aggiungere il supporto per la localizzazione per Microsoft. Extensions.Validation (dotnet/aspnetcore #66646).

Per favore, non commentare su issue chiuse e PR chiuse. Se si hanno commenti e suggerimenti su questa funzionalità, aprire un nuovo problema nel repository dotnet/aspnetcore GitHub.

Blazor Hybrid

In questa sezione vengono descritte le nuove funzionalità per Blazor Hybrid.

Le note sulla versione vengono visualizzate in questa sezione man mano che diventano disponibili le funzionalità di anteprima.

SignalR

In questa sezione vengono descritte le nuove funzionalità per SignalR.

API minimali

Questa sezione descrive le nuove funzionalità per le API minime.

I filtri degli endpoint osservano gli errori di associazione dei parametri

Quando un endpoint API minimo include filtri o factory di filtri configurati, la pipeline di filtro viene ora eseguita anche se l'associazione di parametri ha esito negativo. I filtri possono leggere HttpContext.Response.StatusCode == 400 e sostituire il corpo della risposta con uno proprio.

Nell'ambiente Development impostare RouteHandlerOptions.ThrowOnBadRequest = false in modo che il framework restituisca un valore 400 che il filtro possa osservare anziché generare BadHttpRequestException nella pagina delle eccezioni dello sviluppatore. Si tratta già dell'impostazione predefinita in ambienti nonDevelopment .

Grazie @marcominerva per questo contributo!

Tipi di unione C#

ASP.NET Core supporta i tipi union di C# (novità di .NET 11) ovunque venga usato System.Text.Json: nei corpi JSON di richieste e risposte in Minimal API e MVC, in SignalRJsonHubProtocol, nell'interoperabilità JavaScript di Blazor, nello stato persistente del componente e nei parametri dei componenti sottoposti a prerendering.

public union UnionIntString(int, string);

app.MapGet("/value", () => new UnionIntString(42));

I tipi di unione non sono supportati per origini di binding diverse dal corpo della richiesta, ad esempio parametri di route, stringhe di query, intestazioni e campi del modulo.

OpenAPI

Questa sezione descrive le nuove funzionalità per OpenAPI.

Descrivere le risposte ai file binari

ASP.NET Core 11 introduce il supporto per la generazione di descrizioni OpenAPI per le operazioni che restituiscono risposte di file binari. La funzionalità mappa il tipo di risultato FileContentResult a uno schema OpenAPI con type: string e format: binary.

Usare il Produces<T> metodo di estensione con T di FileContentResult per specificare il tipo di risposta e il tipo di contenuto:

app.MapPost("/filecontentresult", () =>
{
    var content = "This endpoint returns a FileContentResult!"u8.ToArray();
    return TypedResults.File(content);
})
.Produces<FileContentResult>(contentType: MediaTypeNames.Application.Octet);

Il documento OpenAPI generato descrive la risposta dell'endpoint come segue:

responses:
  '200':
    description: OK
    content:
      application/octet-stream:
        schema:
          $ref: '#/components/schemas/FileContentResult'

l'oggetto FileContentResult è definito in components/schemas come:

components:
  schemas:
    FileContentResult:
      type: string
      format: binary

Supporto di OpenAPI 3.2.0 (cambiamento significativo)

Microsoft.AspNetCore.OpenApi ora supporta OpenAPI 3.2.0 tramite una dipendenza aggiornata da Microsoft.OpenApi 3.3.1. Questo aggiornamento include modifiche di rilievo dalla libreria sottostante. Per altre informazioni, vedere il Microsoft. Guida all'aggiornamento openApi.

Per generare un documento OpenAPI 3.2.0, specificare la versione quando si chiama AddOpenApi:

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_2;
});

Gli aggiornamenti successivi sfruttano le nuove funzionalità nella specifica 3.2.0, ad esempio il supporto dello schema degli elementi per gli eventi di streaming.

Grazie @baywet per questo contributo!

Query HTTP nei documenti OpenAPI generati

La generazione di documenti OpenAPI ora riconosce HTTP QUERY come tipo di operazione nota. QUERY è un metodo sicuro e idempotente proposto che consente ai client di inviare il corpo di una richiesta per descrivere una ricerca, utile quando una query è troppo grande o troppo strutturata per rientrare in un URL. Il routing accetta già stringhe verbo arbitrarie tramite MapMethodse OpenAPI 3.2 aggiunge un query campo all'oggetto Elemento percorso in modo che sia possibile descriverlo nel documento OpenAPI.

Si noti che query è valido solo in un documento OpenAPI 3.2, quindi impostare OpenApiVersion in OpenApiOptions. Nelle versioni precedenti di OpenAPI, l'operazione query viene generata all'interno di un'estensione della specifica x-oai-additionalOperations nell'oggetto Path Item.

using Microsoft.OpenApi;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_2;
});

var app = builder.Build();

app.MapOpenApi();

app.MapMethods("/search", ["QUERY"], (SearchRequest request) =>
    SearchService.Run(request));

app.Run();

In un documento OpenAPI 3.2 l'operazione QUERY viene descritta inline come elemento di pari livello di get, poste altre operazioni standard:

"paths": {
  "/search": {
    "query": {
      "requestBody": { ... },
      "responses": { "200": { ... } }
    }
  }
}

Nei documenti OpenAPI 3.0 e 3.1, la stessa operazione è rappresentata sotto l'estensione x-oai-additionalOperations dell'elemento Path:

"paths": {
  "/search": {
    "x-oai-additionalOperations": {
      "QUERY": {
        "requestBody": { ... },
        "responses": { "200": { ... } }
      }
    }
  }
}

Grazie @kilifu per questo contributo!

I tipi di risultati del flusso di file vengono visualizzati nei documenti OpenAPI

FileStreamResult, FileContentHttpResulte FileStreamHttpResult sono ora descritti come schemi di stringa binaria nei documenti OpenAPI generati, in modo che i client vedano forme di risposta accurate per gli endpoint che eseguono il flusso dei file. Annotare l'endpoint con .Produces<FileContentHttpResult>(contentType: "application/pdf") (o il tipo equivalente FileStreamHttpResult/FileStreamResult ) in modo che OpenAPI veda il tipo di risultato e genera lo schema binario.

Grazie @marcominerva per questo contributo!

Gli schemi OpenAPI corrispondono meglio al comportamento di ASP.NET Core

La generazione OpenAPI ora gestisce diversi casi di schema in modo più accurato. I parametri enum non inclusi nel corpo mantengono i nomi originali dei membri enum C# anche quando le opzioni JSON HTTP configurano un criterio di denominazione JsonStringEnumConverter, perché il binding di query, route, header e dei dati del modulo usa Enum.TryParse anziché la serializzazione JSON. Gli ID di riferimento dello schema dell'array ora utilizzano nomi di componenti validi, come stringArray e TodoArray, anziché nomi che usano la sintassi array.

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.Converters.Add(
        new JsonStringEnumConverter(JsonNamingPolicy.KebabCaseLower));
});

app.MapGet("/orders", (OrderStatus status) => Results.Ok(status));

Con questa configurazione, uno schema del corpo può comunque descrivere OrderStatus.PendingReview come pending-review, mentre lo schema dei parametri di query descrive il valore accettato come PendingReview.

Gli endpoint Minimal API possono supportare più chiamate al metodo di estensione Produces per lo stesso codice di stato, ad esempio per specificare che una risposta 200 può essere restituita come application/json o text/plain con schemi differenti. Lo stesso supporto si applica ai controller MVC tramite più [ProducesResponseType] attributi.

Nelle versioni precedenti, il framework comprimeva ogni codice di stato a un singolo tipo di risposta e rilasciava automaticamente il resto, rendendo impossibile descrivere gli endpoint che servono più tipi di contenuto. Microsoft.AspNetCore.Mvc.ApiExplorer mantiene ora ogni tipo di risposta dichiarato con ordinamento deterministico e il documento OpenAPI generato genera voci di contenuto separate per tipo di supporto o uno schema anyOf quando più tipi condividono lo stesso tipo di contenuto.

Grazie @marcominerva per il contributo di riferimento allo schema di matrice.

Autenticazione e autorizzazione

In questa sezione vengono descritte le nuove funzionalità per l'autenticazione e l'autorizzazione.

supporto in TimeProvider ASP.NET Core Identity

ASP.NET Core Identity ora usa TimeProvider anziché DateTime e DateTimeOffset per tutte le operazioni correlate al tempo. Questa modifica rende Identity i componenti più testabili e offre un migliore controllo nel tempo nei test e negli scenari specializzati.

L'esempio seguente illustra come usare un falso TimeProvider per le funzionalità di test Identity :

// In tests
var fakeTimeProvider = new FakeTimeProvider(
    new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero));

services.AddSingleton<TimeProvider>(fakeTimeProvider);
services.AddIdentity<IdentityUser, IdentityRole>();

// Identity will now use the fake time provider

Usando TimeProvider, è possibile scrivere più facilmente test deterministici per funzionalità sensibili al Identity tempo, ad esempio la scadenza del token, la durata del blocco e la convalida dello stamp di sicurezza.

Dedurre il nome chiave di accesso visualizzato dall'autenticatore

ASP.NET Core Identity ora deduce automaticamente nomi di visualizzazione amichevoli per le passkey in base al relativo AAGUID (Authenticator Attestation GUID). I mapping predefiniti sono inclusi per gli autenticatori passkey usati più di frequente, tra cui Google Password Manager, iCloud Keychain, Windows Hello, 1Password e Bitwarden.

Per gli autenticatori noti, il nome viene assegnato automaticamente senza chiedere conferma all'utente. Per gli autenticatori sconosciuti, l'utente viene reindirizzato a una pagina di ridenominazione. Estendere i mapping aggiungendo voci al dizionario PasskeyAuthenticators nel progetto.

Miscellaneous

Questa sezione descrive varie nuove funzionalità in .NET 11.

interfaccia IOutputCachePolicyProvider

ASP.NET Core in .NET 11 fornisce l'interfaccia IOutputCachePolicyProvider per implementare la logica di selezione dei criteri personalizzati di memorizzazione nella cache dell'output. Usando questa interfaccia, le app possono determinare i criteri di memorizzazione nella cache di base predefiniti, verificare l'esistenza di criteri denominati e supportare scenari avanzati in cui i criteri devono essere risolti in modo dinamico. Gli esempi includono il caricamento di criteri da fonti di configurazione esterne, dalle database, o l'applicazione di regole di caching specifiche per l'inquilino.

Il codice seguente illustra l'interfaccia IOutputCachePolicyProvider :

public interface IOutputCachePolicyProvider
{
    IReadOnlyList<IOutputCachePolicy> GetBasePolicies();
    ValueTask<IOutputCachePolicy?> GetPolicyAsync(string policyName);
}

Grazie @lqlive per questo contributo!

Certificati di sviluppo con attendibilità automatica in WSL

L'installazione del certificato di sviluppo considera automaticamente attendibili i certificati negli ambienti WSL (sottosistema Windows per Linux). Quando si esegue dotnet dev-certs https --trust in WSL, il certificato viene installato e considerato attendibile automaticamente nell'ambiente WSL e Windows, eliminando la configurazione dell'attendibilità manuale.

# Automatically trusts certificates in both WSL and Windows
dotnet dev-certs https --trust

Questo miglioramento semplifica l'esperienza di sviluppo quando si usa WSL, rimuovendo un punto di attrito comune per gli sviluppatori che lavorano in ambienti Linux in Windows.

Grazie @StickFun per questo contributo!

Tracciamento OpenTelemetry nativo per ASP.NET Core

ASP.NET Core ora aggiunge in modo nativo gli attributi della convenzione semantica OpenTelemetry all'attività del server HTTP, allineati alla specifica OpenTelemetry HTTP span specification. Tutti gli attributi obbligatori sono inclusi per impostazione predefinita, corrispondenti ai metadati disponibili in precedenza solo tramite la OpenTelemetry.Instrumentation.AspNetCore libreria.

Per raccogliere i dati di traccia predefiniti, sottoscrivere l'origine attività Microsoft.AspNetCore nella configurazione di OpenTelemetry:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource("Microsoft.AspNetCore")
        .AddConsoleExporter());

Non è necessaria alcuna libreria di strumentazione aggiuntiva, ad esempio OpenTelemetry.Instrumentation.AspNetCore. Il framework ora popola direttamente gli attributi della convenzione semantica per l'attività di richiesta, ad esempio http.request.method, url.path, http.response.status_codee server.address.

Se non si desidera aggiungere attributi OpenTelemetry all'attività, è possibile disattivarlo impostando l'interruttore AppContext su Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryDatatrue.

Miglioramenti delle prestazioni

KestrelIl parser di richiesta HTTP/1.1 ora utilizza un percorso di codice che non genera eccezioni per la gestione delle richieste malformate. Anziché generare BadHttpRequestException in ogni errore di analisi, il parser restituisce una struttura di risultato che indica stati di successo, incompleti o di errore. Negli scenari con molte richieste malformate, come l'analisi delle porte, il traffico dannoso o i client configurati in modo errato, elimina il costoso sovraccarico di gestione delle eccezioni e migliora il throughput fino al 20-40%. Non c'è alcun impatto sull'elaborazione valida delle richieste.

Il middleware di registrazione HTTP ora utilizza pool per le ResponseBufferingStream istanze, riducendo le allocazioni per ogni richiesta quando il corpo di risposta o gli intercettori sono abilitati.

Compressione della risposta Zstandard e decompressione delle richieste

ASP.NET Core supporta ora Zstandard (zstd) sia per la compressione della risposta che per la decompressione delle richieste. In questo modo, il supporto zstd viene aggiunto al middleware di compressione e decompressione della risposta esistente e abilita zstd per impostazione predefinita.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
    options.CompressionOptions = new ZstandardCompressionOptions
    {
        Quality = 6 // 1-22, higher = better compression, slower
    };
});

Grazie @manandre per questo contributo!

HTTP/3 avvia l'elaborazione delle richieste in precedenza

Kestrel ora avvia l'elaborazione delle richieste HTTP/3 senza attendere prima il flusso di controllo e il frame SETTINGS, riducendo così la latenza della prima richiesta sulle nuove connessioni.

Modello server MCP fornito con .NET SDK

Il protocollo MCP (Model Context Protocol) è uno standard aperto che le applicazioni e gli agenti di intelligenza artificiale, ad esempio quelli in Visual Studio, Visual Studio Code e GitHub Copilot, usano per individuare e chiamare strumenti, dati e servizi esterni tramite un'interfaccia coerente. Un server MCP espone le proprie funzionalità, ad esempio strumenti personalizzati o accesso a un'origine dati, in modo che un host di intelligenza artificiale possa richiamarlo per conto dell'utente.

Usare il mcpserver modello quando si vuole creare un server MCP C# che integra il codice o i servizi con strumenti basati sull'intelligenza artificiale. Il progetto generato usa l'SDK C# ufficiale per MCP e include uno strumento di esempio funzionante, quindi è disponibile un punto di partenza eseguibile da estendere con i propri strumenti.

Il modello di progetto mcpserver, precedentemente disponibile solo installando Microsoft.McpServer.ProjectTemplates, ora viene fornito come modello in bundle nell'SDK di .NET:

dotnet new mcpserver -o MyMcpServer

Lo spostamento del modello in ASP.NET Core lo rende individuabile da dotnet new list senza un passaggio di installazione separato e ne allinea la manutenzione al resto dello stack Web.

Per altre informazioni, vedere Creare un server MCP (Model Context Protocol) in C#.

Osservabilità dell'handshake TLS in Kestrel

Due modifiche correlate semplificano la diagnosi e la personalizzazione delle connessioni TLS in Kestrel.

ITlsHandshakeFeature ora espone una proprietà Exception contenente l'eccezione generata durante un handshake TLS fallito, così che il middleware e il logging possano registrare il motivo del fallimento di una connessione invece di vedere un semplice IOException a un livello superiore dello stack. La funzionalità continua a funzionare anche se l'handshake non riesce: Kestrel acquisisce uno snapshot dei campi pertinenti dall'oggetto SslStream sottostante prima che venga eliminato.

L'opzione TlsClientHelloBytesCallback su HttpsConnectionAdapterOptions è stata rielaborata come middleware di connessione. La firma della callback precedente è ormai obsoleta; configura invece l'ispezione di ClientHello tramite la nuova estensione ListenOptions.UseTlsClientHelloListener. L'esempio seguente usa entrambe le funzionalità, ovvero il middleware di connessione legge ITlsHandshakeFeature.Exception dopo l'handshake e UseTlsClientHelloListener controlla ClientHello prima di TLS:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.Use(next => async context =>
        {
            await next(context);

            var tlsHandshakeFeature = context.Features.Get<ITlsHandshakeFeature>();
            if (tlsHandshakeFeature?.Exception is { } ex)
            {
                Console.WriteLine($"[TLS Handshake Failed] ConnectionId={context.ConnectionId}, Exception={ex.GetType().Name}: {ex.Message}");
            }
        });

        // UseTlsClientHelloListener must be called before UseHttps()
        listenOptions.UseTlsClientHelloListener((connection, clientHelloBytes) =>
        {
            Console.WriteLine($"TLS Client Hello received on {connection.ConnectionId}, {clientHelloBytes.Length} bytes");
        });
        listenOptions.UseHttps();
    });
});

La compressione della risposta genera sempre Vary: Accept-Encoding

Il middleware di compressione della risposta aggiunge Vary: Accept-Encoding ora a ogni risposta quando è abilitata la compressione, anche quando la risposta stessa non è compressa. Ciò impedisce alle cache condivise e alle reti CDN di gestire un payload compresso in un client che non ha richiesto uno (o viceversa).

Grazie @pedrobsaila per questo contributo!

Runtime-async attivato per le librerie del framework condiviso

Le librerie basate esclusivamente sul framework condiviso di ASP.NET Core vengono ora compilate con la funzionalità runtime-async su net11.0+. Runtime-async consente al runtime, anziché al compilatore C#, di generare la macchina a stati per async/await, il che può ridurre le allocazioni per ogni await e migliorare la diagnostica. Si tratta di una modifica interna alla generazione del codice senza alcun impatto sulle API pubbliche — le app destinate a net11.0 ne beneficiano automaticamente quando effettuano chiamate alle librerie ASP.NET Core interessate.

Le librerie distribuite sia come componenti del framework condiviso sia come pacchetti NuGet autonomi sono escluse, perché runtime-async non è compatibile con WebAssembly e altrimenti causerebbe malfunzionamenti per i consumer Wasm di tali pacchetti.

Poiché runtime-async modifica la modalità di generazione di async/await per una parte estesa dello stack di ASP.NET Core, provare le app in base a questa anteprima e file un problema se si verifica un comportamento imprevisto, in particolare per quanto riguarda gli stack di eccezioni, ExecutionContext/AsyncLocal flow, o qualsiasi elemento simile a una regressione da .NET 10.

Il middleware di limitazione della velocità restituisce header Retry-After corretti

Ora FixedWindowRateLimiter segnala un RetryAfter valore di metadati che riflette in modo accurato il limite della finestra successiva. Le app che propagano questi metadati all'intestazione della Retry-After risposta nel OnRejected callback ora producono automaticamente intervalli di ripetizione corretti, senza che siano necessarie modifiche al codice.

Ulteriori correzioni in System.Threading.RateLimiting risolvono un problema per cui TokenBucketRateLimiter non gestiva correttamente i reintegri parziali dei token durante l'acquisizione di zero permit e migliorano inoltre il limitatore di velocità concatenato restituito da CreateChained in modo che inoltri correttamente la durata di inattività e il comportamento di reintegro dei suoi limitatori interni.

Per una panoramica del middleware di limitazione della frequenza, vedere Rate limit middleware in ASP.NET Core.

Grazie @asbjornvad e @apoorvdarshan per questi contributi!

Kestrel applica i timeout dell'intestazione trailer

Kestrel ora si applica RequestHeadersTimeout agli header trailer HTTP/2 e HTTP/3 frammentati che non completano l'invio del blocco di header. Lo stesso timeout che protegge le intestazioni iniziali della richiesta ora impedisce anche alle connessioni di rimanere aperte indefinitamente mentre Kestrel è in attesa del completamento dei frame trailer HEADERS.

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(10);
});

Modifiche radicali

Usare gli articoli in Modifiche principali in .NET per trovare modifiche irreversibili che potrebbero essere applicate durante l'aggiornamento di un'app a una versione più recente di .NET.