Dela via


Nyheter i ASP.NET Core 9.0

Den här artikeln belyser de viktigaste ändringarna i ASP.NET Core 9.0 med länkar till relevant dokumentation.

Optimering av statisk tillgångsleverans

MapStaticAssets routningsslutpunktskonventioner är en ny funktion som optimerar leveransen av statiska tillgångar i ASP.NET Core-appar.

Information om leverans av statiskt innehåll för Blazor-appar finns i ASP.NET Core Blazor statiska filer.

Att följa bästa praxis för produktion för att betjäna statiska tillgångar kräver en betydande mängd arbets- och teknisk expertis. Utan optimeringar som komprimering, cachelagring och fingeravtryck:

  • Webbläsaren måste göra ytterligare begäranden vid varje sidinläsning.
  • Fler byte än nödvändigt överförs via nätverket.
  • Ibland levereras inaktuella versioner av filer till klienter.

För att skapa högpresterande webbappar krävs det att resursleveransen till webbläsaren optimeras. Möjliga optimeringar är:

  • Hantera en viss tillgång en gång tills filen ändras eller webbläsaren rensar cacheminnet. Ange rubriken ETag.
  • Förhindra att webbläsaren använder gamla eller inaktuella tillgångar när en app har uppdaterats. Ange senaste ändrade-huvudet.
  • Konfigurera rätt cachehuvuden.
  • Använd mellanlager för cachelagring.
  • Hantera komprimerade versioner av tillgångarna när det är möjligt.
  • Använd en CDN- för att leverera resurserna närmare användaren.
  • Minimera storleken på de tillgångar som hanteras i webbläsaren. Den här optimeringen omfattar inte minifiering.

MapStaticAssets är en ny funktion som optimerar leveransen av statiska tillgångar i en app. Den är utformad för att fungera med alla användargränssnittsramverk, inklusive Blazor, Razor Pages och MVC. Det är vanligtvis som en direkt ersättning för UseStaticFiles:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

+app.MapStaticAssets();
-app.UseStaticFiles();
app.MapRazorPages();

app.Run();

MapStaticAssets fungerar genom att kombinera bygg- och publiceringsprocesser för att samla in information om alla statiska resurser i en app. Den här informationen används sedan av runtime-biblioteket för att effektivt hantera dessa filer i webbläsaren.

MapStaticAssets kan ersätta UseStaticFiles i de flesta situationer är den dock optimerad för att betjäna de tillgångar som appen har kunskap om vid bygg- och publiceringstid. Om appen hanterar tillgångar från andra platser, till exempel disk eller inbäddade resurser, bör UseStaticFiles användas.

MapStaticAssets ger följande fördelar som inte hittas med UseStaticFiles:

  • Komprimering av byggtid för alla tillgångar i appen:
    • gzip under utveckling och gzip + brotli under publiceringen.
    • Alla tillgångar komprimeras med målet att minska storleken på tillgångarna till minimum.
  • Innehållsbaserad ETags: Etags för varje resurs är Base64 kodad sträng i SHA-256 hash för innehållet. Detta säkerställer att webbläsaren endast laddar ned en fil igen om innehållet har ändrats.

I följande tabell visas de ursprungliga och komprimerade storlekarna för CSS- och JS-filerna i standardmallen Razor Pages:

Fil Original Komprimerad % minskning
bootstrap.min.css 163 17,5 89,26%
jquery.js 89.6 28 68,75%
bootstrap.min.js 78.5 20 74,52%
totalt 331,1 65.5 80,20%

I följande tabell visas de ursprungliga och komprimerade storlekarna med hjälp av Fluent UI Blazor-komponentbiblioteket:

Fil Original Komprimerad % minskning
flytande.js 384 73 80,99%
fluent.css 94 11 88.30%
totalt 478 84 82.43%

Totalt från 478 KB okomprimerad till 84 KB komprimerad.

I följande tabell visas de ursprungliga och komprimerade storlekarna med hjälp av biblioteket MudBlazorBlazor-komponenter:

Fil Original Komprimerad Reduktion
MudBlazor.min.css 541 37.5 93.07%
MudBlazor.min.js 47.4 9.2 80,59%
totalt 588,4 46.7 92,07%

Optimering sker automatiskt när du använder MapStaticAssets. När ett bibliotek läggs till eller uppdateras, till exempel med nya JavaScript eller CSS, optimeras tillgångarna som en del av bygget. Optimering är särskilt fördelaktigt för mobila miljöer som kan ha lägre bandbredd eller otillförlitliga anslutningar.

För mer information om de nya funktionerna för filleverans, se följande resurser:

Aktivera dynamisk komprimering på servern jämfört med MapStaticAssets

MapStaticAssets har följande fördelar jämfört med dynamisk komprimering på servern:

  • Är enklare eftersom det inte finns någon serverspecifik konfiguration.
  • Är mer högpresterande eftersom tillgångarna komprimeras vid byggtiden.
  • Gör att utvecklaren kan spendera extra tid under byggprocessen för att säkerställa att tillgångarna är den minsta storleken.

Överväg följande tabell som jämför MudBlazor-komprimering med dynamisk IIS-komprimering och MapStaticAssets:

IIS gzip MapStaticAssets MapStaticAssets minskning
≅ 90 37.5 59%

Blazor

I det här avsnittet beskrivs nya funktioner för Blazor.

.NET MAUI,Blazor Hybrid och webbapps-lösningsmall

En ny lösningsmall gör det enklare att skapa .NET MAUI interna och Blazor webbklientappar som delar samma användargränssnitt. Den här mallen visar hur du skapar klientappar som maximerar återanvändning av kod och mål för Android, iOS, Mac, Windows och Webben.

Viktiga funktioner i den här mallen är:

  • Möjligheten att välja ett Blazor interaktivt återgivningsläge för webbappen.
  • Automatiskt skapande av lämpliga projekt, inklusive en Blazor Web App (global interaktiv automatisk återgivning) och en .NET MAUIBlazor Hybrid app.
  • De skapade projekten använder ett delat Razor klassbibliotek (RCL) för att underhålla användargränssnittets Razor komponenter.
  • Exempelkoden ingår som visar hur du använder beroendeinmatning för att tillhandahålla olika gränssnittsimplementeringar för Blazor Hybrid-appen och Blazor Web App.

