Condividi tramite


Novità di ASP.NET Core in .NET 10

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

Blazor

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

Esempi di sicurezza nuovi e aggiornati Blazor Web App

Sono stati aggiunti e aggiornati gli Blazor Web App esempi di sicurezza collegati negli articoli seguenti:

Tutte le soluzioni di esempio OIDC e Entra includono ora un progetto API Web separato (MinimalApiJwt) per illustrare come configurare e chiamare un'API Web esterna in modo sicuro. La chiamata alle API Web è illustrata con un gestore di token e un client HTTP nominato per un identity provider OIDC o pacchetti API Web di Microsoft per Microsoft Entra ID.

Le soluzioni di esempio sono configurate nel codice C# nei relativi Program file. Per configurare le soluzioni dai file di impostazioni dell'app (ad esempio, appsettings.json) vedere la nuova sezione Specificare la configurazione con il provider di configurazione JSON (impostazioni dell'app) degli articoli OIDC o Entra.

L'articolo Entra e le app di esempio includono anche nuove linee guida sugli approcci seguenti:

parametro QuickGridRowClass

Applicare una classe del foglio di stile a una riga della griglia in base all'elemento di riga utilizzando il nuovo parametro RowClass. Nell'esempio seguente viene chiamato il metodo GetRowCssClass su ogni riga per applicare in modo condizionale una classe del foglio di stile in base all'elemento di riga:

<QuickGrid ... RowClass="GetRowCssClass">
    ...
</QuickGrid>

@code {
    private string GetRowCssClass(MyGridItem item) =>
        item.IsArchived ? "row-archived" : null;
}

Per altre informazioni, vedere componente ASP.NET Core Blazor 'QuickGrid'.

Blazor script come statico asset web

Nelle versioni precedenti di .NET, lo script Blazor viene gestito da una risorsa incorporata nel framework condiviso ASP.NET Core. In .NET 10 o versione successiva, lo script Blazor viene utilizzato come asset Web statico con compressione automatica e impronta digitale.

Lo Blazor script (blazor.web.js o blazor.server.js) è incluso dal framework se il progetto contiene almeno un Razor file di componente (.razor). Se l'app richiede lo Blazor script ma non contiene almeno un componente, aggiungere la proprietà MSBuild seguente al file di progetto dell'app per forzare l'inclusione di script non condizionali:

<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>

Per altre informazioni, vedere le risorse seguenti:

Punti salienti del modello di percorso

L'attributo supporta ora l'evidenziazione [Route] della sintassi di route per visualizzare la struttura del modello di route:

Modello di route di un attributo di route per il valore del contatore mostra l'evidenziazione della sintassi

In precedenza, NavigationManager.NavigateTo scorreva fino alla parte superiore della pagina per le navigazioni sulla stessa pagina. Questo comportamento è stato modificato in .NET 10 in modo che il browser non scorre più fino alla parte superiore della pagina quando si passa alla stessa pagina. Ciò significa che il riquadro di visualizzazione non viene più reimpostato quando si apportano aggiornamenti all'indirizzo per la pagina corrente, ad esempio la modifica della stringa di query o del frammento.

Componente dell'interfaccia utente di riconnessione aggiunto al modello di progetto Blazor Web App

Il modello di progetto Blazor Web App include ora un componente ReconnectModal, incluso il foglio di stile collocato e i file JavaScript, per migliorare il controllo dello sviluppatore sull'interfaccia utente di riconnessione quando il client perde la connessione WebSocket al server. Il componente non inserisce stili programmaticamente, garantendo la conformità alle impostazioni CSP (Content Security Policy) più restrittive per il criterio di style-src. Nelle versioni precedenti, l'interfaccia utente di riconnessione predefinita è stata creata dal framework in modo da causare violazioni CSP. Si noti che l'interfaccia utente di riconnessione predefinita viene ancora usata come fallback quando l'app non definisce l'interfaccia utente di riconnessione, ad esempio usando il componente ReconnectModal del modello di progetto o un componente personalizzato simile.

Nuove funzionalità dell'interfaccia utente di riconnessione:

  • Oltre a indicare lo stato di riconnessione impostando una classe CSS specifica sull'elemento dell'interfaccia utente di riconnessione, il nuovo evento components-reconnect-state-changed viene inviato per le modifiche dello stato di riconnessione.
  • Il codice può distinguere meglio le fasi del processo di riconnessione con il nuovo stato di riconnessione "retrying", indicato sia dalla classe CSS che dal nuovo evento.

Per altre informazioni, vedere ASP.NET Core BlazorSignalR guidance.

Quando si utilizza NavLinkMatch.All, ignorare la stringa di query e il frammento.

Il componente NavLink ignora ora la stringa di query e il frammento quando si usa il valore NavLinkMatch.All per il parametro Match. Ciò significa che il collegamento mantiene la classe active se il percorso URL corrisponde, ma la stringa di query o il frammento cambiano. Per ripristinare il comportamento originale, usare l'opzione Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentAppContext impostata su true.

È anche possibile eseguire l'override del metodo ShouldMatch in NavLink per personalizzare il comportamento di corrispondenza:

public class CustomNavLink : NavLink
{
    protected override bool ShouldMatch(string currentUriAbsolute)
    {
        // Custom matching logic
    }
}

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

Chiudi le opzioni della colonna QuickGrid

È ora possibile chiudere l'interfaccia utente delle opzioni di colonna QuickGrid usando il nuovo metodo di HideColumnOptionsAsync.

L'esempio seguente usa il metodo HideColumnOptionsAsync per chiudere l'interfaccia utente delle opzioni di colonna non appena viene applicato il filtro del titolo:

<QuickGrid @ref="movieGrid" Items="movies">
    <PropertyColumn Property="@(m => m.Title)" Title="Title">
        <ColumnOptions>
            <input type="search" @bind="titleFilter" placeholder="Filter by title" 
                @bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
        </ColumnOptions>
    </PropertyColumn>
    <PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
    <PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>

@code {
    private QuickGrid<Movie>? movieGrid;
    private string titleFilter = string.Empty;
    private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
    private IQueryable<Movie> filteredMovies => 
        movies.Where(m => m.Title!.Contains(titleFilter));
}

Lo streaming delle risposte HttpClient è abilitato per impostazione predefinita

Nelle versioni precedenti Blazor, lo streaming delle risposte per le richieste HttpClient era opzionale. Ora, lo streaming delle risposte è abilitato per impostazione predefinita.

Si tratta di una modifica che causa un'interruzione perché la chiamata HttpContent.ReadAsStreamAsync a un HttpResponseMessage.Content (response.Content.ReadAsStreamAsync()) restituisce BrowserHttpReadStream e non più un MemoryStream. BrowserHttpReadStream non supporta operazioni sincrone, ad esempio Stream.Read(Span<Byte>). Se il codice utilizza operazioni sincrone, è possibile evitare lo streaming delle risposte o copiare il Stream in un MemoryStream.

Per rifiutare esplicitamente lo streaming di risposte a livello globale, usare uno degli approcci seguenti:

  • Aggiungere la <WasmEnableStreamingResponse> proprietà al file di progetto con il valore false:

    <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
    
  • Impostare la DOTNET_WASM_ENABLE_STREAMING_RESPONSE variabile di ambiente su false o 0.

Per disattivare il flusso di risposte per una singola richiesta, impostare SetBrowserResponseStreamingEnabled su false nel HttpRequestMessage (requestMessage nell'esempio seguente):

requestMessage.SetBrowserResponseStreamingEnabled(false);

Per altre informazioni, vedere HttpClient e HttpRequestMessage con le opzioni di richiesta dell'API Fetch (articolo Chiamare l'API Web).

Impronta digitale lato client

Il rilascio di .NET 9 ha introdotto fingerprinting lato server di asset statici nei Blazor Web Apps con l'introduzione delle convenzioni degli endpoint di routing Map Static Assets (MapStaticAssets), il componente ImportMap e la proprietà ComponentBase.Assets (@Assets["..."]) per risolvere i moduli JavaScript con impronta digitale (JS). Per .NET 10, puoi scegliere di abilitare l'impronta digitale lato client dei moduli JS per le app autonome Blazor WebAssembly.

Nelle app autonome Blazor WebAssembly durante la compilazione e la pubblicazione, il framework sostituisce i segnaposto in index.html con i valori calcolati durante la compilazione per generare un'impronta digitale degli asset statici. Un'impronta digitale viene inserita nel nome del blazor.webassembly.js file di script.

Il markup seguente deve essere presente nel wwwroot/index.html file per adottare la funzionalità di impronta digitale:

<head>
    ...
+   <script type="importmap"></script>
</head>

<body>
    ...
-   <script src="_framework/blazor.webassembly.js"></script>
+   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>

</html>

Nel file di progetto (.csproj) aggiungere la <OverrideHtmlAssetPlaceholders> proprietà impostata su true:

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
+   <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
  </PropertyGroup>
</Project>

Nell'esempio seguente, tutti i file forniti dallo sviluppatore sono moduli con l'estensione del file JS.

Un modulo denominato scripts.js nella cartella wwwroot/js dell'app viene dotato di impronta digitale aggiungendo #[.{fingerprint}] prima dell'estensione del file (.js).

<script type="module" src="js/scripts#[.{fingerprint}].js"></script>

Specificare l'espressione di impronta digitale con la <StaticWebAssetFingerprintPattern> proprietà nel file di progetto dell'app (.csproj):

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

Qualsiasi JS file (*.js) in index.html con il marcatore di impronta digitale viene improntato dal framework, incluso quando l'app viene pubblicata.

Se adotti l'estensione di file .mjs per i moduli JS, imposta l'estensione di file utilizzando il parametro Pattern:

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

I file vengono inseriti nella mappa di importazione:

  • Automaticamente per Blazor Web App il rendering lato cliente (CSR).
  • Quando si sceglie di attivare l'impronta digitale dei moduli nelle app autonome Blazor WebAssembly seguendo le istruzioni precedenti.

Quando si risolve l'importazione per l'interoperabilità JavaScript, la mappa di importazione viene usata dal browser per risolvere i file con impronta digitale.

Asset statici del framework precaricati Blazor

In Blazor Web Apps, gli asset statici del framework vengono precaricati automaticamente tramite Link intestazioni, il che consente al browser di precaricare le risorse prima che la pagina iniziale venga acquisita e sottoposta a rendering.

Nelle app autonome Blazor WebAssembly , gli asset del framework sono pianificati per il download e la memorizzazione nella cache con priorità elevata durante l'elaborazione delle pagine del browser index.html quando:

  • La OverrideHtmlAssetPlaceholders proprietà MSBuild nel file di progetto dell'app (.csproj) è impostata su true:

    <PropertyGroup>
      <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
    </PropertyGroup>
    
  • L'elemento seguente <link> che contiene rel="preload" è presente nel <head> contenuto di wwwroot/index.html:

    <link rel="preload" id="webassembly" />
    

Per altre informazioni, vedere ASP.NET Core Blazor file statici.

Configurare l'ambiente nelle app autonome Blazor WebAssembly

Il Properties/launchSettings.json file non viene più usato per controllare l'ambiente nelle app autonome Blazor WebAssembly .

A partire da .NET 10, impostare l'ambiente con la proprietà <WasmApplicationEnvironmentName> nel file di progetto dell'app (.csproj).

L'esempio seguente imposta l'ambiente dell'app su Staging:

<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>

Gli ambienti predefiniti sono:

  • Development per la compilazione.
  • Production per la pubblicazione.

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

File di configurazione di avvio integrato

