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 ochgzip + 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:
- AddAuthenticationStateSerialization: Lägger till de tjänster som krävs för att serialisera autentiseringstillståndet på servern.
- AddAuthenticationStateDeserialization: Lägger till nödvändiga tjänster för att deserialisera autentiseringstillståndet i webbläsaren.
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:
- Blazor Identity användargränssnitt (enskilda konton)
- Hantera autentiseringstillstånd i Blazor Web Apps
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 @attribute
Razor-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 standardInteractiveServer
återgivningsläge med global interaktivitet. Du kan ersättaInteractiveServer
medInteractiveWebAssembly
ellerInteractiveAuto
för att ange ett annat globalt standardåtergivningsläge. - Sidor kommenterade med attributet
[ExcludeFromInteractiveRouting]
använder statisk SSR (PageRenderMode
tilldelas tillnull
).
<!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-ancestors
CSP(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:
- ASP.NET Core BlazorSignalR vägledning
- Vägledning för hotreducering för ASP.NET Core Blazor interaktiv återgivning på serversidan
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:
.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 namnetMicrosoft.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 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.
- Som du ser i föregående kod måste appar som använder JSON-serialisering och intern AOT använda
- På SignalR-servern stöds inte hubbmetodparametrar av typen
IAsyncEnumerable<T>
ochChannelReader<T>
därT
ä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>
,ValueTask
ellerValueTask<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 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.
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 HybridCache
må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 IDistributedCache
ger HybridCache
-tjänsten fortfarande processbaserad cachelagring och "stampede"-skydd.
En anteckning om återanvändning av objekt
I vanlig befintlig kod som använder IDistributedCache
resulterar 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
. Nyckelordetsealed
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 IDistributedCache
stö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 HybridCache
finns 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:
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:
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:
Efter:
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:
- Lägg till attributet
[DisableHttpMetrics]
till webb-API-kontrollanten, SignalR hubb eller gRPC-tjänsten. - Anropa DisableHttpMetrics vid mappning av slutpunkter i appstart:
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/nssdb
och ~/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
ASP.NET Core