Kom igång genom att installera .NET 9 SDK och installera arbetsbelastningen .NET MAUI, som innehåller mallen:

dotnet workload install maui

Skapa en lösning från projektmallen i ett kommandogränssnitt med följande kommando:

dotnet new maui-blazor-web

Mallen är också tillgänglig i Visual Studio.

Obs

För närvarande inträffar ett undantag om Blazor återgivningslägen definieras på nivån per sida/komponent. Mer information finns i BlazorWebView behöver ett sätt att aktivera åsidosättande av ResolveComponentForRenderMode (dotnet/aspnetcore #51235).

Mer information finns i Skapa en .NET MAUIBlazor Hybrid-app med en Blazor Web App.

Identifiera återgivningsplats, interaktivitet och tilldelat återgivningsläge vid körning

Vi har introducerat ett nytt API som är utformat för att förenkla processen med att fråga komponenttillstånd vid körning. Det här API:et innehåller följande funktioner:

  • Fastställa den aktuella körningsplatsen för komponenten: Detta kan vara användbart för felsökning och optimering av komponentprestanda.
  • Kontrollera om komponenten körs i en interaktiv miljö: Detta kan vara användbart för komponenter som har olika beteenden baserat på interaktiviteten i deras miljö.
  • Hämta det tilldelade återgivningsläget för komponenten: Om du förstår återgivningsläget kan du optimera renderingsprocessen och förbättra den övergripande prestandan för en komponent.

För mer information, se ASP.NET Core Blazor återgivningslägen.

Förbättrad upplevelse av återanslutning på serverns sida:

Följande förbättringar har gjorts i standardmiljön för återanslutning på serversidan:

  • När användaren navigerar tillbaka till en app med en frånkopplad krets görs återanslutningsförsök omedelbart i stället för att vänta under nästa återanslutningsintervall. Detta förbättrar användarupplevelsen när man navigerar till en app i en webbläsarflik som har gått i viloläge.

  • När ett återanslutningsförsök når servern men servern redan har släppt kretsen sker en siduppdatering automatiskt. Detta förhindrar att användaren behöver uppdatera sidan manuellt om det sannolikt kommer att resultera i en lyckad återanslutning.

  • När du återansluter används en beräknad backoff-strategi. Som standard sker de första flera återanslutningsförsöken i snabb följd utan ett återförsöksintervall innan beräknade fördröjningar införs mellan försöken. Du kan anpassa beteendet för återförsöksintervall genom att ange en funktion för att beräkna återförsöksintervallet, vilket visas i följande exponentiella backoff-exempel:

    Blazor.start({
      circuit: {
        reconnectionOptions: {
          retryIntervalMilliseconds: (previousAttempts, maxRetries) => 
            previousAttempts >= maxRetries ? null : previousAttempts * 1000
        },
      },
    });
    
  • Formateringen för standardgränssnittet för återanslutning har moderniserats.

Mer information finns i ASP.NET Core BlazorSignalR vägledning.

Förenklad serialisering av autentiseringstillstånd för Blazor Web Apps

Nya API:er gör det enklare att lägga till autentisering i en befintlig Blazor Web App. När du skapar en ny Blazor Web App med autentisering med hjälp av enskilda konton och du aktiverar WebAssembly-baserad interaktivitet, innehåller projektet en anpassad AuthenticationStateProvider i både server- och klientprojekten.

Dessa leverantörer flödar användarens autentiseringstillstånd till webbläsaren. Om du autentiserar på servern i stället för klienten kan appen komma åt autentiseringstillståndet under förinläsningen och innan .NET WebAssembly-körningen initieras.

De anpassade AuthenticationStateProvider-implementeringarna använder tjänsten Persistent Component State (PersistentComponentState) för att serialisera autentiseringstillståndet i HTML-kommentarer och läsa tillbaka det från WebAssembly för att skapa en ny AuthenticationState instans.

Detta fungerar bra om du har startat från Blazor Web App-projektmallen och valt alternativet Enskilda Konton, men det är mycket kod för att implementera själv eller kopiera om du försöker lägga till autentisering i ett befintligt projekt. Det finns nu API:er, som nu ingår i Blazor Web App-projektmallen, som kan anropas i server- och klientprojekten för att lägga till den här funktionen:

Som standard serialiserar API:et endast serversidans namn och rollanspråk för åtkomst i webbläsaren. Ett alternativ kan skickas till AddAuthenticationStateSerialization för att inkludera alla anspråk.

Mer information finns i följande avsnitt i ASP.NET Core Blazor-autentisering och auktorisering:

Lägg till SSR-sidor (statisk återgivning på serversidan) till en globalt interaktiv Blazor Web App

Med lanseringen av .NET 9 är det nu enklare att lägga till statiska SSR-sidor i appar som använder global interaktivitet.

Den här metoden är bara användbar när appen har specifika sidor som inte kan fungera med interaktiv server- eller WebAssembly-återgivning. Anta till exempel den här metoden för sidor som är beroende av att läsa/skriva HTTP-cookies och som bara kan fungera i en begäran/svar-cykel i stället för interaktiv återgivning. För sidor som fungerar med interaktiv rendering bör du inte tvinga dem att använda statisk SSR-återgivning, eftersom det är mindre effektivt och mindre dynamiskt för slutanvändaren.

Markera alla Razor komponentsidor med det nya [ExcludeFromInteractiveRouting]-attributet tilldelat med @attributeRazor-direktivet:

@attribute [ExcludeFromInteractiveRouting]

Om attributet tillämpas kommer navigeringen till sidan att avslutas från interaktiv routning. Inkommande navigering tvingas utföra en fullständig sidåterladdning istället för att lösa sidan via interaktiv routing. Fullsidans omladdning tvingar rotkomponenten på den översta nivån, vanligtvis App-komponenten (App.razor), att renderas om från servern, vilket gör att appen kan växla till ett annat renderingsläge på översta nivån.

Med RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting-tilläggsmetoden kan komponenten identifiera om [ExcludeFromInteractiveRouting]-attributet tillämpas på den aktuella sidan.

I komponenten App använder du mönstret i följande exempel:

  • Sidor som inte är annoterade med attributet [ExcludeFromInteractiveRouting] har som standard InteractiveServer återgivningsläge med global interaktivitet. Du kan ersätta InteractiveServer med InteractiveWebAssembly eller InteractiveAuto för att ange ett annat globalt standardåtergivningsläge.
  • Sidor kommenterade med attributet [ExcludeFromInteractiveRouting] använder statisk SSR (PageRenderMode tilldelas till null).
<!DOCTYPE html>
<html>
<head>
    ...
    <HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
    <Routes @rendermode="@PageRenderMode" />
    ...
</body>
</html>

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? PageRenderMode
        => HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}