La configurazione di avvio di Blazor, che prima del rilascio di .NET 10 esisteva in un file denominato blazor.boot.json, è stata inserita nello script dotnet.js. Ciò influisce solo sugli sviluppatori che interagiscono direttamente con il blazor.boot.json file, ad esempio quando gli sviluppatori sono:

Attualmente non esiste una strategia di sostituzione documentata per gli approcci precedenti. Se è necessaria una delle strategie precedenti, aprire un nuovo problema di documentazione che descrive lo scenario usando il collegamento Apri un problema di documentazione nella parte inferiore di uno di questi articoli.

Modello dichiarativo per la persistenza dello stato dei componenti e dei servizi

È ora possibile specificare in modo dichiarativo lo stato da rendere persistente dai componenti e dai servizi usando l'attributo [PersistentState] . Le proprietà con questo attributo vengono memorizzate automaticamente usando il servizio PersistentComponentState durante il prerendering. Lo stato viene recuperato quando il componente esegue il rendering interattivo o viene creata un'istanza del servizio.

Nelle versioni precedenti Blazor, la persistenza dello stato del componente durante il prerendering utilizzando il servizio PersistentComponentState comportava una notevole quantità di codice, come dimostrato nel seguente esempio:

@page "/movies"
@implements IDisposable
@inject IMovieService MovieService
@inject PersistentComponentState ApplicationState

@if (MoviesList == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <QuickGrid Items="MoviesList.AsQueryable()">
        ...
    </QuickGrid>
}

@code {
    public List<Movie>? MoviesList { get; set; }
    private PersistingComponentStateSubscription? persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList), 
            out var movies))
        {
            MoviesList = await MovieService.GetMoviesAsync();
        }
        else
        {
            MoviesList = movies;
        }

        persistingSubscription = ApplicationState.RegisterOnPersisting(() =>
        {
            ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
            return Task.CompletedTask;
        });
    }

    public void Dispose() => persistingSubscription?.Dispose();
}

Questo codice può ora essere semplificato usando il nuovo modello dichiarativo:

@page "/movies"
@inject IMovieService MovieService

@if (MoviesList == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <QuickGrid Items="MoviesList.AsQueryable()">
        ...
    </QuickGrid>
}

@code {
    [PersistentState]
    public List<Movie>? MoviesList { get; set; }

    protected override async Task OnInitializedAsync()
    {
        MoviesList ??= await MovieService.GetMoviesAsync();
    }
}

Lo stato può essere serializzato per più componenti dello stesso tipo ed è possibile stabilire lo stato dichiarativo in un servizio per l'uso nell'app chiamando RegisterPersistentService sul Razor generatore di componenti (AddRazorComponents) con un tipo di servizio personalizzato e la modalità di rendering. Per altre informazioni, vedere ASP.NET Core Blazor persistenza dello stato prerenderizzato.

Nuove funzionalità di interoperabilità JavaScript

Blazor aggiunge il supporto per le funzionalità di interoperabilità seguenti JS :

  • Creare un'istanza di un oggetto JS usando una funzione del costruttore e ottenere l'handle /IJSObjectReference/IJSInProcessObjectReference .NET per fare riferimento all'istanza.
  • Consente di leggere o modificare il valore di una JS proprietà dell'oggetto, comprese le proprietà di dati e accessor.

I metodi asincroni seguenti sono disponibili in IJSRuntime e IJSObjectReference con lo stesso comportamento di ambito del metodo esistente IJSRuntime.InvokeAsync :

  • InvokeConstructorAsync(string identifier, object?[]? args): richiama la funzione del costruttore specificata JS in modo asincrono. La funzione viene richiamata con l'operatore new . Nell'esempio seguente è jsInterop.TestClass una classe con una funzione del costruttore ed classRef è un oggetto IJSObjectReference:

    var classRef = await JSRuntime.InvokeConstructorAsync("jsInterop.TestClass", "Blazor!");
    var text = await classRef.GetValueAsync<string>("text");
    var textLength = await classRef.InvokeAsync<int>("getTextLength");
    
  • GetValueAsync<TValue>(string identifier): legge il valore della proprietà specificata JS in modo asincrono. La proprietà non può essere solo una proprietà set. Se JSException la proprietà non esiste, viene generata un'eccezione . Nell'esempio seguente viene restituito un valore da una proprietà di dati:

    var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>(
      "jsInterop.testObject.num");
    
  • SetValueAsync<TValue>(string identifier, TValue value): aggiorna il valore della proprietà specificata JS in modo asincrono. La proprietà non può essere solo una proprietà get. Se la proprietà non è definita nell'oggetto di destinazione, viene creata la proprietà . Viene JSException generata un'eccezione se la proprietà esiste ma non è scrivibile o quando non è possibile aggiungere una nuova proprietà all'oggetto . Nell'esempio seguente, se non esiste, viene creato num su testObject con un valore pari a 30.

    await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
    

Gli overload sono disponibili per ognuno dei metodi precedenti che accettano un CancellationToken argomento o un argomento di timeout TimeSpan.

I metodi sincroni seguenti sono disponibili in IJSInProcessRuntime e IJSInProcessObjectReference con lo stesso comportamento di ambito del metodo esistente IJSInProcessObjectReference.Invoke :

  • InvokeConstructor(string identifier, object?[]? args): richiama in modo sincrono la funzione del costruttore specificata JS . La funzione viene richiamata con l'operatore new . Nell'esempio seguente è jsInterop.TestClass una classe con una funzione del costruttore ed classRef è un oggetto IJSInProcessObjectReference:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    var classRef = inProcRuntime.InvokeConstructor("jsInterop.TestClass", "Blazor!");
    var text = classRef.GetValue<string>("text");
    var textLength = classRef.Invoke<int>("getTextLength");
    
  • GetValue<TValue>(string identifier): legge il valore della proprietà specificata JS in modo sincrono. La proprietà non può essere solo una proprietà set. Se JSException la proprietà non esiste, viene generata un'eccezione . Nell'esempio seguente viene restituito un valore da una proprietà di dati:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    var valueFromDataProperty = inProcRuntime.GetValue<int>(
      "jsInterop.testObject.num");
    
  • SetValue<TValue>(string identifier, TValue value): aggiorna in modo sincrono il valore della proprietà specificata JS . La proprietà non può essere solo una proprietà get. Se la proprietà non è definita nell'oggetto di destinazione, viene creata la proprietà . Viene JSException generata un'eccezione se la proprietà esiste ma non è scrivibile o quando non è possibile aggiungere una nuova proprietà all'oggetto . Nell'esempio seguente, num viene creato su testObject con un valore di 20 se non esiste:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    inProcRuntime.SetValue("jsInterop.testObject.num", 20);
    

Per altre informazioni, vedere le seguenti sezioni dell'articolo Chiamare le funzioni JavaScript dai metodi .NET:

Blazor WebAssembly profilatura delle prestazioni e contatori di diagnostica

Sono disponibili nuovi contatori di diagnostica e profilatura delle prestazioni per Blazor WebAssembly le app. Per altre informazioni, vedere gli articoli seguenti:

Optare per evitare un NavigationException durante il rendering statico lato server con NavigationManager.NavigateTo

La chiamata NavigationManager.NavigateTo durante il rendering statico lato server genera un'interruzione NavigationExceptiondell'esecuzione prima di essere convertita in una risposta di reindirizzamento. Ciò può causare confusione durante il debugging e non è coerente con il comportamento di rendering interattivo, dove il codice dopo NavigateTo continua a essere eseguito normalmente.

In .NET 10 è possibile impostare la proprietà <BlazorDisableThrowNavigationException> MSBuild su true nel file di progetto dell'app per evitare di generare l'eccezione durante la ssr statica:

<PropertyGroup>
  <BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>

Con la proprietà MSBuild impostata, la chiamata NavigationManager.NavigateTo durante SSR statico non genera più un'eccezione NavigationException. Si comporta invece in modo coerente con il rendering interattivo eseguendo la navigazione senza generare un'eccezione. Il codice dopo NavigationManager.NavigateTo viene eseguito prima che si verifichi il reindirizzamento.

Il modello di progetto .NET 10 Blazor Web App imposta la proprietà MSBuild su true per impostazione predefinita. È consigliabile che le app che si aggiornino a .NET 10 usino la nuova proprietà MSBuild ed evitare il comportamento precedente.

Se viene utilizzata la proprietà MSBuild, è necessario aggiornare il codice che si basa sul sollevamento di NavigationException. Nell'interfaccia utente predefinita BlazorIdentity del modello di progetto Blazor Web App prima del rilascio di .NET 10, il IdentityRedirectManager genera un InvalidOperationException dopo aver chiamato RedirectTo per assicurarsi che il metodo non sia stato richiamato durante il rendering interattivo. Questa eccezione e gli [DoesNotReturn] attributi devono ora essere rimossi quando viene usata la proprietà MSBuild. Per altre informazioni, vedere Migrare da ASP.NET Core in .NET 9 a ASP.NET Core in .NET 10.

Blazor router ha un NotFoundPage parametro

Blazor ora offre un modo migliorato per visualizzare una pagina "Non trovata" quando si passa a una pagina inesistente. È possibile specificare una pagina di cui eseguire il rendering quando NavigationManager.NotFound (descritto nella sezione successiva) viene richiamato passando un tipo di pagina al Router componente usando il NotFoundPage parametro . La funzionalità supporta il routing, funziona attraverso il middleware di riesecuzione delle pagine di codici di stato ed è compatibile anche con scenari non Blazor.

Il frammento di rendering NotFound (<NotFound>...</NotFound>) non è supportato in .NET 10 o versione successiva.

<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>

Il modello di Blazor progetto include ora una NotFound.razor pagina per impostazione predefinita. Questa pagina esegue automaticamente il rendering ogni volta che NotFound viene chiamato nella tua app, rendendo più facile la gestione delle route mancanti con un'esperienza utente coerente.

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

Risposte non trovate con NavigationManager per il rendering statico di SSR e interattivo globale

Il NavigationManager ora include un NotFound metodo per gestire gli scenari durante il rendering statico lato server (SSR statico) o il rendering interattivo globale in cui non viene trovata una risorsa richiesta.

  • Rendering statico lato server (Static SSR): la chiamata a NotFound imposta il codice di stato HTTP su 404.

  • Rendering interattivo: segnala al router (Blazor) di eseguire il Router rendering del contenuto non trovato.

  • Rendering streaming: Se la navigazione avanzata è attiva, il rendering streaming esegue il rendering del contenuto Non trovato senza ricaricare la pagina. Quando la navigazione avanzata è bloccata, il framework reindirizza al contenuto Non trovato con un aggiornamento della pagina.

Il rendering dello streaming può eseguire il rendering solo di componenti che hanno una route, come una mappaturaNotFoundPage (NotFoundPage="...") o una impostazione della pagina di riesecuzione del codice di stato middleware (UseStatusCodePagesWithReExecute). DefaultNotFound Il contenuto 404 ("Not found" testo normale) non ha una route, quindi non può essere usato durante il rendering dello streaming.

NotFound il rendering del contenuto usa quanto segue, indipendentemente dal fatto che la risposta sia stata avviata o meno (in ordine):

  • Se NotFoundEventArgs.Path è impostato, eseguire il rendering del contenuto della pagina assegnata.
  • Se Router.NotFoundPage è impostato, eseguire il rendering della pagina assegnata.
  • Pagina Middleware di ripetizione dell'esecuzione delle tabelle codici di stato, se configurata.
  • Nessuna azione se non viene adottato nessuno degli approcci precedenti.

