Neuerungen in ASP.NET Core 9.0
In diesem Artikel werden die wichtigsten Änderungen in ASP.NET Core 9.0 aufgezeigt und Links zur relevanten Dokumentation bereitgestellt.
Dieser Artikel wurde für .NET 9 Release Candidate 2 aktualisiert.
Blazor
In diesem Abschnitt werden neue Features für Blazor beschrieben.
.NET MAUIBlazor Hybrid und Web App-Lösungsvorlage
Eine neue Lösungsvorlage erleichtert die Erstellung von .NET MAUI-nativen und Blazor-Web-Client-Apps, die dieselbe Benutzeroberfläche verwenden. Diese Vorlage zeigt, wie Sie Client-Apps erstellen, welche die Wiederverwendung von Code maximieren und Android, iOS, Mac, Windows und Web verwenden.
Zu den wichtigsten Funktionen dieser Vorlage gehören:
- Die Möglichkeit, einen interaktiven Blazor-Rendering-Modus für die Web-App zu wählen.
- Automatische Erstellung der entsprechenden Projekte, einschließlich einer Blazor Web App (globales interaktives Autorendering) und einer .NET MAUIBlazor Hybrid-App.
- Die erstellten Projekte verwenden eine freigegebene Razor Klassenbibliothek (RCL), um die Komponenten der Benutzeroberfläche Razor zu verwalten.
- Beispielcode ist enthalten, der veranschaulicht, wie Abhängigkeitsinjektion verwendet wird, um verschiedene Schnittstellenimplementierungen für die Blazor Hybrid-App und die Blazor Web App bereitzustellen.
Um loszulegen, installieren Sie das .NET 9 SDK und installieren Sie den Workload .NET MAUI, der die Vorlage enthält:
dotnet workload install maui
Erstellen Sie eine Projektmappe aus der Projektvorlage in einer Befehlsshell mithilfe des folgenden Befehls:
dotnet new maui-blazor-web
Die Vorlage ist auch in Visual Studio verfügbar.
Hinweis
Derzeit tritt eine Ausnahme auf, wenn Blazor-Rendermodi auf Seiten-/Komponentenebene definiert sind. Weitere Informationen finden Sie unter BlazorWebView benötigt eine Möglichkeit, ResolveComponentForRenderMode (dotnet/aspnetcore
#51235) zu aktivieren.
Weitere Informationen finden Sie unter Erstellen einer .NET MAUIBlazor Hybrid-App mit einer Blazor Web App.
Optimierung der Übermittlung statischer Ressourcen
MapStaticAssets
ist eine neue Middleware, welche die Bereitstellung statischer Ressourcen in jeder ASP.NET Core-App, einschließlich Blazor-Apps, optimiert.
Weitere Informationen finden Sie in den folgenden Ressourcen:
- Der Abschnitt zur Optimierung der Bereitstellung statischer Webobjekte in diesem Artikel.
- ASP.NET Core Blazor statische Dateien.
Erkennen des Renderorts, der Interaktivität und des zugewiesenen Rendermodus zur Laufzeit
Wir haben eine neue API eingeführt, die das Abfragen von Komponentenzuständen zur Laufzeit vereinfacht. Diese API bietet die folgenden Funktionen:
- Ermitteln Sie den aktuellen Ausführungsort der Komponente: Dies kann besonders hilfreich beim Debuggen und Optimieren der Komponentenleistung sein.
- Überprüfen Sie, ob die Komponente in einer interaktiven Umgebung ausgeführt wird: Dies kann für Komponenten hilfreich sein, die je nach Interaktivität ihrer Umgebung unterschiedliche Verhaltensweisen aufweisen.
- Abrufen des zugewiesenen Rendermodus für die Komponente: Das Verständnis des Rendermodus kann dabei helfen, den Renderingprozess zu optimieren und die Gesamtleistung einer Komponente zu verbessern.
Weitere Informationen finden Sie unter ASP.NET CoreBlazor-Rendermodi.
Verbesserte serverseitige erneute Verbindung:
Die folgenden Verbesserungen wurden an der serverseitigen Standardverbindungsumgebung vorgenommen:
Wenn der Benutzer zurück zu einer App mit einer getrennten Verbindung navigiert, wird die erneute Verbindung sofort versucht, anstatt auf die Dauer des nächsten erneuten Verbindungsintervalls zu warten. Dies verbessert die Benutzererfahrung, wenn Sie in einer Browser-Registerkarte, die in den Ruhezustand übergegangen ist, zu einer App navigieren.
Wenn ein erneuter Verbindungsversuch den Server erreicht, der Server aber bereits den Schaltkreis freigegeben hat, tritt automatisch eine Seitenaktualisierung auf. Dadurch wird verhindert, dass der Benutzer die Seite manuell aktualisieren muss, wenn dies wahrscheinlich zu einer erfolgreichen erneuten Verbindung führt.
Die Zeitdauer für die erneute Verbindung verwendet eine berechnete Backoffstrategie. Standardmäßig treten die ersten Wiederholungsversuche in schneller Folge ohne Wiederholungsintervall auf, bevor berechnete Verzögerungen zwischen Versuchen eingeführt werden. Sie können das Wiederholungsintervallverhalten anpassen, indem Sie eine Funktion zum Berechnen des Wiederholungsintervalls angeben, wie das folgende exponentielle Backoff-Beispiel veranschaulicht:
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
Das Design der Standard-Benutzeroberfläche für die Wiederverbindung wurde modernisiert.
Weitere Informationen finden Sie in den Anleitungen zu ASP.NET Core BlazorSignalR.
Vereinfachte Serialisierung des Authentifizierungsstatus für Blazor Web Apps
Neue APIs erleichtern das Hinzufügen der Authentifizierung zu einer bestehenden Blazor Web App. Wenn Sie eine neue Blazor Web App mit Authentifizierung über Einzelkonten erstellen und die WebAssembly-basierte Interaktivität aktivieren, enthält das Projekt sowohl im Server- als auch im Client-Projekt einen benutzerdefinierten AuthenticationStateProvider.
Diese Anbieter geben den Authentifizierungsstatus des Benutzers an den Browser weiter. Die Authentifizierung auf dem Server statt auf dem Client ermöglicht der Anwendung den Zugriff auf den Authentifizierungsstatus während des Prerenderings und vor der Initialisierung der .NET WebAssembly-Laufzeit.
Die benutzerdefinierten AuthenticationStateProvider-Implementierungen verwenden den Dienst Persistent Component State (PersistentComponentState), um den Authentifizierungsstatus in HTML-Kommentare zu serialisieren und ihn von WebAssembly zurückzulesen, um eine neue AuthenticationState-Instanz zu erstellen.
Dies funktioniert gut, wenn Sie von der Projektvorlage Blazor Web App ausgehen und die Option Individuelle Konten ausgewählt haben, aber es ist eine Menge Code, den Sie implementieren oder kopieren müssen, wenn Sie versuchen, die Authentifizierung zu einem bestehenden Projekt hinzuzufügen. Es gibt jetzt APIs, die jetzt Teil der Blazor Web App-Projektvorlage sind, die in Server- und Clientprojekten aufgerufen werden können, um diese Funktionalität hinzuzufügen:
AddAuthenticationStateSerialization
: Fügt die erforderlichen Dienste hinzu, um den Authentifizierungsstatus auf dem Server zu serialisieren.AddAuthenticationStateDeserialization
: Fügt die erforderlichen Dienste hinzu, um den Authentifizierungsstatus im Browser zu deserialisieren.
Diese API serialisiert standardmäßig nur die serverseitigen Namens- und Rollenansprüche für den Zugriff im Browser. Eine Option kann an AddAuthenticationStateSerialization
übergeben werden, um alle Ansprüche einzuschließen.
Weitere Informationen finden Sie in den folgenden Abschnitten von Sichern von serverseitigen Blazor-Apps in ASP.NET Core:
- BlazorIdentity-Benutzeroberfläche (einzelne Konten)
- Verwalten des Authentifizierungsstatus in Blazor Web Apps
Hinzufügen statischer serverseitiger Renderingseiten (SSR) zu einer global interaktiven Blazor Web App
Mit der Veröffentlichung von .NET 9 ist es jetzt einfacher, statische SSR-Seiten zu Apps hinzuzufügen, die globale Interaktivität übernehmen.
Dieser Ansatz ist nur hilfreich, wenn bestimmte Seiten der App nicht mit interaktivem Server- oder WebAssembly-Rendering funktionieren. Dieser Ansatz eignet sich z. B. für Seiten, die vom Lesen/Schreiben von HTTP-Cookies abhängig sind und nur in einem Anforderungs-/Antwortzyklus funktionieren können, aber nicht mit interaktivem Rendering. Für Seiten, die mit interaktivem Rendering funktionieren, sollten Sie kein statisches SSR-Rendering erzwingen, da es weniger reaktionsfähig und weniger effizient für den Endbenutzer ist.
Markieren Sie eine beliebige Razor-Komponentenseite mit dem neuen [ExcludeFromInteractiveRouting]
-Attribut, das mit der @attribute
Razor-Direktive zugewiesen wurde:
@attribute [ExcludeFromInteractiveRouting]
Durch das Anwenden des Attributs wird die Navigation zu der Seite über das interaktive Routing beendet. Die eingehende Navigation wird gezwungen, die Seite vollständig neu zu laden, anstatt die Seite über das interaktive Routing aufzulösen. Das vollständige Neuladen zwingt die Stammkomponente der obersten Ebene, in der Regel die App
-Komponente (App.razor
), vom Server erneut zu rendern, sodass die App zu einem anderen Rendermodus auf oberster Ebene wechseln kann.
Mit der HttpContext.AcceptsInteractiveRouting
-Erweiterungsmethode kann die Komponente erkennen, ob [ExcludeFromInteractiveRouting]
auf die aktuelle Seite angewendet wird.
Verwenden Sie in der App
-Komponente das Muster im folgenden Beispiel:
- Seiten, die nicht mit
[ExcludeFromInteractiveRouting]
-Standard für denInteractiveServer
Rendermodus mit globaler Interaktivität versehen sind. Sie könnenInteractiveServer
durchInteractiveWebAssembly
oderInteractiveAuto
ersetzen, um einen anderen standardmäßigen globalen Rendermodus anzugeben. - Seiten, die mit
[ExcludeFromInteractiveRouting]
versehen sind, übernehmen statischen SSR (PageRenderMode
wirdnull
zugewiesen).
<!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;
}
Eine Alternative zur Verwendung der HttpContext.AcceptsInteractiveRouting
-Erweiterungsmethode besteht darin, Endpunktmetadaten manuell mithilfe von HttpContext.GetEndpoint()?.Metadata
zu lesen.
Dieses Feature wird in der Referenzdokumentation in ASP.NET Core Blazor Rendermodi behandelt.
Constructor Injection
Razor Komponenten unterstützen die Konstruktoreinfügung.
Im folgenden Beispiel fügt die partielle (CodeBehind)-Klasse den NavigationManager
Dienst mithilfe eines primären Konstruktors ein:
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Weitere Informationen finden Sie unter Abhängigkeitsinjektion in ASP.NET Core Blazor.
Websocket-Komprimierung für interaktive Serverkomponenten
Standardmäßig aktivieren interaktive Serverkomponenten die Komprimierung für WebSocket-Verbindungen und legen eine frame-ancestors
-CSP-Anweisung (Content Security Policy) auf 'self'
fest, die nur das Einbetten der App in <iframe>
vom Ursprung ermöglicht, von dem die App bei aktivierter Komprimierung bereitgestellt wird oder wenn eine Konfiguration für den WebSocket-Kontext bereitgestellt wird.
Komprimierung kann durch Festlegen ConfigureWebSocketOptions
auf null
, wodurch die Sicherheitsanfälligkeit der App zum Angriff reduziert wird, dies kann jedoch zu einer verringerten Leistung führen:
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Konfigurieren Sie einen strikteren frame-ancestors
CSP mit einem Wert von 'none'
(einfache Anführungszeichen erforderlich), was die WebSocket-Komprimierung zulässt, aber verhindert, dass Browser die App in eine der <iframe>
folgenden Werte einbetten:
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Weitere Informationen finden Sie in den folgenden Ressourcen:
- Leitfaden zu BlazorSignalR in ASP.NET Core
- Leitfaden zur Bedrohungsminderung für ASP.NET Core Blazor interaktives serverseitiges Rendering
Behandeln von Tastaturkompositionsereignissen in Blazor
Die neue KeyboardEventArgs.IsComposing
-Eigenschaft gibt an, ob das Tastaturereignis Teil einer Kompositionssitzung ist. Das Nachverfolgen des Kompositionszustands von Tastaturereignissen ist entscheidend für die Behandlung internationaler Zeicheneingabemethoden.
Parameter OverscanCount
zu QuickGrid
hinzugefügt
Die QuickGrid
-Komponente macht jetzt eine OverscanCount
-Eigenschaft verfügbar, die angibt, wie viele zusätzliche Zeilen vor und nach dem sichtbaren Bereich gerendert werden, wenn die Virtualisierung aktiviert ist.
Der Standard OverscanCount
ist „3“. Das folgende Beispiel erhöht OverscanCount
um 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumber
-Komponente unterstützt das Attribut type="range"
Die Komponente InputNumber<TValue> unterstützt jetzt das Attribut type="range"
, mit dem eine Bereichseingabe erstellt wird, die Modellbindung und Formularprüfung unterstützt und in der Regel als Schieberegler oder Wählscheibe anstelle eines Textfelds dargestellt wird:
<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; }
}
}
Mehrere Blazor Web Apps pro Serverprojekt
Die Unterstützung für mehrere Blazor Web Apps pro Serverprojekt wird für .NET 10 (November 2025) berücksichtigt.
Weitere Informationen finden Sie unter Unterstützung mehrerer Blazor-Web-Apps pro Serverprojekt (dotnet/aspnetcore
#52216).
SignalR
In diesem Abschnitt werden neue Features für SignalR beschrieben.
Polymorphe Typunterstützung in SignalR-Hubs
Hubmethoden können nun eine Basisklasse anstelle der abgeleiteten Klasse akzeptieren, um polymorphe Szenarien zu ermöglichen. Der Basistyp muss annotiert werden, um Polymorphismus zu ermöglichen.
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; }
}
Verbesserte Aktivitäten für SignalR
SignalR verfügt jetzt über eine ActivitySource mit dem Namen Microsoft.AspNetCore.SignalR.Server
, die Ereignisse für Hubmethodenaufrufe ausgibt:
- Jede Methode ist eine eigene Aktivität, sodass alles, was während des Hubmethodenaufrufs eine Aktivität ausgibt, unter der Hubmethodenaktivität liegt.
- Hubmethodenaktivitäten verfügen nicht über ein übergeordnetes Element. Dies bedeutet, dass sie nicht unter der langlebigen SignalR-Verbindung gebündelt werden.
Das folgende Beispiel verwendet das .NET AspireDashboard und die OpenTelemetry-Pakete:
<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" />
Fügen Sie der Datei Program.cs
den folgenden Startcode hinzu:
// 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())
{
// We want to view all traces in development
tracing.SetSampler(new AlwaysOnSampler());
}
tracing.AddAspNetCoreInstrumentation();
tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
});
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
Im Folgenden finden Sie eine Beispielausgabe vom Aspire-Dashboard:
SignalR unterstützt das Kürzen und natives AOT
Als Fortsetzung der nativen AOT-Journey, die in .NET 8 begann, verfügen wir nun über Unterstützung zum Kürzen und für die native AOT-Kompilierung (AOT = Ahead-of-Time) für SignalR-Client- und Server-Szenarien. Sie können jetzt die Leistungsvorteile der Verwendung von nativem AOT in Anwendungen nutzen, die SignalR für die Echtzeit-Webkommunikation verwenden.
Erste Schritte
Installieren Sie das neueste .NET 9 SDK.
Erstellen Sie eine Lösung aus der webapiaot
-Vorlage in einer Befehlszeile mit dem folgenden Befehl:
dotnet new webapiaot -o SignalRChatAOTExample
Ersetzen Sie den Inhalt der Program.cs
-Datei durch den folgenden SignalR-Code:
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);
}
}
Das obige Beispiel erzeugt eine native Windows-Ausführungsdatei von 10 MB und eine Linux-Ausführungsdatei von 10,9 MB.
Begrenzungen
- Derzeit wird nur das JSON-Protokoll unterstützt:
- Wie im obigen Code dargestellt, müssen Apps, die JSON-Serialisierung und natives AOT verwenden, den
System.Text.Json
-Quellengenerator verwenden. - Dies folgt dem gleichen Ansatz wie minimale APIs.
- Wie im obigen Code dargestellt, müssen Apps, die JSON-Serialisierung und natives AOT verwenden, den
- Auf dem SignalR-Server werden Hub-Methodenparameter vom Typ
IAsyncEnumerable<T>
undChannelReader<T>
, wobeiT
ein ValueType (struct
) ist, nicht unterstützt. Die Verwendung dieser Typen führt zu einer Laufzeitausnahme beim Start in der Entwicklung und in der veröffentlichten App. Weitere Informationen finden Sie unter SignalR: Verwenden von IAsyncEnumerable<T> und ChannelReader<T> mit ValueTypes in nativen AOT (dotnet/aspnetcore
#56179). - Stark typisierte Hubs werden vom nativen AOT (
PublishAot
) nicht unterstützt. Die Verwendung von stark typisierten Hubs mit nativem AOT führt zu Warnungen während der Erstellung und Veröffentlichung sowie zu einer Laufzeitausnahme. Die Verwendung stark typierter Hubs mit Kürzen (PublishedTrimmed
) wird unterstützt. - Nur
Task
,Task<T>
,ValueTask
oderValueTask<T>
werden für asynchrone Rückgabetypen unterstützt.
Minimale APIs
In diesem Abschnitt werden neue Features für minimale APIs beschrieben.
InternalServerError
und InternalServerError<TValue>
wurden zu TypedResults
hinzugefügt.
Die Klasse TypedResults ist ein hilfreiches Mittel für die Rückgabe von stark typisierten HTTP-Statuscode-basierten Antworten aus einer minimalen API. TypedResults
enthält jetzt Factorymethoden und Typen für die Rückgabe von „500 Interner Serverfehler“-Antworten von Endpunkten. Hier ist ein Beispiel, das eine 500-Antwort zurückgibt:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Rufen Sie ProducesProblem
und ProducesValidationProblem
auf Routengruppen auf.
Die Erweiterungsmethoden ProducesProblem
und ProducesValidationProblem
verfügen nun über eine aktualisierte Version, die ihre Verwendung in Routengruppen unterstützt. Diese Methoden zeigen an, dass alle Endpunkte in einer Routengruppe als Antwort ProblemDetails
oder ValidationProblemDetails
für die Zwecke von OpenAPI-Metadaten zurückgegeben werden können.
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);
OpenAPI
In diesem Abschnitt werden neue Funktionen für OpenAPI beschrieben.
Integrierte Unterstützung für die OpenAPI-Dokumentgenerierung
Die OpenAPI-Spezifikation ist ein Standard zur Beschreibung von HTTP-APIs. Mit dem Standard können Entwickler die Form der APIs definieren, die an Clientgeneratoren, Servergeneratoren, Testtools, Dokumentationen und vieles mehr angeschlossen werden können. In .NET 9 Preview bietet ASP.NET Core integrierte Unterstützung für das Generieren von OpenAPI-Dokumenten, die controllerbasierte oder minimale APIs über das Microsoft.AspNetCore.OpenApi-Paket darstellen.
Der folgende hervorgehobene Code ruft Folgendes auf:
AddOpenApi
zum Registrieren der erforderlichen Abhängigkeiten im DI-Container der App.MapOpenApi
zum Registrieren der erforderlichen OpenAPI-Endpunkte in den Routen der App.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);
app.Run();
Installieren Sie das Microsoft.AspNetCore.OpenApi
-Paket im Projekt mithilfe des folgenden Befehls:
dotnet add package Microsoft.AspNetCore.OpenApi --prerelease
Führen Sie die App aus, und navigieren Sie zu openapi/v1.json
, um das generierte OpenAPI-Dokument anzuzeigen:
OpenAPI-Dokumente können auch zur Buildzeit generiert werden, indem Sie das Microsoft.Extensions.ApiDescription.Server
-Paket hinzufügen:
dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease
Fügen Sie in der Projektdatei der App Folgendes hinzu:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>
Führen Sie dotnet build
aus und überprüfen Sie die generierte JSON-Datei im Projektverzeichnis.
In ASP.NET Core integrierte OpenAPI-Dokumentgenerierung bietet Unterstützung für verschiedene Anpassungen und Optionen. Sie bietet Dokument- und Operationstransformatoren und die Möglichkeit, mehrere OpenAPI-Dokumente für dieselbe Anwendung zu verwalten.
Weitere Informationen zu ASP.NET den neuen OpenAPI-Dokumentfunktionen von Core finden Sie in den neuen Microsoft.AspNetCore.OpenApi-Dokumenten.
Verbesserungen der Intellisense-Vervollständigung für das OpenAPI-Paket
Die OpenAPI-Unterstützung von ASP.NET Core ist jetzt besser zugänglich und benutzerfreundlicher. Die OpenAPI-APIs werden als unabhängiges Paket ausgeliefert, getrennt vom gemeinsamen Framework. Bis jetzt bedeutete dies, dass Entwicklerteams nicht den Komfort von Code-Vervollständigungsfunktionen wie Intellisense für OpenAPI-APIs hatten.
Wir haben diese Lücke erkannt und einen neuen Vervollständigungsanbieter und eine Code-Korrekturfunktion eingeführt. Diese Tools sollen das Auffinden und die Verwendung von OpenAPI-APIs erleichtern und Entwicklerteams die Integration von OpenAPI in ihre Projekte ermöglichen. Der Vervollständigungsanbieter bietet Code-Vorschläge in Echtzeit, während die Code-Korrektur dabei hilft, häufige Fehler zu korrigieren und die API-Nutzung zu verbessern. Diese Verbesserung ist Teil unseres ständigen Bestrebens, die Erfahrung für Entwicklerteams zu verbessern und API-bezogene Workflows zu optimieren.
Wenn eine Benutzerin oder ein Benutzer eine Anweisung eingibt, für die eine OpenAPI-bezogene API verfügbar ist, zeigt der Vervollständigungsanbieter eine Empfehlung für die API an. In den folgenden Screenshots werden beispielsweise die Vervollständigungen für AddOpenApi und MapOpenApi erstellt, wenn eine Benutzerin oder ein Benutzer eine Aufrufanweisung für einen unterstützten Typ, wie IEndpointConventionBuilder eingibt:
Wenn die Vervollständigung akzeptiert wird und das Paket Microsoft.AspNetCore.OpenApi
nicht installiert ist, stellt ein Codefixer eine Verknüpfung zum automatischen Installieren der Abhängigkeit im Projekt bereit.
Unterstützung für [Required]
- und [DefaultValue]
-Attribute für Parameter und Eigenschaften
Wenn [Required]
- und [DefaultValue]
-Attribute auf Parameter oder Eigenschaften innerhalb komplexer Typen angewendet werden, ordnet die OpenAPI-Implementierung diese den Eigenschaften required
und default
im OpenAPI-Dokument zu, das dem Parameter oder Typschema zugeordnet ist.
Die folgende API erzeugt z. B. das begleitende Schema für den Typ Todo
.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapPost("/todos", (Todo todo) => { });
app.Run();
class Todo
{
public int Id { get; init; }
public required string Title { get; init; }
[DefaultValue("A new todo")]
public required string Description { get; init; }
[Required]
public DateTime CreatedOn { get; init; }
}
{
"required": [
"title",
"description",
"createdOn"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string"
},
"description": {
"type": "string",
"default": "A new todo"
},
"createdOn": {
"type": "string",
"format": "date-time"
}
}
}
Unterstützung für Schematransformatoren in OpenAPI-Dokumenten
Integrierte OpenAPI-Unterstützung wird jetzt mit Unterstützung für Schematransformatoren bereitgestellt, die zum Ändern von Schemata verwendet werden können, die von System.Text.Json und der OpenAPI-Implementierung generiert werden. Wie Dokument- und Vorgangstransformatoren können Schematransformatoren auf dem OpenApiOptions-Objekt registriert werden. Das folgende Codebeispiel veranschaulicht die Verwendung eines Schematransformators zum Hinzufügen eines Beispiels zum Schema eines Typs.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(Todo))
{
schema.Example = new OpenApiObject
{
["id"] = new OpenApiInteger(1),
["title"] = new OpenApiString("A short title"),
["description"] = new OpenApiString("A long description"),
["createdOn"] = new OpenApiDateTime(DateTime.Now)
};
}
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapPost("/todos", (Todo todo) => { });
app.Run();
class Todo
{
public int Id { get; init; }
public required string Title { get; init; }
[DefaultValue("A new todo")]
public required string Description { get; init; }
[Required]
public DateTime CreatedOn { get; init; }
}
Verbesserungen an den Transformatorregistrierungs-APIs in Microsoft.AspNetCore.OpenApi
OpenAPI-Transformatoren unterstützen Änderungen am OpenAPI-Dokument, Vorgänge innerhalb des Dokuments, oder Schemata, die Typen in der API zugeordnet sind. Die APIs zum Registrieren von Transformatoren in einem OpenAPI-Dokument bieten eine Vielzahl von Optionen für die Registrierung von Transformatoren.
Bislang waren die folgenden APIs für die Registrierung von Transformatoren verfügbar:
OpenApiOptions AddDocumentTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddDocumentTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions AddDocumentTransformer<IOpenApiDocumentTransformer>()
OpenApiOptions UseSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task>)
OpenApiOptions AddOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task>)
Der neue API-Satz lautet wie folgt:
OpenApiOptions AddDocumentTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddDocumentTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions AddDocumentTransformer<IOpenApiDocumentTransformer>()
OpenApiOptions AddSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddSchemaTransformer(IOpenApiSchemaTransformer transformer)
OpenApiOptions AddSchemaTransformer<IOpenApiSchemaTransformer>()
OpenApiOptions AddOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddOperationTransformer(IOpenApiOperationTransformer transformer)
OpenApiOptions AddOperationTransformer<IOpenApiOperationTransformer>()
Microsoft.AspNetCore.OpenApi unterstützt Kürzung und natives AOT
Die neue integrierte OpenAPI-Unterstützung in ASP.NET Core unterstützt jetzt auch Kürzen und natives AOT.
Erste Schritte
Erstellen Sie ein neues ASP.NET Core-Web-API-Projekt (natives AOT).
dotnet new webapiaot
Fügen Sie das Microsoft.AspNetCore.OpenAPI-Paket hinzu.
dotnet add package Microsoft.AspNetCore.OpenApi --prerelease
Für diese Vorschau müssen Sie auch das neueste Microsoft.OpenAPI-Paket hinzufügen, um Kürzungswarnungen zu vermeiden.
dotnet add package Microsoft.OpenApi
Aktualisieren Sie Program.cs, um das Generieren von OpenAPI-Dokumenten zu aktivieren.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Veröffentlichen Sie die App.
dotnet publish
Die App veröffentlicht natives AOT ohne Warnungen.
Unterstützt das Aufrufen von ProducesProblem
und ProducesValidationProblem
bei Routengruppen
Die Erweiterungsmethoden ProducesProblem und "ProducesValidationProblem wurden für Routengruppen aktualisiert. Diese Methoden können verwendet werden, um anzugeben, dass alle Endpunkte in einer Routengruppe ProblemDetails
- oder ValidationProblemDetails
-Antworten für OpenAPI-Metadaten zurückgeben können.
var app = WebApplication.Create();
var todos = app.MapGroup("/todos")
.ProducesProblem(StatusCodes.Status500InternalServerError);
todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));
app.Run();
record Todo(int Id, string Title, bool IsCompleted);
Die Ergebnistypen Problem
und ValidationProblem
unterstützen die Konstruktion mit IEnumerable<KeyValuePair<string, object?>>
-Werten
Vor .NET 9 erforderten die Ergebnistypen Problem und ValidationProblem in minimalen APIs, dass die Eigenschaften errors
und extensions
mit einer Implementierung von IDictionary<string, object?>
initialisiert werden. In diesem Release unterstützen diese Konstruktions-APIs Überladungen, die IEnumerable<KeyValuePair<string, object?>>
verbrauchen.
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);
});
Vielen Dank an GitHub-Benutzer joegoldman2 für diesen Beitrag!
Authentifizierung und Autorisierung
In diesem Abschnitt werden neue Features für Authentifizierung und Autorisierung beschrieben.
OpenIdConnectHandler fügt Unterstützung für Pushed Authorization Requests (PAR) hinzu.
Wir möchten Joe DeCock von Duende Software für das Hinzufügen von Pushed Authorization Requests (PAR) zu OpenIdConnectHandler von ASP.NET Core danken. Joe hat den Hintergrund und die Motivation für die Aktivierung von PAR in seinem API-Vorschlag wie folgt beschrieben:
Pushed Authorization Requests (PAR) ist ein relativ neuer OAuth-Standard, der die Sicherheit von OAuth- und OIDC-Flows verbessert, indem Autorisierungsparameter vom Front-Channel zum Back-Channel verschoben werden. Das heißt, die Verschiebung von Autorisierungsparametern von Umleitungs-URLs im Browser zu direkten Computer-zu-Computer-HTTP-Aufrufen im Backend.
Dadurch wird verhindert, dass Cyberkriminelle im Browser:
- Autorisierungsparameter sehen, die personenbezogene Daten preisgeben könnten.
- Die Parameter manipulieren. Beispielsweise könnte der Cyberangriff den Umfang des angeforderten Zugriffs ändern.
Durch die Übertragung der Autorisierungsparameter bleiben die Anforderungs-URLs außerdem kurz. Autorisierungsparameter können sehr lang werden, wenn komplexere OAuth- und OIDC-Funktionen wie Rich Authorization Requests verwendet werden. Lange URLs verursachen in vielen Browsern und Netzwerkinfrastrukturen Probleme.
Die Verwendung von PAR wird von der FAPI-Arbeitsgruppe innerhalb der OpenID Foundation gefördert. Das FAPI2.0-Sicherheitsprofil erfordert beispielsweise die Verwendung von PAR. Dieses Sicherheitsprofil wird von vielen Gruppen verwendet, die sich mit Open Banking (hauptsächlich in Europa), im Gesundheitswesen und in anderen Branchen mit hohen Sicherheitsanforderungen befassen.
PAR wird von einer Reihe von identity-Anbietern unterstützt, darunter:
Für .NET 9 haben wir uns entschieden, PAR standardmäßig zu aktivieren, wenn das Ermittlungsdokument des Identitätsanbieters die Unterstützung von PAR ankündigt, da es Anbietern, die PAR unterstützen, eine verbesserte Sicherheit bieten sollte. Das Discovery-Dokument des identity-Anbieters befindet sich in der Regel unter .well-known/openid-configuration
. Wenn dies zu Problemen führt, können Sie PAR über OpenIdConnectOptions.PushedAuthorizationBehavior wie folgt deaktivieren:
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;
});
Um sicherzustellen, dass die Authentifizierung nur bei Verwendung von PAR erfolgreich ist, verwenden Sie stattdessen PushedAuthorizationBehavior.Require. Diese Änderung führt auch ein neues OnPushAuthorization-Ereignis in OpenIdConnectEvents ein, mit dem die Push-Autorisierungsanforderung angepasst oder manuell bearbeitet werden kann. Weitere Informationen finden Sie im API-Vorschlag.
Anpassung der OIDC- und OAuth-Parameter
Die OAuth- und OIDC-Authentifizierungs-Handler verfügen jetzt über eine AdditionalAuthorizationParameters
Option, mit der Sie die Parameter der Autorisierungsnachricht, die normalerweise Teil des Redirect-Query-Strings sind, einfacher anpassen können. In .NET 8 und früher erfordert dies einen benutzerdefinierten OnRedirectToIdentityProvider Callback oder eine überschriebene BuildChallengeUrl Methode in einem benutzerdefinierten Handler. Hier ist ein Beispiel für .NET 8 Code:
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;
};
});
Das vorangegangene Beispiel kann nun zu folgendem Code vereinfacht werden:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Konfigurieren Sie die erweiterten Authentifizierungs-Flags von HTTP.sys
Sie können nun die HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
und HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
HTTP.sys-Flags konfigurieren, indem Sie die neuen EnableKerberosCredentialCaching
und CaptureCredentials
Eigenschaften der HTTP.sys AuthenticationManager verwenden, um die Handhabung der Windows-Authentifizierung zu optimieren. Zum Beispiel:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Verschiedenes
In den folgenden Abschnitten werden verschiedene neue Features beschrieben.
Neue HybridCache
-Bibliothek
Die HybridCache
-API überbrückt einige Lücken in den IDistributedCache- und IMemoryCache-APIs. Außerdem werden neue Funktionen hinzugefügt, z. B.:
- "Stampede"-Schutz, um parallele Abrufe derselben Arbeit zu verhindern.
- Konfigurierbare Serialisierung.
HybridCache
ist als Drop-In-Ersatz für vorhandene IDistributedCache
- und IMemoryCache
-Nutzung konzipiert und bietet eine einfache API zum Hinzufügen von neuem Zwischenspeicherungscode. Es bietet eine einheitliche API für die In-Process- und Out-of-Process-Zwischenspeicherung.
Um zu sehen, wie die HybridCache
-API vereinfacht wird, vergleichen Sie sie mit Code, der IDistributedCache
verwendet. Hier sehen Sie ein Beispiel dafür, wie die Verwendung von IDistributedCache
aussieht:
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)
{ /* ... */ }
}
Das ist viel Arbeit, um jedes Mal richtig zu liegen, einschließlich Dinge wie Serialisierung. Und im Szenario „Cachefehler“ könnten Sie mit mehreren gleichzeitigen Threads enden, die alle einen Cachefehler erhalten, alle zugrunde liegenden Daten abrufen, serialisieren und alle diese Daten an den Cache senden.
Um diesen Code mit HybridCache
zu vereinfachen und zu verbessern, müssen wir zuerst die neue Bibliothek Microsoft.Extensions.Caching.Hybrid
hinzufügen:
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Registrieren Sie den HybridCache
-Dienst, wie Sie eine IDistributedCache
-Implementierung registrieren würden:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
Jetzt können die meisten Zwischenspeicherungsbedenken auf HybridCache
entladen werden:
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
);
}
}
Wir stellen eine konkrete Implementierung der abstrakten Klasse HybridCache
über Abhängigkeitsinjektion bereit, aber es ist beabsichtigt, dass Entwickler benutzerdefinierte Implementierungen der API bereitstellen können. Die HybridCache
-Implementierung befasst sich mit allem, was mit dem Caching zusammenhängt, einschließlich der Handhabung gleichzeitiger Operationen. Das cancel
-Token steht hier für die kombinierte Annullierung aller gleichzeitigen Anrufer –- nicht nur für die Annullierung des Anrufers, den wir sehen können (d. h. token
).
Szenarien mit hohem Durchsatz können mithilfe des TState
-Musters weiter optimiert werden, um den Aufwand von erfassten Variablen und Rückrufen pro Instanz zu vermeiden:
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
verwendet ggf. die konfigurierte IDistributedCache
-Implementierung für sekundäre Out-of-Process-Speicherung, z. B. mithilfe von Redis. Aber auch ohne IDistributedCache
bietet der HybridCache
-Dienst weiterhin In-Process-Cache- und Stampede-Schutz.
Ein Hinweis zur Wiederverwendung von Objekten
Im typischen vorhandenen Code, der IDistributedCache
verwendet, führt jeder Abruf eines Objekts aus dem Cache zur Deserialisierung. Dieses Verhalten bedeutet, dass jeder gleichzeitige Aufrufer eine separate Instanz des Objekts erhält, die nicht mit anderen Instanzen interagieren kann. Das Ergebnis ist Threadsicherheit, da es kein Risiko für gleichzeitige Änderungen an derselben Objektinstanz gibt.
Da viele HybridCache
-Verwendungen von vorhandenem IDistributedCache
-Code angepasst werden, behält HybridCache
dieses Verhalten standardmäßig bei, um die Einführung von Parallelitätsfehlern zu vermeiden. Ein bestimmter Anwendungsfall ist jedoch inhärent threadsicher:
- Wenn die zwischengespeicherten Typen unveränderlich sind.
- Wenn der Code sie nicht ändert.
Informieren Sie in solchen Fällen HybridCache
, dass es sicher ist, Instanzen wiederzuverwenden, indem Sie:
- Den Typ als
sealed
markieren. Das Schlüsselwortsealed
in C# bedeutet, dass die Klasse nicht geerbt werden kann. - Anwenden des
[ImmutableObject(true)]
-Attributs. Das Attribut[ImmutableObject(true)]
gibt an, dass der Status des Objekts nach der Erstellung nicht mehr geändert werden kann.
Durch die erneute Verwendung von Instanzen kann HybridCache
den Mehraufwand der CPU- und Objektzuordnungen verringern, die mit der Deserialisierung pro Aufruf verbunden sind. Dies kann zu Leistungsverbesserungen in Szenarien führen, in denen die zwischengespeicherten Objekte groß sind oder häufig aufgerufen werden.
Weitere HybridCache
-Funktionen
Wie IDistributedCache
unterstützt HybridCache
das Entfernen von Schlüsseln mit einer RemoveKeyAsync
-Methode.
HybridCache
stellt auch optionale APIs für IDistributedCache
-Implementierungen bereit, um byte[]
-Zuordnungen zu vermeiden. Dieses Feature wird von den Vorschauversionen der Microsoft.Extensions.Caching.StackExchangeRedis
- und Microsoft.Extensions.Caching.SqlServer
-Pakete implementiert.
Die Serialisierung wird als Teil der Registrierung des Diensts konfiguriert, wobei typspezifische und generalisierte Serialisierer über die WithSerializer
- und .WithSerializerFactory
-Methoden, verkettet vom AddHybridCache
-Aufruf, unterstützt werden. Standardmäßig behandelt die Bibliothek string
und byte[]
intern und verwendet System.Text.Json
für alles weitere, aber Sie können protobuf, xml oder etwas anderes verwenden.
HybridCache
unterstützt ältere .NET-Runtimes bis zu .NET Framework 4.7.2 und .NET Standard 2.0.
Weitere Informationen zu HybridCache
finden Sie unter HybridCache-Bibliothek in ASP.NET Core
Verbesserungen der Seite mit Ausnahmen für Entwickler
Die ASP.NET Core-Seite mit Ausnahmen für Entwickler wird angezeigt, wenn eine App während der Entwicklung eine unbehandelte Ausnahme auslöst. Die Seite mit Ausnahmen für Entwickler bietet ausführliche Informationen zur Ausnahme und zur Anforderung.
In Preview 3 wurden Endpunktmetadaten auf der Entwicklerausnahmeseite hinzugefügt. ASP.NET Core verwendet Endpunkt-Metadaten, um das Verhalten von Endpunkten zu steuern, z. B. Routing, Antwort-Caching, Ratenbegrenzung, OpenAPI-Generierung und mehr. Die folgende Abbildung zeigt die neuen Metadaten-Informationen im Abschnitt Routing
auf der Entwicklerausnahmeseite:
Beim Testen der Ausnahmeseite für Entwickler wurden kleine Verbesserungen der Lebensqualität identifiziert. Sie wurden in Preview 4 ausgeliefert:
- Besserer Textumbruch. Bei langen Cookies, Abfragezeichenfolgenwerten und Methodennamen werden keine horizontalen Browser-Bildlaufleisten mehr hinzugefügt.
- Größerer Text, der in modernen Designs zu finden ist.
- Konsistentere Tabellengrößen.
Die folgende animierte Abbildung zeigt die neue Seite mit Ausnahmen für Entwickler:
Verbesserungen beim Debuggen von Wörterbuchen
Die Debuganzeige von Wörterbüchern und anderen Schlüsselwertsammlungen verfügt über ein verbessertes Layout. Der Schlüssel wird in der Schlüsselspalte des Debuggers angezeigt, anstatt mit dem Wert verkettet zu werden. Die folgenden Bilder stellen die alte und die neue Anzeige eines Wörterbuchs im Debugger dar.
Vorher:
Nachher:
ASP.NET Core verfügt über viele Schlüsselwertauflistungen. Diese verbesserte Debugerfahrung gilt für:
- HTTP-Kopfzeilen
- Abfragezeichenfolgen
- Formulare
- Cookies
- Anzeigen von Daten
- Routendaten
- Features
Fix für 503-Apps während der Wiederverwendung von Apps in IIS
Standardmäßig gibt es jetzt eine Verzögerung von einer Sekunde, wenn IIS über eine Wiederverwendung oder ein Herunterfahren benachrichtigt wird und wenn ANCM den verwalteten Server anweist, das Herunterfahren zu initiieren. Die Verzögerung kann über die Umgebungsvariable ANCM_shutdownDelay
oder durch Festlegen der Handlereinstellung shutdownDelay
konfiguriert werden. Beide Werte sind in Millisekunden. Die Verzögerung soll in erster Linie die Wahrscheinlichkeit eines Wettrennens verringern, bei dem:
- IIS keine Warteschlangenanforderungen gestartet hat, um zur neuen App zu wechseln.
- ANCM mit der Ablehnung neuer Anforderungen beginnt, die in die alte App gelangen.
Für langsamere Computer oder Computer mit höherer CPU-Auslastung muss dieser Wert möglicherweise angepasst werden, um die Wahrscheinlichkeit 503 zu verringern.
Beispiel der Einstellung 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>
Der Fix befindet sich im global installierten ANCM-Modul, das aus dem Hostingbundle stammt.
Optimieren der Bereitstellung statischer Webobjekte
Die Einhaltung bewährter Methoden für die Bereitstellung statischer Ressourcen erfordert einen erheblichen Arbeitsaufwand und technisches Fachwissen. Ohne Optimierungen wie Komprimierung, Zwischenspeichern und Fingerabdrücke:
- Der Browser muss zusätzliche Anforderungen an jeden Seiteladevorgang stellen.
- Mehr Bytes als erforderlich werden über das Netzwerk übertragen.
- Manchmal werden veraltete Versionen von Dateien für Clients bereitgestellt.
Das Erstellen von leistungsfähigen Web-Apps erfordert die Optimierung der Ressourcenübermittlung an den Browser. Mögliche Optimierungen sind:
- Eine bestimmte Ressource wird einmal bereitgestellt, bis sich die Datei ändert oder der Browser seinen Cache leert. Legen Sie den ETag-Header fest.
- Es wird verhindert, dass der Browser alte oder veraltete Ressourcen verwendet, nachdem eine Anwendung aktualisiert wurde. Legen Sie den Header Zuletzt geändert fest.
- Legen Sie den richtigen Zwischenspeicherungsheader fest.
- Verwenden Sie die Zwischenspeicherungs-Middleware.
- Stellen Sie wenn möglich komprimierte Versionen der Ressourcen bereit.
- Verwenden Sie einen CDN, um die Ressourcen näher an die Benutzerin oder den Benutzer zu bringen.
- Es minimiert die Größe der Ressourcen, die dem Browser zur Verfügung gestellt werden. Diese Optimierung beinhaltet keine Minimierung.
MapStaticAssets
ist eine neue Middleware, die die Übermittlung statischer Objekte in einer App optimiert. Sie ist für alle UI-Frameworks geeignet, einschließlich Blazor, Razor Pages und MVC. Sie ist in der Regel ein Drop-In-Ersatz 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();
Der Vorgang von MapStaticAssets
besteht in der Kombination von Build- und Veröffentlichungsprozessen, um Informationen über alle statischen Ressourcen in einer Anwendung zu sammeln. Diese Informationen werden dann von der Laufzeitbibliothek verwendet, um diese Dateien effizient an den Browser zu liefern.
MapStaticAssets
kann UseStaticFiles
in den meisten Situationen ersetzen, ist aber für die Bereitstellung der Ressourcen optimiert, die der App zum Zeitpunkt der Erstellung und Veröffentlichung bekannt sind. Wenn die App Ressourcen von anderen Speicherorten bereitstellt, z. B. von der Festplatte oder von eingebetteten Ressourcen, sollte UseStaticFiles
verwendet werden.
MapStaticAssets
bietet die folgenden Vorteile, die UseStaticFiles
nicht hat:
- Buildzeitkomprimierung für alle Ressourcen in der App:
gzip
während der Entwicklung undgzip + brotli
während der Veröffentlichung.- Alle Ressourcen werden mit dem Ziel komprimiert, die Größe der Ressourcen auf ein Minimum zu reduzieren.
- Inhaltsbasierte
ETags
: DieEtags
für jede Ressource sind die Base64-codierte Zeichenfolge des SHA-256-Hashs des Inhalts. Dadurch wird sichergestellt, dass der Browser eine Datei nur dann erneut lädt, wenn sich der Inhalt geändert hat.
In der folgenden Tabelle sind die ursprünglichen und komprimierten Größen der CSS- und JS-Dateien in der Razor Pages-Standardvorlage aufgeführt:
Datei | Ursprünglich | Compressed | % Reduzierung |
---|---|---|---|
bootstrap.min.css | 163 | 17,5 | 89,26 % |
jquery.js | 89,6 | 28 | 68,75 % |
bootstrap.min.js | 78,5 | 20 | 74,52 % |
Gesamt | 331,1 | 65,5 | 80,20 % |
Die folgende Tabelle zeigt die ursprünglichen und komprimierten Größen mithilfe der Fluent UI Blazor Komponentenbibliothek:
Datei | Ursprünglich | Compressed | % Reduzierung |
---|---|---|---|
fluent.js | 384 | 73 | 80,99 % |
fluent.css | 94 | 11 | 88,30 % |
Gesamt | 478 | 84 | 82,43 % |
Ergebnis: 478 KB unkomprimiert zu 84 KB komprimiert.
Die folgende Tabelle zeigt die originalen und komprimierten Größen mithilfe der MudBlazorBlazor-Komponentenbibliothek:
Datei | Ursprünglich | Compressed | Reduzierung |
---|---|---|---|
MudBlazor.min.css | 541 | 37,5 | 93,07 % |
MudBlazor.min.js | 47,4 | 9.2 | 80,59 % |
Gesamt | 588,4 | 46,7 | 92,07 % |
Optimierung erfolgt automatisch bei Verwendung von MapStaticAssets
. Wenn eine Bibliothek hinzugefügt oder aktualisiert wird, z. B. mit neuem JavaScript oder CSS, werden die Ressourcen als Teil des Builds optimiert. Die Optimierung ist besonders für mobile Umgebungen von Vorteil, die eine geringere Bandbreite oder unzuverlässige Verbindungen haben können.
Weitere Informationen über die neuen Funktionen zur Bereitstellung von Dateien finden Sie in den folgenden Ressourcen:
Aktivieren der dynamischen Komprimierung auf dem Server gegenüber der Verwendung von MapStaticAssets
MapStaticAssets
hat die folgenden Vorteile gegenüber der dynamischen Komprimierung auf dem Server:
- Es ist einfacher, da keine serverspezifische Konfiguration vorhanden ist.
- Es ist leistungsfähiger, da die Ressourcen zur Erstellungszeit komprimiert werden.
- Es ermöglicht der Entwicklerin oder dem Entwickler, während des Buildprozesses zusätzliche Zeit damit zu verbringen, sicherzustellen, dass die Ressourcen die Mindestgröße aufweisen.
Betrachten Sie die folgende Tabelle, in der die MudBlazor-Komprimierung mit der dynamischen IIS-Komprimierung und MapStaticAssets
verglichen wird:
IIS gzip | MapStaticAssets | MapStaticAssets-Reduzierung |
---|---|---|
≅ 90 | 37,5 | 59 % |
ASP0026: Analysetool, das warnt, wenn [Authorize] von [AllowAnonymous] von „weiter entfernt“ außer Kraft gesetzt wird
Es erscheint intuitiv, dass ein [Authorize]
-Attribut, das "näher" an einer MVC-Aktion platziert ist als ein [AllowAnonymous]
-Attribut, das [AllowAnonymous]
-Attribut außer Kraft setzt und eine Autorisierung erzwingt. Dies ist jedoch nicht unbedingt der Fall. Was zählt, ist die relative Reihenfolge der Attribute.
Der folgende Code zeigt Beispiele, in denen ein näher gelegenes [Authorize]
-Attribut durch ein weiter entferntes [AllowAnonymous]
-Attribut außer Kraft gesetzt wird.
[AllowAnonymous]
public class MyController
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on the class
public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}
public class MyControllerInherited2 : MyControllerAnon
{
[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}
In .NET 9 Preview 6 haben wir ein Analysetool eingeführt, das solche Fälle hervorhebt, in denen ein näheres [Authorize]
-Attribut durch ein [AllowAnonymous]
-Attribut überschrieben wird, das weiter von einer MVC-Aktion entfernt ist. Die Warnung verweist auf das überschriebene [Authorize]
-Attribut mit der folgenden Meldung:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
Was Sie tun müssen, wenn Sie diese Warnung sehen, hängt von der Absicht ab, die hinter den Attributen steht. Das weiter entfernte [AllowAnonymous]
-Attribut sollte entfernt werden, wenn es den Endpunkt ungewollt anonymen Benutzerinnen und Benutzern zugänglich macht. Wenn das [AllowAnonymous]
-Attribut ein näheres [Authorize]
-Attribut außer Kraft setzen soll, können Sie das [AllowAnonymous]
-Attribut nach dem [Authorize]
-Attribut wiederholen, um die Absicht zu verdeutlichen.
[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;
}
Verbesserte Kestrel-Verbindungsmetriken
Wir haben die Verbindungsmetriken von Kestrel erheblich verbessert, indem wir Metadaten dazu einschließen, warum eine Verbindung fehlgeschlagen ist. Die Metrik kestrel.connection.duration
enthält nun den Grund für das Schließen einer Verbindung im Attribut error.type
.
Hier ist ein kleines Beispiel für die error.type
-Werte:
tls_handshake_failed
– Für die Verbindung ist TLS erforderlich, und der TLS-Handshake ist fehlgeschlagen.connection_reset
– Die Verbindung wurde unerwartet vom Client geschlossen, während Anforderungen ausgeführt wurden.request_headers_timeout
– Kestrel hat die Verbindung geschlossen, da sie keine Anforderungsheader rechtzeitig empfangen hat.max_request_body_size_exceeded
– Kestrel hat die Verbindung geschlossen, da hochgeladene Daten die maximale Größe überschritten haben.
Zuvor war die Diagnose von Kestrel-Verbindungsproblemen erforderlich, um eine detaillierte Protokollierung auf niedriger Ebene aufzuzeichnen. Allerdings kann es teuer sein, Protokolle zu generieren und zu speichern, und es kann schwierig sein, die richtigen Informationen Im Datenrauschen zu finden.
Metriken sind eine viel günstigere Alternative, die in einer Produktionsumgebung mit minimalen Auswirkungen aktiv bleiben können. Gesammelte Metriken können Dashboards und Warnungen steuern. Sobald ein Problem auf hoher Ebene mit Metriken identifiziert wurde, kann eine weitere Untersuchung mithilfe der Protokollierung und anderer Tools beginnen.
Wir gehen davon aus, dass verbesserte Verbindungsmetriken in vielen Szenarien nützlich sind:
- Untersuchen von Leistungsproblemen, die durch kurze Verbindungslebensdauern verursacht werden.
- Beobachten laufender externer Angriffe auf Kestrel, die sich auf Leistung und Stabilität auswirken.
- Aufzeichnen von versuchten externen Angriffen auf Kestrel, die durch die integrierte Sicherheitshärtung von Kestrel verhindert wurden.
Weitere Informationen finden Sie unter ASP.NET Core-Metriken.
Anpassen von Kestrel-Named-Pipe-Endpunkten
Die Named Pipe-Unterstützung von Kestrel verfügt über verbesserte, erweiterte Anpassungsoptionen. Mit der neuen CreateNamedPipeServerStream
-Methode für die Named Pipe-Optionen können Pipes pro Endpunkt angepasst werden.
Ein Beispiel für eine nützliche Anwendung ist eine Kestrel-App, die zwei Pipe-Endpunkte mit unterschiedlicher Zugriffssicherheit erfordert. Die CreateNamedPipeServerStream
Option kann verwendet werden, um Pipes mit benutzerdefinierten Sicherheitseinstellungen zu erstellen, je nach Pipename.
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
-Option zur Auswahl des Statuscodes basierend auf dem Ausnahmetyp
Eine neue Option bei der Konfiguration des ExceptionHandlerMiddleware
ermöglicht es App-Entwickelnden, den Statuscode auszuwählen, der zurückgegeben werden soll, wenn während der Anfragebearbeitung eine Ausnahme auftritt. Die neue Option ändert den Statuscode, der in der Antwort ProblemDetails
von ExceptionHandlerMiddleware
festgelegt wird.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Abmelden von HTTP-Metriken für bestimmte Endpunkte und Anforderungen
.NET 9 bietet die Möglichkeit, HTTP-Metriken für bestimmte Endpunkte und Anforderungen abzumelden. Das Abmelden von Aufzeichnungsmetriken ist von Vorteil für Endpunkte, die häufig von automatisierten Systemen aufgerufen werden, z. B. Integritätsprüfungen. Aufzeichnungsmetriken für diese Anforderungen sind im Allgemeinen nicht erforderlich.
HTTP-Anforderungen an einen Endpunkt können durch Hinzufügen von Metadaten von Metriken ausgeschlossen werden. Entweder:
- Fügen Sie das
[DisableHttpMetrics]
-Attribut dem Web-API-Controller, SignalR-Hub oder gRPC-Dienst hinzu. - Rufen Sie DisableHttpMetrics beim Zuordnen von Endpunkten beim Start der App auf:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
Die Eigenschaft MetricsDisabled
wurde zu IHttpMetricsTagsFeature
hinzugefügt für:
- Erweiterte Szenarien, in denen eine Anforderung keinem Endpunkt zugeordnet ist.
- Dynamisches Deaktivieren der Metriksammlung für bestimmte HTTP-Anforderungen.
// 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);
});
Datenschutzunterstützung für das Löschen von Schlüsseln.
Vor .NET 9 waren Datenschutzschlüssel nicht löschbar, um Datenverlust zu verhindern. Wenn Sie einen Schlüssel löschen, sind die geschützten Daten unwiederbringlich verloren. Angesichts ihrer geringen Größe hat die Anhäufung dieser Schlüssel im Allgemeinen minimale Auswirkungen. Um jedoch extrem lang laufende Dienste zu berücksichtigen, verfügen wir über die Option, Schlüssel zu löschen. Im Allgemeinen sollten nur alte Schlüssel gelöscht werden. Löschen Sie Schlüssel nur, wenn Sie das Risiko eines Datenverlusts im Austausch für Speicherplatzersparnisse in Kauf nehmen können. Wir empfehlen, Datenschutzschlüssel nicht zu löschen.
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.");
}
Middleware unterstützt Keyed DI
Middleware unterstützt jetzt Keyed DI sowohl im Konstruktor als auch in der Invoke
/InvokeAsync
Methode:
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);
}
Das ASP.NET Core HTTPS-Entwicklungszertifikat als vertrauenswürdig einstufen
Auf Ubuntu- und Fedora-basierten Linux-Distributionen konfiguriert dotnet dev-certs https --trust
nun das ASP.NET Core HTTPS-Entwicklungszertifikat als vertrauenswürdiges Zertifikat für:
- Chromium-Browser, z. B. Google Chrome, Microsoft Edge und Chromium.
- Mozilla Firefox und Mozilla abgeleitete Browser.
- .NET APIs, zum Beispiel, HttpClient
Zuvor funktionierte --trust
nur unter Windows und macOS. Eine Zertifikats-Vertrauensstellung wird pro Benutzender angewendet.
Um eine Vertrauenstellung in OpenSSL herzustellen, wird das dev-certs
-Tool verwendet:
- Platziert das Zertifikat in
~/.aspnet/dev-certs/trust
- Führt eine vereinfachte Version von OpenSSL's c_rehash tool auf dem Verzeichnis aus.
- Fordert den Benutzenden auf, die Umgebungsvariable
SSL_CERT_DIR
zu aktualisieren.
Um eine Vertrauensstellung in dotnet aufzubauen, legt das Tool das Zertifikat im My/Root
-Zertifikatspeicher ab.
Um die Vertrauensstellung in NSS-Datenbanken herzustellen, falls vorhanden, durchsucht das Tool das home-Verzeichnis nach Firefox-Profilen, ~/.pki/nssdb
und ~/snap/chromium/current/.pki/nssdb
. Für jedes gefundene Verzeichnis fügt das Tool einen Eintrag in das Verzeichnis nssdb
ein.
Vorlagen wurden auf die neuesten Versionen bootstrap, jQuery und jQuery Validation aktualisiert.
Die ASP.NET Core-Projektvorlagen und -bibliotheken wurden aktualisiert, um die neuesten Versionen von Bootstrap, jQuery und jQuery Validation zu verwenden, insbesondere:
- Bootstrap 5.3.3
- jQuery 3.7.1
- jQuery Validation 1.21.0
ASP.NET Core