Ett alternativ till att använda RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting-tilläggsmetoden är att läsa slutpunktsmetadata manuellt med hjälp av HttpContext.GetEndpoint()?.Metadata.

Den här funktionen omfattas av referensdokumentationen i ASP.NET Core Blazor återgivningslägen.

Konstruktorinjektion

Razor komponenter stöder konstruktorinmatning.

I följande exempel injicerar den delvisa (code-behind) klassen NavigationManager-tjänsten med hjälp av en primärkonstruktor:

public partial class ConstructorInjection(NavigationManager navigation)
{
    private void HandleClick()
    {
        navigation.NavigateTo("/counter");
    }
}

Mer information finns i ASP.NET Core Blazor beroendeinjektion.

Websocket-komprimering för interaktiva serverkomponenter

Som standard aktiverar interaktiva serverkomponenter komprimering för WebSocket-anslutningar och anger ett frame-ancestorsCSP(Content Security Policy) direktiv inställt på 'self', som endast tillåter inbäddning av appen i en <iframe> av det ursprung som appen hanteras från när komprimering är aktiverat eller när en konfiguration för WebSocket-kontexten tillhandahålls.

Komprimering kan inaktiveras genom att ange ConfigureWebSocketOptions till null, vilket minskar appens sårbarhet för angrepp men kan leda till sämre prestanda:

.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

Konfigurera en striktare frame-ancestors CSP med värdet 'none' (enkla citattecken krävs), vilket tillåter WebSocket-komprimering men förhindrar att webbläsare bäddar in appen i alla <iframe>:

.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

Mer information finns i följande resurser:

Hantera tangentbordssammansättningshändelser i Blazor

Den nya egenskapen KeyboardEventArgs.IsComposing anger om tangentbordshändelsen ingår i en kompositionssession. Det är viktigt att spåra kompositionstillståndet för tangentbordshändelser för att hantera indatametoder för internationella tecken.

OverscanCount parameter har lagts till i QuickGrid

Komponenten QuickGrid exponerar nu en OverscanCount-egenskap som anger hur många ytterligare rader som återges före och efter den synliga regionen när virtualisering är aktiverad.

Standard OverscanCount är 3. I följande exempel ökar OverscanCount till 4:

<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
    ...
</QuickGrid>

InputNumber-komponenten stöder attributet type="range"

Komponenten InputNumber<TValue> stöder nu attributet type="range", som skapar ett intervallindata som stöder modellbindning och formulärverifiering, som vanligtvis återges som en skjutreglage eller uppringningskontroll i stället för en textruta:

<EditForm Model="Model" OnSubmit="Submit" FormName="EngineForm">
    <div>
        <label>
            Nacelle Count (2-6): 
            <InputNumber @bind-Value="Model!.NacelleCount" max="6" min="2" 
                step="1" type="range" />
        </label>
    </div>
    <div>
        <button type="submit">Submit</button>
    </div>
</EditForm>

@code {
    [SupplyParameterFromForm]
    private EngineSpecifications? Model { get; set; }

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

    private void Submit() {}

    public class EngineSpecifications
    {
        [Required, Range(minimum: 2, maximum: 6)]
        public int NacelleCount { get; set; }
    }
}

Nya förbättrade navigeringshändelser

Utlösa JavaScript-återanrop före eller efter förbättrad navigering med nya händelselyssnare:

  • blazor.addEventListener("enhancednavigationstart", {CALLBACK})
  • blazor.addEventListener("enhancednavigationend", {CALLBACK})

Mer information finns i ASP.NET Core Blazor JavaScript med statisk återgivning på serversidan (statisk SSR).

SignalR

I det här avsnittet beskrivs nya funktioner för SignalR.

Stöd för polymorf typ i SignalR Hubs

Hubbmetoder kan nu acceptera en basklass i stället för den härledda klassen för att aktivera polymorfa scenarier. Bastypen måste kommenteras för att tillåta polymorfism.

public class MyHub : Hub
{
    public void Method(JsonPerson person)
    {
        if (person is JsonPersonExtended)
        {
        }
        else if (person is JsonPersonExtended2)
        {
        }
        else
        {
        }
    }
}

[JsonPolymorphic]
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
private class JsonPerson
{
    public string Name { get; set; }
    public Person Child { get; set; }
    public Person Parent { get; set; }
}

private class JsonPersonExtended : JsonPerson
{
    public int Age { get; set; }
}

private class JsonPersonExtended2 : JsonPerson
{
    public string Location { get; set; }
}

Förbättrade aktiviteter för SignalR

SignalR har nu en ActivitySource för både hubbservern och klienten.

.NET SignalR-server ActivitySource

SignalR ActivitySource med namnet Microsoft.AspNetCore.SignalR.Server genererar händelser för hubbmetodanrop:

  • Varje metod är en egen aktivitet, så allt som emitterar en aktivitet under ett hubbmetodanrop är under hubbmetodaktiviteten.
  • Hubbmetodaktiviteter har ingen överordnad. Det innebär att de inte paketeras under den långvariga anslutningen SignalR.

I följande exempel används .NET Aspire instrumentpanel och OpenTelemetry--paket:

<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />

Lägg till följande startkod i filen Program.cs:

using OpenTelemetry.Trace;
using SignalRChat.Hubs;

// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        if (builder.Environment.IsDevelopment())
        {
            // View all traces only in development environment.
            tracing.SetSampler(new AlwaysOnSampler());
        }

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
    });

builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());

var app = builder.Build();

Följande är exempelutdata från Aspire-instrumentpanelen:

aktivitetslista för SignalR Hub-metodanropshändelser

.NET SignalR-klient för ActivitySource

SignalR ActivitySource med namnet Microsoft.AspNetCore.SignalR.Client genererar händelser för en SignalR-klient:

  • .NET SignalR-klienten har en ActivitySource med namnet Microsoft.AspNetCore.SignalR.Client. Hubbanrop skapar nu ett klientsegment. Observera att andra SignalR klienter, till exempel JavaScript-klienten, inte stöder spårning. Den här funktionen kommer att läggas till i fler klienter i framtida versioner.
  • Anrop till hubben både på klienten och servern stöder kontextspridning. Spridning av spårningskontexten möjliggör sann distribuerad spårning. Nu är det möjligt att se anropsflödet från klienten till servern och tillbaka.