Il middleware di riesecuzione delle pagine di codice di stato con UseStatusCodePagesWithReExecute ha la precedenza per i problemi di routing degli indirizzi basati su browser, come un URL non corretto digitato nella barra degli indirizzi del browser o la selezione di un collegamento senza endpoint nell'app.

È possibile usare l'evento NavigationManager.OnNotFound per le notifiche quando NotFound viene richiamato.

Per altre informazioni ed esempi, vedere ASP.NET Core Blazor navigation.

Supporto per le risposte "Not Found" nelle app senza il router di Blazor

Le app che implementano un router personalizzato possono usare NotFound. Esistono due modi per informare il renderer su quale pagina deve essere resa quando NotFound viene chiamato.

L'approccio consigliato funziona indipendentemente dallo stato della risposta consiste nel chiamare UseStatusCodePagesWithReExecute. Quando NotFound viene chiamato, il middleware esegue il rendering del percorso passato al metodo :

app.UseStatusCodePagesWithReExecute(
    "/not-found", createScopeForStatusCodePages: true);

Se non si vuole usare UseStatusCodePagesWithReExecute, l'app può comunque supportare NotFound le risposte già avviate. Iscriviti a OnNotFoundEvent nel router e assegna il percorso della pagina Non trovato a NotFoundEventArgs.Path per informare il renderer quale contenuto eseguire quando NotFound viene chiamato.

CustomRouter.razor:

@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Http
@implements IDisposable
@inject NavigationManager NavigationManager

@code {
    protected override void OnInitialized() =>
        NavigationManager.OnNotFound += OnNotFoundEvent;

    [CascadingParameter]
    public HttpContext? HttpContext { get; set; }

    private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
    {
        // Only execute the logic if HTTP response has started
        // because setting NotFoundEventArgs.Path blocks re-execution
        if (HttpContext?.Response.HasStarted == false)
        {
            return;
        }

        e.Path = GetNotFoundRoutePath();
    }

    // Return the path of the Not Found page that you want to display
    private string GetNotFoundRoutePath()
    {
        ...
    }

    public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
}

Se utilizzi entrambi gli approcci nella tua app, il percorso "Non trovato" specificato nel gestore OnNotFoundEvent ha la precedenza sul percorso configurato nel middleware per la riesecuzione.

Metriche e tracciamento

Questa versione introduce metriche complete e funzionalità di traccia per Blazor le app, offrendo un'osservabilità dettagliata del ciclo di vita dei componenti, la navigazione, la gestione degli eventi e la gestione dei circuiti.

Per altre informazioni, vedere ASP.NET Core Blazor procedure consigliate per le prestazioni.

Supporto del bundler JavaScript

BlazorL'output di compilazione non è compatibile con i bundler JavaScript, ad esempio Gulp, Webpack e Rollup. Blazor può ora produrre output compatibile con il bundler durante la pubblicazione impostando la proprietà MSBuild WasmBundlerFriendlyBootConfig su true.

Per altre informazioni, vedere Host e distribuire ASP.NET Core Blazor.

Blazor WebAssembly precaricamento delle risorse statiche in Blazor Web Apps

Abbiamo sostituito <link> le intestazioni con un componente ResourcePreloader (<ResourcePreloader />) per il precaricamento degli asset WebAssembly in Blazor Web App. Ciò consente alla configurazione del percorso di base dell'app (<base href="..." />) di identificare correttamente la radice dell'app.

La rimozione del componente disabilita la funzionalità se l'app usa un loadBootResource callback per modificare gli URL.

Il modello Blazor Web App adotta la funzionalità per impostazione predefinita in .NET 10 e le app che si aggiornano a .NET 10 possono implementare la funzionalità inserendo il componente ResourcePreloader dopo il tag URL di base (<base>) nel contenuto head del componente App (App.razor):

<head>
    ...
    <base href="/" />
+   <ResourcePreloader />
    ...
</head>

Per ulteriori informazioni, vedere Ospitare e distribuire app ASP.NET Core lato server Blazor.

Convalida dei moduli migliorata

Blazor sono state ora migliorate le funzionalità di convalida dei moduli, incluso il supporto per la convalida delle proprietà degli oggetti annidati e degli elementi della raccolta.

Per creare un modulo convalidato, usare un DataAnnotationsValidator componente all'interno di un EditForm componente, esattamente come in precedenza.

Per acconsentire esplicitamente alla nuova funzionalità di convalida:

  1. Chiamare il metodo di estensione AddValidation nel file Program in cui vengono registrati i servizi.
  2. Dichiarare i tipi di modello di modulo in un file di classe C#, non in un Razor componente (.razor).
  3. Annotare il tipo di modello di modulo radice con l'attributo [ValidatableType] .

Senza seguire i passaggi precedenti, il comportamento di convalida rimane uguale a quello delle versioni precedenti .NET.

L'esempio seguente illustra gli ordini dei clienti con la convalida migliorata del modulo (dettagli omessi per brevità):

In Program.cs chiama AddValidation nella raccolta di servizi per abilitare il nuovo comportamento di convalida:

builder.Services.AddValidation();

Nella classe seguente Order , l'attributo [ValidatableType] è obbligatorio nel tipo di modello di primo livello. Gli altri tipi vengono individuati automaticamente. OrderItem e ShippingAddress non vengono visualizzati per brevità, ma la convalida nidificata e delle raccolte funziona allo stesso modo in tali tipi, se fossero stati mostrati.

Order.cs:

[ValidatableType]
public class Order
{
    public Customer Customer { get; set; } = new();
    public List<OrderItem> OrderItems { get; set; } = [];
}

public class Customer
{
    [Required(ErrorMessage = "Name is required.")]
    public string? FullName { get; set; }

    [Required(ErrorMessage = "Email is required.")]
    public string? Email { get; set; }

    public ShippingAddress ShippingAddress { get; set; } = new();
}

Nel seguente componente OrderPage, il componente DataAnnotationsValidator è presente nel componente EditForm.

OrderPage.razor:

<EditForm Model="Model">
    <DataAnnotationsValidator />

    <h3>Customer Details</h3>
    <div class="mb-3">
        <label>
            Full Name
            <InputText @bind-Value="Model!.Customer.FullName" />
        </label>
        <ValidationMessage For="@(() => Model!.Customer.FullName)" />
    </div>

    @* ... form continues ... *@
</EditForm>

@code {
    public Order? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    // ... code continues ...
}

Il requisito di dichiarare i tipi di modello al di fuori dei Razor componenti (.razor file) è dovuto al fatto che sia la nuova funzionalità di convalida che il Razor compilatore stesso usano un generatore di origine. Attualmente, l'output di un generatore di origine non può essere usato come input per un altro generatore di origine.

Il supporto per la convalida include ora:

  • La convalida di oggetti complessi e raccolte annidate è ora supportata.
    • Sono incluse le regole di convalida definite dagli attributi della proprietà, dagli attributi della classe e dall'implementazione IValidatableObject .
    • L'attributo [SkipValidation] può escludere proprietà o tipi dalla convalida.
  • La convalida ora utilizza un'implementazione basata su un generatore di codice sorgente piuttosto che su un'implementazione basata su riflessione per migliorare le prestazioni e la compatibilità con la compilazione AOT (Ahead-of-Time).

Il DataAnnotationsValidator componente ha ora lo stesso ordine di convalida e lo stesso comportamento di corto circuito di System.ComponentModel.DataAnnotations.Validator. Quando si convalida un'istanza di tipo Tvengono applicate le regole seguenti:

  1. Le proprietà membro di T vengono convalidate, inclusa la convalida ricorsiva degli oggetti annidati.
  2. Gli attributi a livello di tipo di T vengono convalidati.
  3. Il IValidatableObject.Validate metodo viene eseguito, se T lo implementa.

Se uno dei passaggi precedenti genera un errore di convalida, i passaggi rimanenti vengono ignorati.

Usare modelli di convalida da un assembly diverso

È possibile convalidare i form con modelli definiti in un assembly diverso, ad esempio una libreria o il .Clientprogetto di unBlazor Web Appoggetto, creando un metodo nella libreria o nel progetto .Client che riceve come argomento un'istanza IServiceCollection e chiama AddValidation su di essa.

  • Nell'app chiamare sia il metodo che AddValidation.

Per altre informazioni e un esempio, vedere ASP.NET Core Blazor form validation.

Cache personalizzata Blazor e BlazorCacheBootResources proprietà MSBuild rimosse

Ora che tutti i Blazor file lato client sono dotati di fingerprint e cachati dal browser, il meccanismo di caching personalizzato di Blazor e la proprietà MSBuild di BlazorCacheBootResources sono stati rimossi dal framework. Se il file di progetto sul lato client contiene la proprietà MSBuild, rimuovere la proprietà , perché non ha più alcun effetto:

- <BlazorCacheBootResources>...</BlazorCacheBootResources>

Per altre informazioni, vedere ASP.NET Core Blazor WebAssembly errori di memorizzazione nella cache e controllo dell'integrità.

Supporto dell'API autenticazione Web (passkey) per ASP.NET Core Identity

Il supporto dell'API Autenticazione Web (WebAuthn), noto ampiamente come passkey, è un metodo di autenticazione moderno e resistente al phishing che migliora la sicurezza e l'esperienza utente sfruttando la crittografia a chiave pubblica e l'autenticazione basata su dispositivo. ASP.NET Core Identity supporta ora l'autenticazione passkey basata su standard WebAuthn e FIDO2. Questa funzionalità consente agli utenti di accedere senza password, usando metodi di autenticazione sicuri e basati su dispositivo, ad esempio biometria o chiavi di sicurezza.

Il Blazor Web App modello di progetto fornisce funzionalità predefinite per la gestione passkey e l'accesso.

Per altre informazioni, vedere gli articoli seguenti:

Persistenza dello stato del circuito

Durante il rendering lato server, Blazor Web Apps può ora mantenere lo stato di sessione (circuito) di un utente quando la connessione al server viene persa per un lungo periodo di tempo o sospesa in modo proattivo, purché non venga attivato un aggiornamento a pagina intera. In questo modo gli utenti possono riprendere la sessione senza perdere il lavoro non salvato negli scenari seguenti:

  • Gestione delle schede del browser
  • Utenti di dispositivi mobili che cambiano app
  • Interruzioni di rete
  • Gestione proattiva delle risorse (sospensione di circuiti inattivi)
  • Spostamento avanzato

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

Ricaricamento rapido per Blazor WebAssembly e .NET in WebAssembly

L'SDK è stato migrato a un Ricaricamento rapido di uso generale per gli scenari WebAssembly. È disponibile una nuova proprietà MSBuild WasmEnableHotReload, che è true per impostazione predefinita nella configurazione Debug (Configuration == "Debug"), che abilita Ricaricamento rapido.

Per altre configurazioni con nomi di configurazione personalizzati, impostare il valore su true nel file di progetto dell'app per abilitare Ricaricamento rapido:

<PropertyGroup>
  <WasmEnableHotReload>true</WasmEnableHotReload>
</PropertyGroup>

Per disabilitare Ricaricamento rapido per la configurazione Debug, impostare il valore su false:

<PropertyGroup>
  <WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>

Aggiornamento della registrazione del service worker PWA per evitare problemi di cache

La registrazione del service worker nel modello di progetto di Applicazione Web Progressiva (PWA) ora include l'opzione, che impedisce problemi di memorizzazione nella cache durante gli aggiornamenti del service worker.

- navigator.serviceWorker.register('service-worker.js');
+ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });

L'opzione garantisce che:

  • Il browser non usa versioni memorizzate nella cache dello script di lavoro del servizio.
  • Gli aggiornamenti dei service worker vengono applicati in modo affidabile senza essere bloccati dalla cache HTTP.
  • Le applicazioni PWA possono aggiornare i propri service worker in modo più prevedibile.

Questo risolve i problemi di cache che possono impedire il corretto aggiornamento dei service worker, particolarmente importante per le PWA che si basano sui service worker per le funzionalità offline.

È consigliabile usare l'opzione impostata su none in tutte le PWA, incluse quelle destinate a .NET 9 o versioni precedenti.

Estendibilità della serializzazione per lo stato del componente persistente

Implementare un serializzatore personalizzato con PersistentComponentStateSerializer<T>. Senza un serializzatore personalizzato registrato, la serializzazione ripiega sulla serializzazione JSON già esistente.

Il serializzatore personalizzato viene registrato nel file dell'app Program . Nell'esempio seguente, viene registrato per il tipo :

builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>, 
    CustomUserSerializer>();

Il tipo viene salvato in modo permanente e ripristinato automaticamente con il serializzatore personalizzato:

[PersistentState] 
public User? CurrentUser { get; set; } = new();

OwningComponentBase ora implementa IAsyncDisposable

OwningComponentBase include ora il supporto per l'eliminazione asincrona, migliorando la gestione delle risorse. Sono disponibili nuovi metodi DisposeAsync e DisposeAsyncCore con un metodo aggiornato Dispose per gestire sia lo smaltimento sincrono che asincrono dell'ambito del servizio.

Nuovo InputHidden componente per gestire i campi di input nascosti nei moduli

Il nuovo InputHidden componente fornisce un campo di input nascosto per l'archiviazione dei valori stringa.

Nell'esempio seguente viene creato un campo di input nascosto per la proprietà del Parameter modulo. Quando viene inviato il modulo, viene visualizzato il valore del campo nascosto:

<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="InputHidden Example">
    <InputHidden id="hidden" @bind-Value="Parameter" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Parameter!</p>
}

@code {
    private bool submitted;

    [SupplyParameterFromForm] 
    public string Parameter { get; set; } = "stranger";

    private void Submit() => submitted = true;
}

Supporto dello stato persistente del componente per la navigazione avanzata

Blazor supporta ora la gestione dello stato del componente persistente durante la navigazione avanzata. Lo stato persistente durante la navigazione avanzata può essere letto dai componenti interattivi presenti nella pagina.

Per impostazione predefinita, lo stato del componente persistente viene caricato solo dai componenti interattivi quando vengono inizialmente caricati nella pagina. Ciò impedisce che lo stato importante, ad esempio i dati in una webform modificata, venga sovrascritto se si verificano eventi di spostamento avanzati aggiuntivi nella stessa pagina dopo il caricamento del componente.

Se i dati sono di sola lettura e non cambiano di frequente, acconsentire esplicitamente per consentire gli aggiornamenti durante la navigazione avanzata impostando AllowUpdates = truesull'attributo[PersistentState] . Ciò è utile per scenari come la visualizzazione di dati memorizzati nella cache costosi da recuperare, ma non cambia spesso. L'esempio seguente illustra l'uso di AllowUpdates per i dati delle previsioni meteo.

[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }

protected override async Task OnInitializedAsync()
{
    Forecasts ??= await ForecastService.GetForecastAsync();
}

Per ignorare il ripristino dello stato durante il pre-rendering, impostare RestoreBehavior su SkipInitialValue:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }

Per ignorare il ripristino dello stato durante la riconnessione, impostare RestoreBehavior su SkipLastSnapshot. Ciò può essere utile per garantire i dati aggiornati dopo la riconnessione:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }

Chiamare PersistentComponentState.RegisterOnRestoring per registrare un callback per controllare in modo imperativo il modo in cui viene ripristinato lo stato, in modo analogo a come PersistentComponentState.RegisterOnPersisting fornisce il controllo completo della modalità di persistenza dello stato.

Blazor WebAssembly rispetta l'impostazione attuale della cultura dell'interfaccia utente

In .NET 9 o versioni precedenti, le app autonome Blazor WebAssembly caricano le risorse di globalizzazione dell'interfaccia utente in base a CultureInfo.DefaultThreadCurrentCulture. Se desideri caricare anche i dati di globalizzazione per la cultura di localizzazione definita da CultureInfo.DefaultThreadCurrentUICulture, aggiorna l'app a .NET 10 o versione successiva.

Blazor Hybrid

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

Nuovo .NET MAUIBlazor Hybrid con un articolo Blazor Web App e ASP.NET Core Identity ed esempio

È stato aggiunto un nuovo articolo e un'app di esempio per .NET MAUIBlazor Hybrid e app Web usando ASP.NET Core Identity.

Per altre informazioni, vedere le risorse seguenti:

SignalR

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

API minimali

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

Trattare le stringhe vuote nell'invio del modulo come null per i tipi di valore nullable

Quando si usa l'attributo [FromForm] con un oggetto complesso in API Minimali, i valori stringa vuoti in un invio del modulo vengono ora convertiti in null anziché causare un fallimento dell'analisi. Questo comportamento corrisponde alla logica di elaborazione per i form non associati a oggetti complessi nelle Minimal API.

using Microsoft.AspNetCore.Http;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/todo", ([FromForm] Todo todo) => TypedResults.Ok(todo));

app.Run();

public class Todo
{
  public int Id { get; set; }
  public DateOnly? DueDate { get; set; } // Empty strings map to `null`
  public string Title { get; set; }
  public bool IsCompleted { get; set; }
}

Grazie a @nvmkpk per contribuire a questo cambiamento!

Supporto della convalida nelle API minime

È ora disponibile il supporto per la convalida nelle API minime. Questa funzionalità consente di richiedere la convalida dei dati inviati agli endpoint API. L'abilitazione della convalida consente al runtime di ASP.NET Core di eseguire tutte le convalide definite in :

  • Query
  • Header
  • Testo della richiesta

Le validazioni vengono definite usando attributi nello spazio dei nomi DataAnnotations. Gli sviluppatori personalizzano il comportamento del sistema di convalida in base a:

  • Creazione di implementazioni di attributi personalizzati [Validation] .
  • Implementazione dell'IValidatableObject interfaccia per la logica di convalida complessa.

Se la convalida ha esito negativo, il runtime restituisce una risposta di richiesta non valida 400 con i dettagli degli errori di convalida.

Abilitare il supporto di convalida predefinito per le API minime

Abilitare il supporto di convalida predefinito per le API minime chiamando il AddValidation metodo di estensione per registrare i servizi necessari nel contenitore di servizi per l'applicazione:

builder.Services.AddValidation();

L'implementazione individua automaticamente i tipi definiti nei gestori API minimi o come tipi di base definiti nei gestori API minimi. Un filtro per gli endpoint esegue la convalida su questi tipi ed è aggiunto per ciascun endpoint.

La convalida può essere disabilitata per endpoint specifici usando il DisableValidation metodo di estensione, come nell'esempio seguente:

app.MapPost("/products",
    ([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
        => TypedResults.Ok(productId))
    .DisableValidation();

Note

Sono stati apportati alcuni piccoli miglioramenti e correzioni al generatore di convalida delle API minime introdotto in ASP.NET Core per .NET 10. Per supportare miglioramenti futuri, le API del resolver di convalida sottostanti sono ora contrassegnate come sperimentali. Le API di primo livello AddValidation e il filtro di convalida predefinito rimangono stabili e non sperimentali.

Validazione dei tipi di record

Le API minime supportano anche la convalida con i tipi di record C#. I tipi di record possono essere convalidati usando attributi dello spazio dei nomi System.ComponentModel.DataAnnotations, in modo simile alle classi. Per esempio:

public record Product(
    [Required] string Name,
    [Range(1, 1000)] int Quantity);

Quando si usano tipi di record come parametri negli endpoint API minimi, gli attributi di convalida vengono applicati automaticamente nello stesso modo dei tipi di classe:

app.MapPost("/products", (Product product) =>
{
    // Endpoint logic here
    return TypedResults.Ok(product);
});

Integrazione minima della convalida API con IProblemDetailsService

Le risposte di errore dalla logica di convalida per le API minime possono ora essere personalizzate da un'implementazione IProblemDetailsService fornita nella raccolta di servizi dell'applicazione (contenitore per l'iniezione delle dipendenze). Ciò consente risposte di errore più coerenti e specifiche dell'utente.

Supporto per eventi Server-Sent (SSE)

ASP.NET Core supporta ora la restituzione di un risultato ServerSentEvents usando l'API TypedResults.ServerSentEvents. Questa funzionalità è supportata sia nelle API minime che nelle app basate su controller.

Server-Sent Eventi è una tecnologia push server che consente a un server di inviare un flusso di messaggi di evento a un client tramite una singola connessione HTTP. In .NET i messaggi di evento sono rappresentati come oggetti SseItem<T>, che possono contenere un tipo di evento, un ID e un payload di dati di tipo T.

La classe TypedResults include un nuovo metodo statico denominato ServerSentEvents che può essere usato per restituire un risultato ServerSentEvents. Il primo parametro di questo metodo è un oggetto IAsyncEnumerable<SseItem<T>> che rappresenta il flusso di messaggi di evento da inviare al client.

L'esempio seguente illustra come usare l'API TypedResults.ServerSentEvents per restituire un flusso di eventi di frequenza cardiaca come oggetti JSON al client:

app.MapGet("/json-item", (CancellationToken cancellationToken) =>
{
    async IAsyncEnumerable<HeartRateRecord> GetHeartRate(
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var heartRate = Random.Shared.Next(60, 100);
            yield return HeartRateRecord.Create(heartRate);
            await Task.Delay(2000, cancellationToken);
        }
    }

    return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken),
                                                  eventType: "heartRate");
});

Per altre informazioni, vedere:

  • Server-Sent Events su MDN.
  • App di esempio API Minima che usa l'API TypedResults.ServerSentEvents per restituire un flusso di eventi di frequenza cardiaca come stringhe, ServerSentEvents e oggetti JSON al client.
  • App di esempio del Controller API che utilizza l'API TypedResults.ServerSentEvents per restituire un flusso di eventi di frequenza cardiaca come stringhe, ServerSentEventse oggetti JSON al client.

Le API di convalida sono state spostate in Microsoft.Extensions.Validation

Le API di convalida sono state spostate nello spazio dei nomi Microsoft.Extensions.Validation e nel pacchetto NuGet. Questa modifica rende le API utilizzabili al di fuori degli scenari HTTP ASP.NET Core. Le API pubbliche e il comportamento rimangono invariati. Solo il pacchetto e lo spazio dei nomi sono diversi. I progetti esistenti non richiedono modifiche al codice, perché i riferimenti precedenti vengono reindirizzati alla nuova implementazione.

Convalida migliorata per classi e record

Gli attributi di convalida possono ora essere applicati sia alle classi che ai record con un comportamento coerente di generazione e convalida del codice. Questo miglioramento migliora la flessibilità durante la progettazione di modelli usando record nelle app ASP.NET Core.

Contributo comunitario: grazie a @marcominerva!

OpenAPI

Questa sezione descrive le nuove funzionalità per OpenAPI.

Supporto di OpenAPI 3.1

ASP.NET Core ha aggiunto il supporto per la generazione di documenti OpenAPI versione 3.1 in .NET 10. Nonostante il piccolo incremento di versione, OpenAPI 3.1 è un aggiornamento significativo della specifica OpenAPI, in particolare con il supporto completo per la bozza 2020-12 dello JSON Schema.