Så här ser de här nya aktiviteterna ut på .NET Aspire instrumentpanelen:

SignalR distribuerad spårning i Aspire-instrumentpanel

SignalR stöder trimning och inbyggd AOT

Om vi fortsätter interna AOT-resan startade i .NET 8 har vi aktiverat stöd för trimning och intern AOT-kompilering i förväg för både SignalR klient- och serverscenarier. Nu kan du dra nytta av prestandafördelarna med att använda intern AOT i program som använder SignalR för webbkommunikation i realtid.

Komma igång

Installera den senaste .NET 9 SDK.

Skapa en lösning från webapiaot-mallen i ett kommandogränssnitt med följande kommando:

dotnet new webapiaot -o SignalRChatAOTExample

Ersätt innehållet i Program.cs-filen med följande SignalR kod:

using Microsoft.AspNetCore.SignalR;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateSlimBuilder(args);

builder.Services.AddSignalR();
builder.Services.Configure<JsonHubProtocolOptions>(o =>
{
    o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

var app = builder.Build();

app.MapHub<ChatHub>("/chatHub");
app.MapGet("/", () => Results.Content("""
<!DOCTYPE html>
<html>
<head>
    <title>SignalR Chat</title>
</head>
<body>
    <input id="userInput" placeholder="Enter your name" />
    <input id="messageInput" placeholder="Type a message" />
    <button onclick="sendMessage()">Send</button>
    <ul id="messages"></ul>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
    <script>
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/chatHub")
            .build();

        connection.on("ReceiveMessage", (user, message) => {
            const li = document.createElement("li");
            li.textContent = `${user}: ${message}`;
            document.getElementById("messages").appendChild(li);
        });

        async function sendMessage() {
            const user = document.getElementById("userInput").value;
            const message = document.getElementById("messageInput").value;
            await connection.invoke("SendMessage", user, message);
        }

        connection.start().catch(err => console.error(err));
    </script>
</body>
</html>
""", "text/html"));

app.Run();

[JsonSerializable(typeof(string))]
internal partial class AppJsonSerializerContext : JsonSerializerContext { }

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

I föregående exempel skapas en intern Körbar Windows-fil på 10 MB och en Körbar Linux-fil på 10,9 MB.

Begränsningar

  • Endast JSON-protokollet stöds för närvarande:
    • Som du ser i föregående kod måste appar som använder JSON-serialisering och intern AOT använda System.Text.Json-källgeneratorn.
    • Detta följer samma metod som minimala API:er.
  • På SignalR-servern stöds inte hubbmetodparametrar av typen IAsyncEnumerable<T> och ChannelReader<T> där T är en ValueType (struct). Om du använder dessa typer resulterar det i ett körningsundantag vid start, både under utveckling och i den publicerade appen. Mer information finns i SignalR: Använda IAsyncEnumerable<T> och ChannelReader<T> med ValueTypes i native AOT (dotnet/aspnetcore #56179).
  • Hubbar med stark typ stöds inte med intern AOT (PublishAot). Om du använder starkt typade hubbar med Native AOT resulterar det i varningar under bygg- och publiceringsprocessen samt ett körtidsundantag. Det finns stöd för att använda starkt typade hubbar med trimning (PublishedTrimmed).
  • Endast Task, Task<T>, ValueTaskeller ValueTask<T> stöds för asynkrona returtyper.

Minimala API:er

I det här avsnittet beskrivs nya funktioner för minimala API:er.

InternalServerError och InternalServerError<TValue> har lagts till i TypedResults

Klassen TypedResults är ett användbart verktyg för att returnera starkt skrivna HTTP-statuskodbaserade svar från ett minimalt API. TypedResults innehåller nu fabriksmetoder och typer för att returnera svar om "500 internt serverfel" från slutpunkter. Här är ett exempel som returnerar ett svar på 500:

var app = WebApplication.Create();

app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));

app.Run();

Anropa ProducesProblem och ProducesValidationProblem i routningsgrupper

Tilläggsmetoderna ProducesProblem och ProducesValidationProblem har uppdaterats för att stödja deras användning i routningsgrupper. Dessa metoder anger att alla slutpunkter i en routningsgrupp kan returnera ProblemDetails eller ValidationProblemDetails svar för OpenAPI-metadata.

var app = WebApplication.Create();

var todos = app.MapGroup("/todos")
    .ProducesProblem();

todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));

app.Run();

record Todo(int Id, string Title, boolean IsCompleted);

Problem och ValidationProblem resultattyper stöder konstruktion med IEnumerable<KeyValuePair<string, object?>> värden

Före .NET 9 krävde skapandet av Problem och ValidationProblem resultattyper i minimala API:er att egenskaperna errors och extensions initierades med en implementering av IDictionary<string, object?>. I den här versionen stöder dessa bygg-API:er överlagringar som förbrukar IEnumerable<KeyValuePair<string, object?>>.

var app = WebApplication.Create();

app.MapGet("/", () =>
{
    var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
    return TypedResults.Problem("This is an error with extensions",
                                                       extensions: extensions);
});

Tack vare GitHub-användaren joegoldman2 för det här bidraget!

OpenAPI

I det här avsnittet beskrivs nya funktioner för OpenAPI

Inbyggt stöd för OpenAPI-dokumentgenerering

OpenAPI-specifikationen är en standard för att beskriva HTTP-API:er. Standarden gör att utvecklare kan definiera formen på API:er som kan anslutas till klientgeneratorer, servergeneratorer, testverktyg, dokumentation med mera. I .NET 9 ger ASP.NET Core inbyggt stöd för att generera OpenAPI-dokument som representerar kontrollantbaserade eller minimala API:er via Microsoft.AspNetCore.OpenApi--paketet.

Följande markerade kodanrop:

  • AddOpenApi för att registrera nödvändiga beroenden i appens DI-container.
  • MapOpenApi för att registrera nödvändiga OpenAPI-slutpunkter i appens vägar.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Installera Microsoft.AspNetCore.OpenApi-paketet i projektet med hjälp av följande kommando:

dotnet add package Microsoft.AspNetCore.OpenApi

Kör appen och gå till openapi/v1.json för att visa det genererade OpenAPI-dokumentet:

OpenAPI-dokument

OpenAPI-dokument kan också genereras vid byggtid genom att lägga till Microsoft.Extensions.ApiDescription.Server-paketet:

dotnet add package Microsoft.Extensions.ApiDescription.Server

Om du vill ändra platsen för de avgivna OpenAPI-dokumenten anger du målsökvägen i egenskapen OpenApiDocumentsDirectory i appens projektfil:

<PropertyGroup>
  <OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>

Kör dotnet build och inspektera den genererade JSON-filen i projektkatalogen.

OpenAPI-dokumentgenerering vid byggtid

ASP.NET Cores inbyggda OpenAPI-dokumentgenerering ger stöd för olika anpassningar och alternativ. Den tillhandahåller dokument-, åtgärds- och schematransformatorer och har möjlighet att hantera flera OpenAPI-dokument för samma program.

Mer information om ASP.NET Cores nya OpenAPI-dokumentfunktioner finns i de nya Dokumenten för Microsoft.AspNetCore.OpenApi.

Microsoft.AspNetCore.OpenApi stöder trimning och intern AOT

OpenAPI i ASP.NET Core stöder trimning och intern AOT. Följande steg skapar och publicerar en OpenAPI-app med trimning och inbyggd AOT:

Skapa ett nytt ASP.NET Core Web API-projekt (native AOT).

dotnet new webapiaot

Lägg till Paketet Microsoft.AspNetCore.OpenAPI.

dotnet add package Microsoft.AspNetCore.OpenApi

Uppdatera Program.cs för att aktivera generering av OpenAPI-dokument.

+ builder.Services.AddOpenApi();

var app = builder.Build();

+ app.MapOpenApi();

Publicera appen.

dotnet publish

Autentisering och auktorisering

I det här avsnittet beskrivs nya funktioner för autentisering och auktorisering.

OpenIdConnectHandler lägger till stöd för push-begäranden (PAR)

Vi vill tacka Joe DeCock från Duende Software för att ha lagt till Pushed Authorization Requests (PAR) i ASP.NET Cores OpenIdConnectHandler. Joe beskrev bakgrunden och motivationen för att aktivera PAR i sitt API-förslag på följande sätt:

Pushed Authorization Requests (PAR) är en relativt ny OAuth-standard som förbättrar säkerheten för OAuth- och OIDC-flöden genom att flytta auktoriseringsparametrar från den främre kanalen till den bakre kanalen. Det vill säga, flytta auktoriseringsparametrar från omdirigerings-URL:er i webbläsaren till direkta maskin-till-maskin-http-anrop på bakänden.

Detta hindrar en cyberattacker i webbläsaren från att:

  • Se auktoriseringsparametrar som kan läcka personligt identifierbar information.
  • Manipulering av dessa parametrar. Cyberattacker kan till exempel ändra omfattningen för den åtkomst som begärs.

Genom att skicka auktorisationsparametrarna håller du begärande-URL:er korta. Auktoriseringsparametrar kan bli mycket långa när du använder mer komplexa OAuth- och OIDC-funktioner, till exempel Rich Authorization Requests. URL:er som är långa orsakar problem i många webbläsare och nätverksinfrastrukturer.

Användningen av PAR uppmuntras av FAPI-arbetsgrupp inom OpenID Foundation. säkerhetsprofilen FAPI2.0 kräver till exempel användning av PAR. Den här säkerhetsprofilen används av många av de grupper som arbetar med öppen bankverksamhet (främst i Europa), inom hälso- och sjukvården och i andra branscher med höga säkerhetskrav.

PAR stöds av ett antal identity leverantörer, inklusive

För .NET 9 har vi beslutat att aktivera PAR som standard om identity-providerns identifieringsdokument annonserar stöd för PAR, eftersom det bör ge förbättrad säkerhet för leverantörer som stöder det. identity leverantörens discovery-dokument finns vanligtvis på .well-known/openid-configuration. Om detta orsakar problem kan du inaktivera PAR via OpenIdConnectOptions.PushedAuthorizationBehavior på följande sätt:

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect("oidc", oidcOptions =>
    {
        // Other provider-specific configuration goes here.

        // The default value is PushedAuthorizationBehavior.UseIfAvailable.

        // 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
        // and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
        // of type 'OpenIdConnectOptions' could be found
        oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
    });

Om du vill se till att autentiseringen endast lyckas om PAR används använder du PushedAuthorizationBehavior.Require i stället. Den här ändringen introducerar också en ny OnPushAuthorization-händelse till OpenIdConnectEvents som kan användas för att anpassa den pushade auktoriseringsbegäran eller hantera den manuellt. Mer information finns i API-förslag.

OIDC- och OAuth-parameteranpassning

OAuth- och OIDC-autentiseringshanterare har nu ett AdditionalAuthorizationParameters alternativ för att göra det enklare att anpassa auktoriseringsmeddelandeparametrar som vanligtvis ingår som en del av omdirigeringsfrågesträngen. I .NET 8 och tidigare kräver detta en anpassad OnRedirectToIdentityProvider callback eller åsidosatt BuildChallengeUrl metod i en anpassad hanterare. Här är ett exempel på .NET 8-kod:

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.Events.OnRedirectToIdentityProvider = context =>
    {
        context.ProtocolMessage.SetParameter("prompt", "login");
        context.ProtocolMessage.SetParameter("audience", "https://api.example.com");
        return Task.CompletedTask;
    };
});

Föregående exempel kan nu förenklas till följande kod:

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.AdditionalAuthorizationParameters.Add("prompt", "login");
    options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});

Konfigurera HTTP.sys utökade autentiseringsflaggor

Nu kan du konfigurera flaggorna HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING och HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL HTTP.sys med hjälp av de nya egenskaperna EnableKerberosCredentialCaching och CaptureCredentials på HTTP.sys AuthenticationManager för att optimera hur Windows-autentisering hanteras. Till exempel:

webBuilder.UseHttpSys(options =>
{
    options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
    options.Authentication.EnableKerberosCredentialCaching = true;
    options.Authentication.CaptureCredentials = true;
});

Diverse

I följande avsnitt beskrivs diverse nya funktioner.

Nytt HybridCache-bibliotek