Alcune delle modifiche che verranno visualizzate nel documento OpenAPI generato includono:

  • I tipi nullable non hanno più la proprietà nullable: true nello schema.
  • Anziché una proprietà nullable: true, hanno una parola chiave type il cui valore è una matrice che include null come uno dei tipi.
  • Le proprietà o i parametri definiti come C# int o long ora vengono visualizzati nel documento OpenAPI generato senza il type: integer campo e hanno un pattern campo che limita il valore alle cifre. Ciò si verifica quando la proprietà NumberHandling nella JsonSerializerOptions è impostata su AllowReadingFromString, l'impostazione predefinita per le app Web di ASP.NET Core. Per abilitare la rappresentazione di C# int e long nel documento OpenAPI come type: integer, impostare la proprietà NumberHandling su Strict.

Con questa funzionalità, la versione OpenAPI predefinita per i documenti generati è3.1. La versione può essere modificata impostando in modo esplicito la proprietà OpenApiVersion di OpenApiOptions nel configureOptions parametro delegato di AddOpenApi:

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

Quando si genera il documento OpenAPI in fase di compilazione, è possibile selezionare la versione OpenAPI impostando --openapi-version nell'elemento OpenApiGenerateDocumentsOptions MSBuild:

<PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
    <!-- Configure build-time OpenAPI generation to produce an OpenAPI 3.1 document. -->
    <OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>

Il supporto di OpenAPI 3.1 è stato aggiunto principalmente nel seguente pull request .

Modifiche dirompenti di OpenAPI 3.1

Il supporto per OpenAPI 3.1 richiede un aggiornamento alla libreria OpenAPI.NET sottostante a una nuova versione principale 2.0. Questa nuova versione presenta alcune modifiche che rompono la compatibilità rispetto alla versione precedente. Le modifiche importanti possono influire sulle app se hanno dei trasformatori di documenti, operazioni o schemi. Le modifiche importanti in questa iterazione includono quanto segue:

  • Le entità all'interno del documento OpenAPI, come le operazioni e i parametri, vengono tipate come interfacce. Esistono implementazioni concrete per le varianti in linea e referenziate di un'entità. Ad esempio, IOpenApiSchema può essere OpenApiSchema incorporato oppure OpenApiSchemaReference che fa riferimento a uno schema definito in un'altra parte del documento.
  • La proprietà Nullable è stata rimossa dal tipo OpenApiSchema. Per determinare se un tipo è nullable, valutare se la proprietà OpenApiSchema.Type imposta JsonSchemaType.Null.

Una delle modifiche più significative è che la classe OpenApiAny è stata eliminata a favore dell'uso diretto di JsonNode. I trasformatori che usano OpenApiAny devono essere aggiornati per usare JsonNode. Il diff seguente illustra le modifiche apportate al trasformatore di schema da .NET 9 a .NET 10:

options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
    if (context.JsonTypeInfo.Type == typeof(WeatherForecast))
    {
-       schema.Example = new OpenApiObject
+       schema.Example = new JsonObject
        {
-           ["date"] = new OpenApiString(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")),
+           ["date"] = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"),
-           ["temperatureC"] = new OpenApiInteger(0),
+           ["temperatureC"] = 0,
-           ["temperatureF"] = new OpenApiInteger(32),
+           ["temperatureF"] = 32,
-           ["summary"] = new OpenApiString("Bracing"),
+           ["summary"] = "Bracing",
        };
    }
    return Task.CompletedTask;
});

Si noti che queste modifiche sono necessarie anche quando si configura solo la versione OpenAPI su 3.0.

OpenAPI in YAML

ASP.NET supporta ora la gestione del documento OpenAPI generato in formato YAML. YAML può essere più conciso di JSON, eliminando parentesi graffe e virgolette quando si possono sottintendere. YAML supporta anche stringhe a più righe, che possono essere utili per le descrizioni lunghe.

Per configurare un'app per gestire il documento OpenAPI generato in formato YAML, specificare l'endpoint nella chiamata MapOpenApi con un suffisso ".yaml" o ".yml", come illustrato nell'esempio seguente:

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi("/openapi/{documentName}.yaml");
}

Assistenza per:

  • YAML è attualmente disponibile solo per OpenAPI servito dall'endpoint OpenAPI.
  • La generazione di documenti OpenAPI in formato YAML in fase di compilazione viene aggiunta in un'anteprima futura.

Consulta questa richiesta pull che ha aggiunto il supporto per la distribuzione del documento OpenAPI generato in formato YAML.

Descrizione della risposta in ProducesResponseType per i controller API

, ProducesAttributee ProducesResponseTypeAttribute ora accettano un parametro stringa facoltativo, ProducesDefaultResponseTypeAttribute, che imposta la Descriptiondescrizione della risposta:

[HttpGet(Name = "GetWeatherForecast")]
[ProducesResponseType<IEnumerable<WeatherForecast>>(StatusCodes.Status200OK,
    Description = "The weather forecast for the next 5 days.")]