Viktig

HybridCache är för närvarande fortfarande i förhandsversion men kommer att släppas helt efter .NET 9.0 i en framtida mindre uppdatering av .NET Extensions.

Det HybridCache API:et överbryggar vissa luckor i befintliga IDistributedCache- och IMemoryCache API:er. Den lägger också till nya funktioner, till exempel:

  • "Stampede"-skydd för att förhindra parallella hämtningar av samma arbete.
  • Konfigurerbar serialisering.

HybridCache är utformat för att vara en dropp-in ersättning för befintlig IDistributedCache- och IMemoryCache-användningar, och det ger ett enkelt API för att addera ny cachekod. Det tillhandahåller ett enhetligt API för både intern och extern processbaserad cachelagring.

Om du vill se hur HybridCache API:et förenklas kan du jämföra det med kod som använder IDistributedCache. Här är ett exempel på hur användning av IDistributedCache ser ut:

public class SomeService(IDistributedCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
        var bytes = await cache.GetAsync(key, token); // Try to get from cache.
        SomeInformation info;
        if (bytes is null)
        {
            // Cache miss; get the data from the real source.
            info = await SomeExpensiveOperationAsync(name, id, token);

            // Serialize and cache it.
            bytes = SomeSerializer.Serialize(info);
            await cache.SetAsync(key, bytes, token);
        }
        else
        {
            // Cache hit; deserialize it.
            info = SomeSerializer.Deserialize<SomeInformation>(bytes);
        }
        return info;
    }

    // This is the work we're trying to cache.
    private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
        CancellationToken token = default)
    { /* ... */ }
}

Det är mycket arbete att få rätt varje gång, inklusive saker som serialisering. Och i scenariot med cachemissen kan du få flera samtidiga trådar, alla får en cachemiss, alla hämtar underliggande data, alla serialiserar dem och alla skickar dessa data till cachen.

För att förenkla och förbättra den här koden med HybridCachemåste vi först lägga till det nya biblioteket Microsoft.Extensions.Caching.Hybrid:

<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />

Registrera HybridCache-tjänsten, som om du skulle registrera en IDistributedCache implementering:

builder.Services.AddHybridCache(); // Not shown: optional configuration API.

Nu kan de flesta cachelagringsproblem avlastas till HybridCache:

public class SomeService(HybridCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        return await cache.GetOrCreateAsync(
            $"someinfo:{name}:{id}", // Unique key for this combination.
            async cancel => await SomeExpensiveOperationAsync(name, id, cancel),
            token: token
        );
    }
}

Vi tillhandahåller en konkret implementering av den HybridCache abstrakta klassen via beroendeinmatning, men det är avsett att utvecklare kan tillhandahålla anpassade implementeringar av API:et. Implementeringen HybridCache behandlar allt som rör cachelagring, inklusive samtidig åtgärdshantering. Den cancel token här representerar den kombinerade annulleringen av alla samtidiga anropare– inte bara annulleringen av den anropare som vi kan se (det vill säga token).

Scenarier med högt dataflöde kan optimeras ytterligare med hjälp av mönstret TState för att undvika vissa omkostnader från insamlade variabler och återanrop per instans:

public class SomeService(HybridCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
    {
        return await cache.GetOrCreateAsync(
            $"someinfo:{name}:{id}", // unique key for this combination
            (name, id), // all of the state we need for the final call, if needed
            static async (state, token) =>
                await SomeExpensiveOperationAsync(state.name, state.id, token),
            token: token
        );
    }
}

HybridCache använder den konfigurerade IDistributedCache implementeringen, om någon, för sekundär cachelagring utan process, till exempel med Redis. Men även utan en IDistributedCacheger HybridCache-tjänsten fortfarande processbaserad cachelagring och "stampede"-skydd.

En anteckning om återanvändning av objekt

I vanlig befintlig kod som använder IDistributedCacheresulterar varje hämtning av ett objekt från cachen i deserialisering. Det här beteendet innebär att varje samtidig anropare får en separat instans av objektet, som inte kan interagera med andra instanser. Resultatet är trådsäkerhet eftersom det inte finns någon risk för samtidiga ändringar av samma objektinstans.

Eftersom mycket HybridCache användning kommer att anpassas från befintlig IDistributedCache kod, bevarar HybridCache det här beteendet som standard för att undvika samtidighetsbuggar. Ett angivet användningsfall är dock i sig trådsäkert:

  • Om de typer som cachelagras är oföränderliga.
  • Om koden inte ändrar dem.

I sådana fall bör du informera HybridCache om att det är säkert att återanvända instanser genom att:

  • Markera typen som sealed. Nyckelordet sealed i C# innebär att klassen inte kan ärvas.
  • Tillämpa attributet [ImmutableObject(true)] på det. Attributet [ImmutableObject(true)] anger att objektets tillstånd inte kan ändras när det har skapats.

Genom att återanvända instanser kan HybridCache minska kostnaderna för PROCESSOR- och objektallokeringar som är associerade med deserialisering per anrop. Detta kan leda till prestandaförbättringar i scenarier där cachelagrade objekt är stora eller används ofta.

Andra HybridCache funktioner

Precis som IDistributedCachestöder HybridCache borttagning efter nyckel med en RemoveKeyAsync-metod.

HybridCache tillhandahåller även valfria API:er för IDistributedCache implementeringar för att undvika byte[] allokeringar. Den här funktionen implementeras av förhandsversionerna av paketen Microsoft.Extensions.Caching.StackExchangeRedis och Microsoft.Extensions.Caching.SqlServer.

Serialisering konfigureras som en del av registreringen av tjänsten, med stöd för typspecifika och generaliserade serialiserare via metoderna WithSerializer och .WithSerializerFactory, kedjade från AddHybridCache-anropet. Som standard hanterar biblioteket string och byte[] internt och använder System.Text.Json för allt annat, men du kan använda protobuf, xml eller något annat.

HybridCache stöder äldre .NET-körningar, ned till .NET Framework 4.7.2 och .NET Standard 2.0.

Mer information om HybridCachefinns i HybridCache-biblioteket i ASP.NET Core

Förbättringar av undantagssidan för utvecklare

Undantagssidan för ASP.NET Core-utvecklare visas när en app genererar ett ohanterat undantag under utvecklingen. Undantagssidan för utvecklare innehåller detaljerad information om undantaget och begäran.