public IEnumerable<WeatherForecast> Get()
{

Dati OpenAPI generati:

"responses": {
  "200": {
    "description": "The weather forecast for the next 5 days.",
    "content": {

Questa funzionalità è supportata sia nei controller API che nelle API minime. Per le API minime, la Description proprietà viene impostata correttamente anche quando il tipo dell'attributo e il tipo restituito dedotto non sono una corrispondenza esatta.

Contributo comunitario (dotnet/aspnetcore n. 58193) di Sander ten Brinke.

Inserire i commenti del documento XML nel documento OpenAPI

La generazione di documenti OpenAPI in ASP.NET Core includerà ora i metadati dai commenti della documentazione XML sulle definizioni di metodo, classe e membro nel documento OpenAPI. Per usare questa funzionalità, è necessario abilitare i commenti dei documenti XML nel file di progetto. A tale scopo, aggiungere la proprietà seguente al file di progetto:

  <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

In fase di compilazione, il pacchetto OpenAPI userà un generatore di origine per individuare i commenti XML nell'assembly dell'applicazione corrente ed eventuali riferimenti al progetto e generare codice sorgente per inserirli nel documento tramite un trasformatore di documento OpenAPI.

Si noti che il processo di compilazione C# non acquisisce i commenti della documentazione XML inseriti in expresions lambda, quindi per usare i commenti della documentazione XML per aggiungere metadati a un endpoint API minimo, è necessario definire il gestore endpoint come metodo, inserire i commenti del documento XML sul metodo e quindi fare riferimento a tale metodo dal MapXXX metodo . Ad esempio, per usare i commenti della documentazione XML per aggiungere metadati a un endpoint API minimo originariamente definito come espressione lambda:

app.MapGet("/hello", (string name) =>$"Hello, {name}!");

Modificare la chiamata MapGet per fare riferimento a un metodo:

app.MapGet("/hello", Hello);

Definire il metodo Hello con i commenti della documentazione XML:

static partial class Program
{
    /// <summary>
    /// Sends a greeting.
    /// </summary>
    /// <remarks>
    /// Greeting a person by their name.
    /// </remarks>
    /// <param name="name">The name of the person to greet.</param>
    /// <returns>A greeting.</returns>
    public static string Hello(string name)
    {
        return $"Hello, {name}!";
    }
}

Nell'esempio precedente il metodo Hello viene aggiunto alla classe Program, ma è possibile aggiungerlo a qualsiasi classe del progetto.

Nell'esempio precedente vengono illustrati i commenti del documento XML <summary>, <remarks>e <param>. Per altre informazioni sui commenti dei documenti XML, inclusi tutti i tag supportati, vedere la documentazione di C#.

Poiché la funzionalità di base viene fornita tramite un generatore di origine, può essere disabilitata aggiungendo il codice MSBuild seguente al file di progetto.

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.2.*" GeneratePathProperty="true" />
</ItemGroup>

<Target Name="DisableCompileTimeOpenApiXmlGenerator" BeforeTargets="CoreCompile">
  <ItemGroup>
    <Analyzer Remove="$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll" />
  </ItemGroup>
</Target>

Il generatore di origine elabora i file XML inclusi nella proprietà AdditionalFiles. Per aggiungere (o rimuovere), le origini modificano la proprietà nel modo seguente:

<Target Name="AddXmlSources" BeforeTargets="CoreCompile">
  <ItemGroup>
    <AdditionalFiles Include="$(PkgSome_Package)/lib/net10.0/Some.Package.xml" />
  </ItemGroup>
</Target>

Microsoft.AspNetCore.OpenApi aggiunto al modello API Web ASP.NET Core (AOT nativo)

Il modello di progetto ASP.NET Core API Web (AOT nativa) (nome breve webapiaot) include ora il supporto per la generazione di documenti OpenAPI usando il pacchetto Microsoft.AspNetCore.OpenApi per impostazione predefinita. Questo supporto è disabilitato usando il flag --no-openapi quando si crea un nuovo progetto.

Contributo comunitario (dotnet/aspnetcore #60337) di Sander ten Brinke.

Supporto per IOpenApiDocumentProvider nel contenitore DI.

ASP.NET Core in .NET 10 supporta IOpenApiDocumentProvider nel contenitore di dependency injection. Gli sviluppatori possono inserire IOpenApiDocumentProvider nelle loro app e usarlo per accedere al documento OpenAPI. Questo approccio è utile per accedere ai documenti OpenAPI all'esterno del contesto delle richieste HTTP, ad esempio nei servizi in background o nel middleware personalizzato.

In precedenza, l'esecuzione della logica di avvio dell'applicazione senza avviare un server HTTP potrebbe essere eseguita usando HostFactoryResolver con un'implementazione di no-op IServer . La nuova funzionalità semplifica questo processo fornendo un'API semplificata ispirata a Aspire' s IDistributedApplicationPublisher, che fa parte del framework di Aspireper l'hosting e la pubblicazione di applicazioni distribuite.

Per altre informazioni, vedere dotnet/aspnetcore #61463.

Miglioramenti al generatore di commenti XML

La generazione di commenti XML gestisce meglio tipi complessi in .NET 10 rispetto alle versioni precedenti di .NET.

  • Produce commenti XML accurati e completi per una gamma più ampia di tipi.
  • Gestisce scenari più complessi.
  • Ignora normalmente l'elaborazione per tipi complessi che causano errori di compilazione nelle versioni precedenti.

Questi miglioramenti modificano la modalità di errore per determinati scenari, dagli errori di compilazione ai metadati mancanti.

Inoltre, l'elaborazione dei commenti della documentazione XML può ora essere configurata per accedere ai commenti XML in altri assembly. Ciò è utile per generare la documentazione per i tipi definiti all'esterno dell'assembly corrente, ad esempio il tipo ProblemDetails nello spazio dei nomi Microsoft.AspNetCore.Http.

Questa configurazione viene eseguita con le direttive nel file di compilazione del progetto. Nell'esempio seguente viene illustrato come configurare il generatore di commenti XML per accedere ai commenti XML per i tipi nell'assembly Microsoft.AspNetCore.Http, che include la classe ProblemDetails.

<Target Name="AddOpenApiDependencies" AfterTargets="ResolveReferences">
  <ItemGroup>
  <!-- Include XML documentation from Microsoft.AspNetCore.Http.Abstractions
    to get metadata for ProblemDetails -->
    <AdditionalFiles
          Include="@(ReferencePath->'
            %(RootDir)%(Directory)%(Filename).xml')"
          Condition="'%(ReferencePath.Filename)' ==
           'Microsoft.AspNetCore.Http.Abstractions'"
          KeepMetadata="Identity;HintPath" />
  </ItemGroup>
</Target>

Si prevede di includere commenti XML da un set selezionato di assembly nel framework condiviso nelle anteprime future per evitare la necessità di questa configurazione nella maggior parte dei casi.

Gestione unificata degli ID della documentazione nel generatore di commenti XML OpenAPI

I commenti della documentazione XML degli assembly a cui si fa riferimento vengono uniti correttamente anche quando gli ID della documentazione includono suffissi di tipo restituito. Di conseguenza, tutti i commenti XML validi sono inclusi in modo affidabile nella documentazione openAPI generata, migliorando l'accuratezza e la completezza della documentazione per le API usando assembly di riferimento.

I parametri di enumerazione dei dati del modulo usano il tipo di enumerazione effettivo in OpenAPI

I parametri dei dati del modulo nelle azioni del controller MVC ora generano metadati OpenAPI usando il tipo di enumerazione effettivo anziché impostare come valore predefinito la stringa.

Contributo della comunità: grazie a @ascott18!

Supporto per la generazione di OpenApiSchemas nei trasformatori

Gli sviluppatori possono ora generare uno schema per un tipo C# usando la stessa logica di ASP.NET Core generazione di documenti OpenAPI e aggiungerlo al documento OpenAPI. È quindi possibile fare riferimento allo schema da un'altra posizione nel documento OpenAPI.

Il contesto passato a documenti, operazioni e trasformatori di schema include un nuovo GetOrCreateSchemaAsync metodo che può essere usato per generare uno schema per un tipo. Questo metodo include anche un parametro facoltativo ApiParameterDescription per specificare metadati aggiuntivi per lo schema generato.

Per supportare l'aggiunta dello schema al documento OpenAPI, è stata aggiunta una Document proprietà ai contesti Operation e Schema Transformer. Ciò consente a qualsiasi trasformatore di aggiungere uno schema al documento OpenAPI usando il metodo del AddComponent documento.

Example

Per utilizzare questa funzionalità in un documento, un'operazione o un trasformatore di schema, crea lo schema utilizzando il metodo GetOrCreateSchemaAsync fornito nel contesto e aggiungilo al documento OpenAPI usando il metodo AddComponent.

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer(async (operation, context, cancellationToken) =>
    {
        // Generate schema for error responses
        var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
        context.Document?.AddComponent("Error", errorSchema);

        operation.Responses ??= new OpenApiResponses();
        // Add a "4XX" response to the operation with the newly created schema
        operation.Responses["4XX"] = new OpenApiResponse
        {
            Description = "Bad Request",
            Content = new Dictionary<string, OpenApiMediaType>
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchemaReference("Error", context.Document)
                }
            }
        };
    });
});

Trasformatori di operazioni OpenAPI specifici per endpoint

I trasformatori di operazione specifici dell'endpoint consentono la personalizzazione con granularità fine della documentazione openAPI per i singoli endpoint di route. Questa funzionalità consente agli sviluppatori di personalizzare i metadati e le descrizioni di Swagger/OpenAPI per azione o per route, migliorando l'estendibilità per scenari API avanzati.

Per informazioni dettagliate sull'implementazione e esempi di codice, vedere Personalizzare documenti OpenAPI.

Aggiornare Microsoft.OpenApi alla versione 2.0.0

La raccolta Microsoft.OpenApi usata per la generazione di documenti OpenAPI in ASP.NET Core è stata aggiornata alla versione 2.0.0 (GA).

Modifiche di rilievo nella versione 2.0.0

Le modifiche di rilievo seguenti sono state introdotte nelle versioni di anteprima e rimangono nella versione disponibile a livello generale. Questi influiscono principalmente sugli utenti che implementano trasformatori di documenti, operazioni o schemi:

Con l'aggiornamento alla versione disponibile a livello generale, non sono previste ulteriori modifiche di rilievo nella generazione di documenti OpenAPI.

Miglioramenti alla generazione dello schema OpenAPI

Modellare i tipi nullable usando oneOf nello schema OpenAPI

La generazione dello schema OpenAPI per i tipi nullable è stata migliorata usando il oneOf modello anziché la proprietà nullable per tipi e raccolte complesse. Implementazione:

  • Usa oneOf insieme a null e allo schema del tipo effettivo per i tipi complessi nullable negli schemi di richiesta e risposta.
  • Rileva la nullabilità per parametri, proprietà e tipi di ritorno usando la Reflection e NullabilityInfoContext.
  • Rimuove i tipi Null dagli schemi componentizzati per evitare la duplicazione.

Correzioni e miglioramenti alla risoluzione dei riferimenti allo schema

Questa versione migliora la gestione degli schemi JSON per la generazione di documenti OpenAPI risolvendo correttamente i riferimenti allo schema JSON relativi ($ref) nel documento dello schema radice.

Includere le descrizioni delle proprietà come elementi di pari livello di $ref nello schema OpenAPI

Prima di .NET 10, ASP.NET Core rimosse descrizioni sulle proprietà definite con $ref nel documento OpenAPI generato perché OpenAPI v3.0 non consentiva proprietà di pari livello insieme a $ref nelle definizioni dello schema. OpenAPI 3.1 consente ora di includere descrizioni insieme a $ref. RC1 aggiunge il supporto per includere le descrizioni delle proprietà come elementi di pari livello di $ref nello schema OpenAPI generato.

Questo è stato un contributo della comunità. Grazie @desjoerd!

Aggiungere metadati dai commenti XML ai [AsParameters] tipi allo schema OpenAPI

La generazione di schemi OpenAPI elabora ora commenti XML sulle proprietà delle classi di [AsParameters] parametri per estrarre i metadati per la documentazione.

Escludere metodi HTTP sconosciuti da OpenAPI

La generazione dello schema OpenAPI ora esclude i metodi HTTP sconosciuti dal documento OpenAPI generato. I metodi di query, che sono metodi HTTP standard ma non riconosciuti da OpenAPI, vengono ora esclusi normalmente dal documento OpenAPI generato.

Questo è stato un contributo della comunità. Grazie @martincostello!

Migliorare la descrizione dei corpi delle richieste di JSON Patch

La generazione dello schema OpenAPI per le operazioni patch JSON ora applica correttamente il tipo di media application/json-patch+json al corpo della richiesta che utilizza JSON Patch. In questo modo, il documento OpenAPI generato riflette in modo accurato il tipo di supporto previsto per le operazioni JSON Patch. Inoltre, il corpo della richiesta patch JSON ha uno schema dettagliato che descrive la struttura del documento patch JSON, incluse le operazioni che è possibile eseguire.

Questo è stato un contributo della comunità. Grazie @martincostello!

Usare la cultura invariabile per la generazione del documento OpenAPI

La generazione di documenti OpenAPI ora utilizza impostazioni culturali invarianti per formattare numeri e date nel documento OpenAPI che viene generato. In questo modo il documento generato è coerente e non varia in base alle impostazioni cultura del server.

Questo è stato un contributo della comunità. Grazie @martincostello!

Autenticazione e autorizzazione

Metriche di autenticazione e autorizzazione

Le metriche sono state aggiunte per determinati eventi di autenticazione e autorizzazione in ASP.NET Core. Con questa modifica, è ora possibile ottenere le metriche per gli eventi seguenti:

  • Authentication:
    • Durata della richiesta autenticata
    • Conteggio delle sfide
    • Proibire il conteggio
    • Numero di accessi
    • Conteggio uscite
  • Authorization:
    • Numero di richieste che richiedono l'autorizzazione

L'immagine seguente mostra un esempio della metrica di durata della richiesta autenticata nel Aspire dashboard:

Durata della richiesta autenticata nel Aspire dashboard

Per altre informazioni, vedere ASP.NET Core metriche predefinite.

Le metriche di ASP.NET Core Identity

ASP.NET Core Identity l'osservabilità è stata migliorata in .NET 10 con le metriche. Le metriche sono contatori, istogrammi e misuratori che forniscono misurazioni di serie temporali del comportamento del sistema o dell'applicazione.

Ad esempio, usare le nuove metriche ASP.NET Core Identity per osservare:

  • Gestione utenti: nuove creazioni utente, modifiche delle password e assegnazioni di ruolo.
  • Gestione dell'accesso/sessione: tentativi di login, accessi, disconnessioni e utenti che usano l'autenticazione a due fattori.

Le nuove metriche si trovano nel contatore Microsoft.AspNetCore.Identity:

  • aspnetcore.identity.user.create.duration
  • aspnetcore.identity.user.update.duration
  • aspnetcore.identity.user.delete.duration
  • aspnetcore.identity.user.check_password_attempts
  • aspnetcore.identity.user.generated_tokens
  • aspnetcore.identity.user.verify_token_attempts
  • aspnetcore.identity.sign_in.authenticate.duration
  • aspnetcore.identity.sign_in.check_password_attempts
  • aspnetcore.identity.sign_in.sign_ins
  • aspnetcore.identity.sign_in.sign_outs
  • aspnetcore.identity.sign_in.two_factor_clients_remembered
  • aspnetcore.identity.sign_in.two_factor_clients_forgotten

Per altre informazioni sull'uso delle metriche in ASP.NET Core, vedere metriche ASP.NET Core.

Per impostazione predefinita, le richieste non autenticate e non autorizzate effettuate agli endpoint API noti protetti dall'autenticazione cookie comportano ora 401 e 403 risposte anziché reindirizzare a un account di accesso o a un URI negato.

Questa modifica è stata altamente richiesta, perché il reindirizzamento di richieste non autenticate a una pagina di accesso non ha in genere senso per gli endpoint API che in genere si basano su codici di stato 401 e 403 anziché reindirizzamenti HTML per comunicare errori di autenticazione.

Gli endpoint API noti vengono identificati usando la nuova IApiEndpointMetadata interfaccia e i metadati che implementano la nuova interfaccia sono stati aggiunti automaticamente agli elementi seguenti:

  • [ApiController] Endpoint
  • Endpoint API minimi che leggono i corpi delle richieste JSON o scrivono risposte JSON
  • Endpoint che usano TypedResults restituiscono tipi
  • SignalR Endpoint

Quando IApiEndpointMetadata è presente, il cookie gestore di autenticazione restituisce ora i codici di stato HTTP appropriati (401 per le richieste non autenticate, 403 richieste non consentite) anziché il reindirizzamento.

Se si vuole impedire questo nuovo comportamento e reindirizzare sempre agli URI di accesso e di accesso negato per le richieste non autenticate o non autorizzate indipendentemente dall'endpoint di destinazione, è possibile sovrascrivere gli eventi RedirectToLogin e RedirectToAccessDenied come indicato di seguito:

builder.Services.AddAuthentication()
    .AddCookie(options =>
    {
        options.Events.OnRedirectToLogin = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };

        options.Events.OnRedirectToAccessDenied = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };
    });

Per ulteriori informazioni su questa modifica importante, vedere l'annuncio delle modifiche importanti di ASP.NET Core.

Miscellaneous

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

Configurare la soppressione della diagnostica del gestore delle eccezioni

È stata aggiunta una nuova opzione di configurazione al middleware del gestore eccezioni ASP.NET Core per controllare l'output di diagnostica: ExceptionHandlerOptions.SuppressDiagnosticsCallback. Questo callback fornisce informazioni contestuali sulla richiesta e sull'eccezione, consentendo di aggiungere una logica che determina se il middleware deve scrivere log delle eccezioni e altri dati di telemetria.

Questa impostazione è utile quando si sa che un'eccezione è temporanea o è stata gestita dal middleware del gestore eccezioni e non si desidera che i log degli errori vengano scritti nella piattaforma di osservabilità.

Anche il comportamento predefinito del middleware è cambiato: non scrive più la diagnostica delle eccezioni per le eccezioni gestite da IExceptionHandler. In base al feedback degli utenti, la registrazione delle eccezioni gestite al livello di errore era spesso indesiderata quando IExceptionHandler.TryHandleAsync restituiva true.

È possibile ripristinare il comportamento precedente configurando SuppressDiagnosticsCallback:

app.UseExceptionHandler(new ExceptionHandlerOptions
{
    SuppressDiagnosticsCallback = context => false;
});

Per altre informazioni su questa modifica radicale, vedere https://github.com/aspnet/Announcements/issues/524.

Supporto per il dominio di Top-Level .localhost

Il .localhost dominio di primo livello (TLD) viene definito in RFC2606 e RFC6761 come riservato a scopo di test e disponibile per gli utenti da usare localmente come qualsiasi altro nome di dominio. Ciò significa che l'uso di un nome come myapp.localhost localmente che si risolve nell'indirizzo di loopback IP è consentito e previsto in base a queste RFC. Inoltre, i moderni browser evergreen risolvono automaticamente qualsiasi nome *.localhost nell'indirizzo di loopback IP (127.0.0.1/::1), rendendoli effettivamente un alias per qualsiasi servizio già ospitato su localhost nel computer locale; cioè, qualsiasi servizio che risponde a http://localhost:6789 risponderà anche a http://anything-here.localhost:6789, a condizione che il servizio non esegua alcuna verifica o imposizione specifica del nome host.

ASP.NET Core è stato aggiornato in .NET 10 Preview 7 per supportare meglio il TLD .localhost, in modo che sia ora possibile usarlo facilmente durante la creazione e l'esecuzione di applicazioni ASP.NET Core nell'ambiente di sviluppo locale. Avere app diverse in esecuzione in locale essere risolvibili tramite nomi diversi consente una migliore separazione di alcuni asset di siti Web associati al nome di dominio, ad esempio i cookie e semplifica l'identificazione dell'app in cui si sta esplorando tramite il nome visualizzato nella barra degli indirizzi del browser.

Il server HTTP predefinito ASP.NET Core, Kestrel, ora considererà correttamente qualsiasi nome *.localhost impostato tramite meccanismi di configurazione dell'endpoint supportati come indirizzo di loopback locale e quindi associarlo a esso anziché a tutti gli indirizzi esterni (ad esempio, eseguire il binding a 127.0.0.1/::1 anziché 0.0.0.0/::). Ciò include la "applicationUrl" proprietà nei profili di avvio configurati in un file dilaunchSettings.jsone la ASPNETCORE_URLS variabile di ambiente. Se configurato per l'ascolto su un indirizzo, registrerà un messaggio informativo per entrambi gli indirizzi e , per rendere chiaro che entrambi i nomi possono essere utilizzati.

Mentre i Web browser risolvono *.localhost automaticamente i nomi nell'indirizzo di loopback locale, altre app potrebbero trattare *.localhost i nomi come nomi di dominio regolari e tentare di risolverli tramite lo stack DNS corrispondente. Se la configurazione DNS non risolve *.localhost i nomi in un indirizzo, non riescono a connettersi. È possibile continuare a usare il nome normale localhost per indirizzare le app quando non in un Web browser.

Il certificato di sviluppo HTTPS ASP.NET Core (incluso il comando dotnet dev-certs https) è stato aggiornato per assicurare che il certificato sia valido per l'uso con il nome di dominio *.dev.localhost. Dopo aver installato .NET 10 SDK Preview 7, considerare attendibile il nuovo certificato per sviluppatori eseguendo dotnet dev-certs https --trust nella riga di comando per assicurarsi che il sistema sia configurato per considerare attendibile il nuovo certificato.

Il certificato elenca il *.dev.localhost nome come Nome Alternativo del Soggetto (SAN) anziché *.localhost perché l'uso di un certificato wildcard per un nome di dominio di primo livello non è valido.

I modelli di progetto per ASP.NET Core Empty (web) e Blazor Web App (blazor) sono stati aggiornati con una nuova opzione che, quando specificato, configura il progetto creato per l'uso del suffisso nome di dominio .dev.localhost, combinandolo con il nome del progetto per consentire all'app di passare a un indirizzo come https://myapp.dev.localhost:5036:

$ dotnet new web -n MyApp --localhost-tld
The template "ASP.NET Core Empty" was created successfully.

Processing post-creation actions...
Restoring D:\src\MyApp\MyApp.csproj:
Restore succeeded.

$ cd .\MyApp\
$ dotnet run --launch-profile https
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://myapp.dev.localhost:7099
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7099/
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://myapp.dev.localhost:5036
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5036/
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\src\local\10.0.1xx\MyApp

Supporto della deserializzazione json+PipeReader nelle API MVC e minime

PR: https://github.com/dotnet/aspnetcore/pull/62895

Vedere https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview7/libraries.md#pipereader-support-for-json-serializer

MVC, API minime e i HttpRequestJsonExtensions.ReadFromJsonAsync metodi sono stati aggiornati per usare il nuovo supporto Json+PipeReader senza richiedere modifiche al codice dalle applicazioni.

Per la maggior parte delle applicazioni, l'aggiunta di questo supporto non ha alcun effetto sul loro comportamento. Tuttavia, se l'applicazione usa un oggetto personalizzato JsonConverter, è possibile che il convertitore non gestisca Utf8JsonReader.HasValueSequence correttamente. Ciò può comportare errori e dati mancanti, ad esempio ArgumentOutOfRangeException, durante la deserializzazione.

La soluzione alternativa rapida (soprattutto se non si è proprietari del JsonConverter utilizzato) consiste nell'impostare l'interruttore "Microsoft.AspNetCore.UseStreamBasedJsonParsing"AppContext su "true". Deve trattarsi di una soluzione alternativa temporanea e deve JsonConverter essere aggiornato per supportare HasValueSequence.

Per correggere JsonConverter le implementazioni, è disponibile una correzione rapida che alloca una matrice da ReadOnlySequence e sarà simile all'esempio seguente:

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
    // previous code
}

Esiste anche una correzione più complessa (ma efficiente), che comporta la presenza di un percorso di codice separato per la ReadOnlySequence gestione:

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    if (reader.HasValueSequence)
    {
        reader.ValueSequence;
        // ReadOnlySequence optimized path
    }
    else
    {
        reader.ValueSpan;
        // ReadOnlySpan optimized path
    }
}

Rimozione automatica dal pool di memoria

I pool di memoria usati da Kestrel, IIS e HTTP.sys ora eliminano automaticamente i blocchi di memoria quando l'applicazione è inattiva o meno carica. La funzionalità viene eseguita automaticamente e non deve essere abilitata o configurata manualmente.

Perché l'espulsione della memoria è importante

In precedenza, la memoria allocata dal pool sarebbe rimasta riservata, anche quando non veniva utilizzata. Questa funzionalità rilascia la memoria al sistema quando l'app è inattiva per un periodo di tempo. Questa rimozione riduce l'utilizzo complessivo della memoria e aiuta le applicazioni a rimanere reattive in carichi di lavoro diversi.

Usare le metriche di rimozione della memoria

Le metriche sono state aggiunte al pool di memoria predefinito usato dalle implementazioni del server. Le nuove metriche sono sotto il nome "Microsoft.AspNetCore.MemoryPool".

Per informazioni sulle metriche e su come usarle, vedere ASP.NET Core metriche.

Gestire i pool di memoria

Oltre a usare pool di memoria in modo più efficiente rimuovendo blocchi di memoria non necessario, .NET 10 migliora l'esperienza di creazione di pool di memoria. A tale scopo, fornisce un IMemoryPoolFactory predefinito e un'implementazione MemoryPoolFactory. Rende l'implementazione disponibile per l'applicazione tramite iniezione di dipendenza.

L'esempio di codice seguente illustra un semplice servizio in background che usa l'implementazione predefinita della factory del pool di memoria per creare pool di memoria. Questi pool traggono vantaggio dalla funzionalità di rimozione automatica:

public class MyBackgroundService : BackgroundService
{
    private readonly MemoryPool<byte> _memoryPool;

    public MyBackgroundService(IMemoryPoolFactory<byte> factory)
    {
        _memoryPool = factory.Create();
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(20, stoppingToken);
                // do work that needs memory
                var rented = _memoryPool.Rent(100);
                rented.Dispose();
            }
            catch (OperationCanceledException)
            {
                return;
            }
        }
    }
}

Per utilizzare una factory del pool di memoria personalizzata, creare una classe che implementa IMemoryPoolFactory e registrarla con l'iniezione di dipendenze, come illustrato nell'esempio seguente. I pool di memoria creati in questo modo non traggono vantaggio dalla funzionalità di rimozione automatica, a meno che non si implementi una logica di rimozione simile nella factory personalizzata:

services.AddSingleton<IMemoryPoolFactory<byte>,
CustomMemoryPoolFactory>();

public class CustomMemoryPoolFactory : IMemoryPoolFactory<byte>
{
    public MemoryPool<byte> Create()
    {
        // Return a custom MemoryPool implementation
        // or the default, as is shown here.
        return MemoryPool<byte>.Shared;
    }
}

Descrittori di sicurezza personalizzabili per HTTP.sys

Da ora è possibile specificare un descrittore di sicurezza personalizzato per le code di richieste HTTP.sys. La nuova proprietà RequestQueueSecurityDescriptor in HttpSysOptions consente un controllo più granulare sui diritti di accesso per la coda delle richieste. Questo controllo granulare consente di personalizzare la sicurezza in base alle esigenze dell'applicazione.

Operazioni che è possibile eseguire con la nuova proprietà

Una coda di richieste in HTTP.sys è una struttura a livello di kernel che archivia temporaneamente le richieste HTTP in ingresso fino a quando l'applicazione non è pronta per elaborarle. Personalizzando il descrittore di sicurezza, è possibile consentire o negare l'accesso a utenti o gruppi specifici alla coda delle richieste. Ciò è utile negli scenari in cui si vuole limitare o delegare la gestione delle richieste HTTP.sys a livello di sistema operativo.

Come usare la nuova proprietà

La RequestQueueSecurityDescriptor proprietà si applica solo quando si crea una nuova coda di richieste. La proprietà non influisce sulle code di richieste esistenti. Per usare questa proprietà, impostarla su un'istanza GenericSecurityDescriptor durante la configurazione del server HTTP.sys.

Ad esempio, il codice seguente consente l'accesso a tutti gli utenti autenticati ma nega l'accesso agli utenti ospiti.

using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.AspNetCore.Server.HttpSys;

// Create a new security descriptor
var securityDescriptor = new CommonSecurityDescriptor(isContainer: false, isDS: false, sddlForm: string.Empty);

// Create a discretionary access control list (DACL)
var dacl = new DiscretionaryAcl(isContainer: false, isDS: false, capacity: 2);
dacl.AddAccess(
    AccessControlType.Allow,
    new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
    -1,
    InheritanceFlags.None,
    PropagationFlags.None
);
dacl.AddAccess(
    AccessControlType.Deny,
    new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null),
    -1,
    InheritanceFlags.None,
    PropagationFlags.None
);

// Assign the DACL to the security descriptor
securityDescriptor.DiscretionaryAcl = dacl;

// Configure HTTP.sys options
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseHttpSys(options =>
{
    options.RequestQueueSecurityDescriptor = securityDescriptor;
});

Per altre informazioni, vedere HTTP.sys implementazione del server Web in ASP.NET Core.

Supporto migliore per i test delle app con istruzioni di primo livello

.NET 10 offre ora un supporto migliore per testare le app che usano istruzioni di livello superiore. In precedenza gli sviluppatori dovevano aggiungere manualmente public partial class Program al file di Program.cs in modo che il progetto di test potesse fare riferimento al Program class. public partial class Program è stato necessario perché la funzionalità di istruzione di primo livello in C# 9 ha generato un oggetto Program class dichiarato come interno.