Förhandsversion 3 lade till slutpunktsmetadata på undantagssidan för utvecklare. ASP.NET Core använder slutpunktsmetadata för att styra slutpunktsbeteendet, till exempel routning, cachelagring av svar, hastighetsbegränsning, OpenAPI-generering med mera. Följande bild visar den nya metadatainformationen i avsnittet Routing på undantagssidan för utvecklare:

Den nya metadatainformationen på undantagssidan för utvecklare

När utvecklaren testade undantagssidan identifierades små förbättringar av livskvaliteten. De levererades i förhandsversion 4:

  • Bättre textbrytning. Långa cookies, frågesträngsvärden och metodnamn lägger inte längre till vågräta webbläsarrullningslister.
  • Större text som finns i modern design.
  • Mer konsekventa tabellstorlekar.

Följande animerade bild visar den nya undantagssidan för utvecklare:

Den nya undantagssidan för utvecklare

Förbättringar av felsökning av ordlista

Felsökningsvisningen av ordlistor och andra nyckel/värde-samlingar har en förbättrad layout. Nyckeln visas i felsökarens nyckelkolumn i stället för att sammanfogas med värdet. Följande bilder visar den gamla och nya visningen av en ordlista i felsökningsprogrammet.

Före:

Den tidigare felsökningsupplevelsen

Efter:

Den nya felsökningsupplevelsen

ASP.NET Core har många nyckel/värde-samlingar. Den här förbättrade felsökningsupplevelsen gäller för:

  • HTTP-huvuden
  • Frågesträngar
  • Blanketter
  • Cookies
  • Visa data
  • Routa data
  • Funktioner

Korrigering för 503-fel under appåterinställning i IIS

Som standard är det nu en fördröjning på 1 sekund mellan när IIS meddelas om en återvinning eller avstängning och när ANCM uppmanar den hanterade servern att börja stängas. Fördröjningen kan konfigureras via miljövariabeln ANCM_shutdownDelay eller genom att ange inställningen för shutdownDelay-hanteraren. Båda värdena finns i millisekunder. Fördröjningen är främst för att minska sannolikheten för en tävling där:

  • IIS har inte börjat köa begäranden om att gå till den nya appen.
  • ANCM börjar avvisa nya begäranden som kommer in i den gamla appen.

Långsammare datorer eller datorer med tyngre CPU-användning kanske vill justera det här värdet för att minska sannolikheten för 503.

Exempel på inställning shutdownDelay:

<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
  <handlerSettings>
    <!-- Milliseconds to delay shutdown by.
    this doesn't mean incoming requests will be delayed by this amount,
    but the old app instance will start shutting down after this timeout occurs -->
    <handlerSetting name="shutdownDelay" value="5000" />
  </handlerSettings>
</aspNetCore>

Korrigeringen finns i den globalt installerade ANCM-modulen som kommer från värdpaketet.

ASP0026: Analysator för att varna när [Authorize] åsidosätts av [AllowAnonymous] längre bort i koden

Det verkar intuitivt att ett [Authorize] attribut som placeras "närmare" en MVC-åtgärd än ett [AllowAnonymous] attribut skulle åsidosätta [AllowAnonymous]-attributet och tvinga auktorisering. Detta är dock inte nödvändigtvis fallet. Vad som spelar roll är attributens relativa ordning.

Följande kod visar exempel där ett närmare [Authorize] attribut åsidosätts av ett [AllowAnonymous] attribut som ligger längre bort.

[AllowAnonymous]
public class MyController
{
    [Authorize] // Overridden by the [AllowAnonymous] attribute on the class
    public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}

[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}

public class MyControllerInherited2 : MyControllerAnon
{
    [Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
    public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}

I .NET 9 Preview 6 har vi introducerat en analysator som visar instanser som dessa där ett närmare [Authorize] attribut åsidosätts av ett [AllowAnonymous] attribut som ligger längre bort från en MVC-åtgärd. Varningen pekar på det åsidosatta attributet [Authorize] med följande meddelande:

ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away

Rätt åtgärd om du ser den här varningen beror på avsikten bakom attributen. Längre bort [AllowAnonymous]-attributet bör tas bort om det oavsiktligt exponerar slutpunkten för anonyma användare. Om attributet [AllowAnonymous] var avsett att åsidosätta ett närmare [Authorize] attribut kan du upprepa attributet [AllowAnonymous] efter attributet [Authorize] för att förtydliga avsikten.

[AllowAnonymous]
public class MyController
{
    // This produces no warning because the second, "closer" [AllowAnonymous]
    // clarifies that [Authorize] is intentionally overridden.
    // Specifying AuthenticationSchemes can still be useful
    // for endpoints that allow but don't require authenticated users.
    [Authorize(AuthenticationSchemes = "Cookies")]
    [AllowAnonymous]
    public IActionResult Privacy() => null;
}

Förbättrade Kestrel anslutningsmetrik

Vi har gjort en betydande förbättring av Kestrelanslutningsmått genom att inkludera metadata om varför en anslutning misslyckades. Måttet kestrel.connection.duration innehåller nu orsaken till att anslutningen stängdes i attributet error.type.

Här är ett litet exempel på de error.type värdena:

  • tls_handshake_failed – Anslutningen kräver TLS och TLS-handskakningen misslyckades.
  • connection_reset – Anslutningen stängdes oväntat av klienten medan begäranden pågick.
  • request_headers_timeout – Kestrel stängde anslutningen eftersom den inte tog emot begärandehuvuden i tid.
  • max_request_body_size_exceeded – Kestrel stängde anslutningen eftersom uppladdade data överskred maxstorleken.

Tidigare krävde diagnos av Kestrel anslutningsproblem en server för att registrera detaljerad loggning på låg nivå. Loggar kan dock vara dyra att generera och lagra, och det kan vara svårt att hitta rätt information bland bruset.

Mått är ett mycket billigare alternativ som kan lämnas kvar i en produktionsmiljö med minimal påverkan. Insamlade mått kan styra instrumentpaneler och aviseringar. När ett problem har identifierats på en hög nivå med mått kan ytterligare undersökning med hjälp av loggning och andra verktyg börja.

Vi förväntar oss att förbättrade anslutningsmått är användbara i många scenarier:

  • Undersöka prestandaproblem som orsakas av korta anslutningslivslängder.
  • Observera pågående externa attacker på Kestrel som påverkar prestanda och stabilitet.
  • Registrering av försök till externa attacker på Kestrel som Kestrel:s inbyggda säkerhetsskydd förhindrade.

För mer information, se ASP.NET Core metrics.

Anpassa Kestrel namngivna rörslutpunkter

Kestrel:s namngivna rörstöd har förbättrats med avancerade anpassningsalternativ. Med den nya CreateNamedPipeServerStream metoden för de namngivna röralternativen kan rör anpassas per slutpunkt.

Ett exempel på var detta är användbart är en Kestrel app som kräver två pipe-slutpunkter med olika åtkomstsäkerhet. Alternativet CreateNamedPipeServerStream kan användas för att skapa rör med anpassade säkerhetsinställningar, beroende på rörnamnet.

var builder = WebApplication.CreateBuilder();

builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenNamedPipe("pipe1");
    options.ListenNamedPipe("pipe2");
});

builder.WebHost.UseNamedPipes(options =>
{
    options.CreateNamedPipeServerStream = (context) =>
    {
        var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);

        return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut,
            NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
            context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
    };
});

ExceptionHandlerMiddleware alternativ för att välja statuskod baserat på undantagstyp

Ett nytt alternativ när du konfigurerar ExceptionHandlerMiddleware gör det möjligt för apputvecklare att välja vilken statuskod som ska returneras när ett undantag inträffar under hanteringen av begäranden. Det nya alternativet ändrar statuskoden som anges i ProblemDetails-svaret från ExceptionHandlerMiddleware.

app.UseExceptionHandler(new ExceptionHandlerOptions
{
    StatusCodeSelector = ex => ex is TimeoutException
        ? StatusCodes.Status503ServiceUnavailable
        : StatusCodes.Status500InternalServerError,
});

Hoppa över HTTP-mått vid vissa slutpunkter och begäranden

.NET 9 introducerar möjligheten att välja bort HTTP-mått för specifika slutpunkter och begäranden. Att välja bort inspelningsmått är fördelaktigt för slutpunkter som ofta anropas av automatiserade system, till exempel hälsokontroller. Det är vanligtvis onödigt att registrera mått för dessa begäranden.

HTTP-begäranden till en slutpunkt kan undantas från mått genom att lägga till metadata. Antingen:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();

var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();

Egenskapen MetricsDisabled har lagts till i IHttpMetricsTagsFeature för:

  • Avancerade scenarier där en begäran inte mappas till en slutpunkt.
  • Dynamiskt inaktiverar måttsamling för specifika HTTP-begäranden.
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
    var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
    if (metricsFeature != null &&
        context.Request.Headers.ContainsKey("x-disable-metrics"))
    {
        metricsFeature.MetricsDisabled = true;
    }

    await next(context);
});

Dataskyddsstöd för att ta bort nycklar

Före .NET 9 var dataskyddsnycklar inte avsiktligt borttagningsbara för att förhindra dataförlust. Om du tar bort en nyckel blir dess skyddade data oåterkalleliga. Med tanke på deras lilla storlek innebar ackumuleringen av dessa nycklar i allmänhet minimal inverkan. Men för att hantera mycket långvariga tjänster har vi infört alternativet att ta bort nycklar. I allmänhet bör endast gamla nycklar tas bort. Ta bara bort nycklar när du kan acceptera risken för dataförlust i utbyte mot lagringsbesparingar. Vi rekommenderar att dataskyddsnycklar inte tas bort.

using Microsoft.AspNetCore.DataProtection.KeyManagement;

var services = new ServiceCollection();
services.AddDataProtection();

var serviceProvider = services.BuildServiceProvider();

var keyManager = serviceProvider.GetService<IKeyManager>();

if (keyManager is IDeletableKeyManager deletableKeyManager)
{
    var utcNow = DateTimeOffset.UtcNow;
    var yearAgo = utcNow.AddYears(-1);

    if (!deletableKeyManager.DeleteKeys(key => key.ExpirationDate < yearAgo))
    {
        Console.WriteLine("Failed to delete keys.");
    }
    else
    {
        Console.WriteLine("Old keys deleted successfully.");
    }
}
else
{
    Console.WriteLine("Key manager does not support deletion.");
}

Mellanprogram har stöd för keyed DI

Middleware stöder nu Keyed DI- i både konstruktorn och Invoke-/-InvokeAsync-metoden:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");

var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();

internal class MyMiddleware
{
    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next,
        [FromKeyedServices("test")] MySingletonClass service)
    {
        _next = next;
    }

    public Task Invoke(HttpContext context,
        [FromKeyedServices("test2")]
            MyScopedClass scopedService) => _next(context);
}

Lita på ASP.NET Core HTTPS-utvecklingscertifikat i Linux

På Ubuntu- och Fedora-baserade Linux-distributioner konfigurerar dotnet dev-certs https --trust nu ASP.NET Core HTTPS-utvecklingscertifikat som ett betrott certifikat för:

  • Chromium-webbläsare, till exempel Google Chrome, Microsoft Edge och Chromium.
  • Mozilla Firefox och Mozilla härledda webbläsare.
  • .NET-API:er, till exempel HttpClient

Tidigare fungerade --trust bara på Windows och macOS. Certifikatförtroende tillämpas per användare.

För att upprätta förtroende för OpenSSL dev-certs verktyget:

  • Placerar certifikatet i ~/.aspnet/dev-certs/trust
  • Kör en förenklad version av OpenSSL:s c_rehash verktyg i katalogen.
  • Uppmanar användaren att uppdatera miljövariabeln SSL_CERT_DIR.

För att upprätta förtroende för dotnet placerar verktyget certifikatet i My/Root certifikatarkivet.

För att upprätta förtroende för NSS-databaser, om någon finns, letar verktyget i katalogen home efter Firefox-profiler, ~/.pki/nssdboch ~/snap/chromium/current/.pki/nssdb. För varje katalog som hittas lägger verktyget till en post i nssdb.

Mallar uppdaterade till de senaste bootstrap-, jQuery- och jQuery-valideringsversionerna

De ASP.NET Core-projektmallarna och biblioteken har uppdaterats för att använda de senaste versionerna av Bootstrap, jQuery och jQuery Validation, specifikt:

  • Bootstrap 5.3.3
  • jQuery 3.7.1
  • jQuery Validation 1.21.0