In .NET 10 viene utilizzato un generatore di codice sorgente per creare la dichiarazione public partial class Program se il programmatore non l'ha dichiarata esplicitamente. Inoltre, è stato aggiunto un analizzatore per rilevare quando public partial class Program viene dichiarato in modo esplicito e consigliare allo sviluppatore di rimuoverlo.

Image

Le seguenti pull request hanno contribuito a questa funzionalità.

Nuova implementazione della patch JSON con System.Text.Json

Patch JSON:

  • Formato standard per descrivere le modifiche da applicare a un documento JSON.
  • È definito in RFC 6902 ed è ampiamente usato nelle API RESTful per eseguire aggiornamenti parziali alle risorse JSON.
  • Rappresenta una sequenza di operazioni, ad esempio Add, Remove, Replace, Move, Copy, Test, che possono essere applicate per modificare un documento JSON.

Nelle app Web, patch JSON viene comunemente usata in un'operazione PATCH per eseguire aggiornamenti parziali di una risorsa. Anziché inviare l'intera risorsa per un aggiornamento, i client possono inviare un documento patch JSON contenente solo le modifiche. L'applicazione di patch riduce le dimensioni del payload e migliora l'efficienza.

Questa versione introduce una nuova implementazione di Microsoft.AspNetCore.JsonPatch basata sulla serializzazione System.Text.Json. Questa funzionalità offre i vantaggi seguenti:

  • Si allinea alle procedure di .NET moderne sfruttando la libreria System.Text.Json, ottimizzata per .NET.
  • Offre prestazioni migliorate e un utilizzo ridotto della memoria rispetto all'implementazione basata su legacy Newtonsoft.Json.

I benchmark seguenti confrontano le prestazioni della nuova System.Text.Json implementazione con l'implementazione legacy Newtonsoft.Json .

Scenario Implementation Mean Memoria allocata
Benchmark delle applicazioni Newtonsoft.JsonPatch 271,924 μs 25 KB
System.Text.JsonPatch 1,584 μs 3 KB
Benchmark di deserializzazione Newtonsoft.JsonPatch 19.261 μs 43 KB
System.Text.JsonPatch 7,917 μs 7 KB

Questi benchmark evidenziano miglioramenti significativi delle prestazioni e riduzione dell'utilizzo della memoria con la nuova implementazione.

Notes:

  • La nuova implementazione non sostituisce l'implementazione legacy. In particolare, la nuova implementazione non supporta i tipi dinamici, ExpandoObjectad esempio .
  • Lo standard JSON Patch presenta rischi di sicurezza intrinseci. Poiché questi rischi sono intrinseci allo standard JSON Patch, la nuova implementazione non tenta di attenuare i rischi di sicurezza intrinseci. È responsabilità dello sviluppatore assicurarsi che il documento JSON Patch sia sicuro da applicare all'oggetto di destinazione. Per altre informazioni, vedere la sezione Mitigazione dei rischi per la sicurezza .

Usage

Per abilitare il supporto delle patch JSON con System.Text.Json, installare il pacchetto NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson.

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease

Questo pacchetto fornisce una JsonPatchDocument<T> classe per rappresentare un documento JSON Patch per oggetti di tipo T e logica personalizzata per la serializzazione e la deserializzazione di documenti patch JSON tramite System.Text.Json. Il metodo chiave della JsonPatchDocument<T> classe è ApplyTo, che applica le operazioni patch a un oggetto di destinazione di tipo T.

Gli esempi seguenti illustrano come usare il ApplyTo metodo per applicare un documento Patch JSON a un oggetto .

Esempio: applicazione di un JsonPatchDocument

L'esempio seguente illustra:

  1. Operazioni add, replacee remove .
  2. Operazioni sulle proprietà annidate.
  3. Aggiunta di un nuovo elemento a una matrice.
  4. Uso di un convertitore di enumerazioni di stringhe JSON in un documento patch JSON.
// Original object
var person = new Person {
  FirstName = "John",
  LastName = "Doe",
  Email = "johndoe@gmail.com",
  PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
  Address = new Address
  {
    Street = "123 Main St",
    City = "Anytown",
    State = "TX"
  }
};

// Raw JSON Patch document
var jsonPatch = """
[
  { "op": "replace", "path": "/FirstName", "value": "Jane" },
  { "op": "remove", "path": "/Email"},
  { "op": "add", "path": "/Address/ZipCode", "value": "90210" },
  {
    "op": "add",
    "path": "/PhoneNumbers/-",
    "value": { "Number": "987-654-3210", "Type": "Work" }
  }
]
""";

// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

// Apply the JSON Patch document
patchDoc!.ApplyTo(person);

// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

// Output:
// {
//   "firstName": "Jane",
//   "lastName": "Doe",
//   "address": {
//     "street": "123 Main St",
//     "city": "Anytown",
//     "state": "TX",
//     "zipCode": "90210"
//   },
//   "phoneNumbers": [
//     {
//       "number": "123-456-7890",
//       "type": "Mobile"
//     },
//     {
//       "number": "987-654-3210",
//       "type": "Work"
//     }
//   ]
// }

Il metodo ApplyTo segue in genere le convenzioni e le opzioni di System.Text.Json per l'elaborazione di JsonPatchDocument, incluso il comportamento controllato dalle seguenti opzioni:

  • NumberHandling: indica se le proprietà numeriche vengono lette dalle stringhe.
  • PropertyNameCaseInsensitive: indica se i nomi delle proprietà fanno distinzione tra maiuscole e minuscole.

Differenze principali tra System.Text.Json e la nuova JsonPatchDocument<T> implementazione:

  • Il tipo di runtime dell'oggetto di destinazione, non il tipo dichiarato, determina quali proprietà vengono modificate da ApplyTo.
  • System.Text.Json la deserializzazione si basa sul tipo dichiarato per identificare le proprietà idonee.

Esempio: Applicazione di un JsonPatchDocument con la gestione degli errori

Esistono diversi errori che possono verificarsi quando si applica un documento patch JSON. Ad esempio, l'oggetto di destinazione potrebbe non avere la proprietà specificata oppure il valore specificato potrebbe non essere compatibile con il tipo di proprietà.

Patch JSON supporta anche l'operazione test . L'operazione test controlla se un valore specificato è uguale alla proprietà di destinazione e, in caso contrario, restituisce un errore.

Nell'esempio seguente viene illustrato come gestire correttamente questi errori.

Important

L'oggetto passato al metodo ApplyTo viene modificato sul posto. È responsabilità del chiamante eliminare queste modifiche se un'operazione non riesce.

// Original object
var person = new Person {
  FirstName = "John",
  LastName = "Doe",
  Email = "johndoe@gmail.com"
};

// Raw JSON Patch document
var jsonPatch = """
[
  { "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
  { "op": "test", "path": "/FirstName", "value": "Jane" },
  { "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";

// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

// Apply the JSON Patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
    {
        errors ??= new ();
        var key = jsonPatchError.AffectedObject.GetType().Name;
        if (!errors.ContainsKey(key))
        {
            errors.Add(key, new string[] { });
        }
        errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
    });
if (errors != null)
{
    // Print the errors
    foreach (var error in errors)
    {
        Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
    }
}

// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

// Output:
// Error in Person: The current value 'John' at path 'FirstName' is not equal 
// to the test value 'Jane'.
// {
//   "firstName": "John",
//   "lastName": "Smith",              <<< Modified!
//   "email": "janedoe@gmail.com",     <<< Modified!
//   "phoneNumbers": []
// }

Prevenzione dei rischi per la sicurezza

Quando si usa il pacchetto Microsoft.AspNetCore.JsonPatch.SystemTextJson, è fondamentale comprendere e attenuare i potenziali rischi per la sicurezza. Le sezioni seguenti illustrano i rischi di sicurezza identificati associati alla patch JSON e forniscono mitigazioni consigliate per garantire l'utilizzo sicuro del pacchetto.

Important

Questo non è un elenco completo delle minacce. Gli sviluppatori di app devono condurre revisioni del proprio modello di minaccia per determinare un elenco completo specifico dell'app e trovare soluzioni di mitigazione appropriate in base alle esigenze. Ad esempio, le app che espongono raccolte alle operazioni patch devono considerare il potenziale di attacchi di complessità algoritmica se tali operazioni inseriscono o rimuovono elementi all'inizio della raccolta.

Eseguendo modelli di minaccia completi per le proprie app e risolvendo le minacce identificate seguendo le mitigazioni consigliate di seguito, i consumer di questi pacchetti possono integrare le funzionalità patch JSON nelle proprie app riducendo al minimo i rischi per la sicurezza.

I consumer di questi pacchetti possono integrare le funzionalità patch JSON nelle app riducendo al minimo i rischi per la sicurezza, tra cui:

  • Eseguire modelli di minaccia completi per le proprie app.
  • Risolvere le minacce identificate.
  • Seguire le mitigazioni consigliate nelle sezioni seguenti.
Denial of Service (DoS) tramite amplificazione della memoria
  • Scenario: un client dannoso invia un'operazione copy che duplica più volte grafici di oggetti di grandi dimensioni, causando un consumo eccessivo di memoria.
  • Impatto: potenziali condizioni di out-Of-Memory (OOM), causando interruzioni del servizio.
  • Mitigation:
    • Convalidare i documenti patch JSON in ingresso per le dimensioni e la struttura prima di chiamare ApplyTo.
    • La convalida deve essere specifica dell'app, ma una convalida di esempio può essere simile alla seguente:
public void Validate(JsonPatchDocument<T> patch)
{
    // This is just an example. It's up to the developer to make sure that
    // this case is handled properly, based on the app's requirements.
    if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
        > MaxCopyOperationsCount)
    {
        throw new InvalidOperationException();
    }
}
Subversione della logica di business
  • Scenario: le operazioni patch possono modificare i campi con invarianti impliciti, ad esempio flag interni, ID o campi calcolati, violando i vincoli aziendali.
  • Impatto: problemi di integrità dei dati e comportamento imprevisto dell'app.
  • Mitigation:
    • Utilizzare oggetti POCO con proprietà definite in modo esplicito che possono essere modificate in modo sicuro.
    • Evitare di esporre proprietà sensibili o critiche per la sicurezza nell'oggetto di destinazione.
    • Se non viene utilizzato alcun oggetto POCO, convalidare l'oggetto modificato dopo l'applicazione delle operazioni per garantire che le regole aziendali e gli invarianti non vengano violati.
Autenticazione e autorizzazione
  • Scenario: i client non autenticati o non autorizzati inviano richieste di patch JSON dannose.
  • Impatto: accesso non autorizzato per modificare i dati sensibili o interrompere il comportamento dell'app.
  • Mitigation:
    • Proteggere gli endpoint che accettano richieste patch JSON con meccanismi di autenticazione e autorizzazione appropriati.
    • Limitare l'accesso a client o utenti attendibili con autorizzazioni appropriate.

Rilevare se l'URL è locale usando RedirectHttpResult.IsLocalUrl

Usare il nuovo metodo helper RedirectHttpResult.IsLocalUrl(url) per rilevare se un URL è locale. Un URL viene considerato locale se sono vere le condizioni seguenti:

Anche gli URL che usano percorsi"~/" virtuali sono locali.

IsLocalUrl è utile per convalidare gli URL prima di reindirizzarli per impedire attacchi di reindirizzamento aperti.

if (RedirectHttpResult.IsLocalUrl(url))
{
    return Results.LocalRedirect(url);
}

Grazie @martincostello per questo contributo!

Modifiche radicali

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