Wat is er nieuw in ASP.NET Core 9.0?
In dit artikel worden de belangrijkste wijzigingen in ASP.NET Core 9.0 beschreven met koppelingen naar relevante documentatie.
Optimalisatie van statische assetlevering
MapStaticAssets
conventies voor routeringseindpunten is een nieuwe functie waarmee de levering van statische assets in ASP.NET Core-apps wordt geoptimaliseerd.
Zie Blazorvoor informatie over de levering van statische assets voor -apps.
Voor het serveren van statische assets volgens best practices voor productie is een aanzienlijke hoeveelheid werk en technische expertise vereist. Zonder optimalisaties zoals compressie, cachen en vingerafdrukken:
- De browser moet extra aanvragen indienen bij elke pagina die wordt geladen.
- Er worden meer bytes overgedragen dan nodig is via het netwerk.
- Soms worden verouderde versies van bestanden aan clients aangeboden.
Voor het maken van krachtige web-apps is het optimaliseren van assetlevering in de browser vereist. Mogelijke optimalisaties zijn:
- Voer een bepaalde asset eenmaal uit totdat het bestand wordt gewijzigd of de browser de cache wist. Stel de ETag koptekst in.
- Voorkomen dat de browser oude of verouderde assets gebruikt nadat een app is bijgewerkt. Stel de koptekst Laatst gewijzigd in.
- Stel de juiste cache headers in .
- Gebruik caching middleware .
- Gebruik indien mogelijk gecomprimeerde versies van de assets.
- Gebruik een CDN- om de assets dichter bij de gebruiker te leveren.
- Minimaliseer de grootte van assets die aan de browser worden geleverd. Deze optimalisatie omvat geen minificatie.
MapStaticAssets is een nieuwe functie waarmee de levering van statische assets in een app wordt geoptimaliseerd. Het is ontworpen om te werken met alle UI-frameworks, waaronder Blazor, Razor Pagina's en MVC. Het is meestal een directe vervanging voor 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
werkt door build- en publicatietijdprocessen te combineren om informatie te verzamelen over alle statische resources in een app. Deze informatie wordt vervolgens gebruikt door de runtimebibliotheek om deze bestanden efficiënt te leveren aan de browser.
MapStaticAssets
kan in de meeste situaties UseStaticFiles
vervangen, maar deze is geoptimaliseerd voor het leveren van de assets waarvan de app kennis heeft op het moment van bouwen en publiceren. Als de app assets van andere locaties bedient, zoals schijf of ingesloten resources, moet UseStaticFiles
worden gebruikt.
MapStaticAssets
biedt de volgende voordelen die niet zijn gevonden met UseStaticFiles
:
- Bouwtijdcompressie voor alle assets in de app:
-
gzip
tijdens het ontwikkelen engzip + brotli
tijdens het publiceren. - Alle assets worden gecomprimeerd met het doel om de grootte van de assets tot het minimum te verkleinen.
-
- Op inhoud gebaseerde
ETags
: deEtags
voor elke resource zijn de Base64 gecodeerde tekenreeks van de SHA-256 hash van de inhoud. Dit zorgt ervoor dat de browser een bestand alleen opnieuw download als de inhoud ervan is gewijzigd.
In de volgende tabel ziet u de oorspronkelijke en gecomprimeerde grootten van de CSS- en JS-bestanden in de standaardsjabloon Razor Pagina's:
Bestand | Origineel | Gecomprimeerd | % reductie |
---|---|---|---|
bootstrap.min.css | 163 | 17.5 | 89.26% |
jquery.js | 89.6 | 28 | 68,75% |
bootstrap.min.js | 78.5 | 20 | 74,52% |
Totaal | 331.1 | 65.5 | 80.20% |
In de volgende tabel ziet u de oorspronkelijke en gecomprimeerde grootten met behulp van de Fluent UI Blazor onderdelenbibliotheek:
Bestand | Origineel | Gecomprimeerd | % reductie |
---|---|---|---|
vloeiend.js | 384 | 73 | 80,99% |
fluent.css | 94 | 11 | 88.30% |
Totaal | 478 | 84 | 82.43% |
Voor een totaal van 478 kB ongecomprimeerd tot 84 kB gecomprimeerd.
In de volgende tabel ziet u de oorspronkelijke en gecomprimeerde grootten met behulp van de bibliotheek MudBlazorBlazor onderdelen:
Bestand | Origineel | Gecomprimeerd | Vermindering |
---|---|---|---|
MudBlazor.min.css | 541 | 37,5 | 93.07% |
MudBlazor.min.js | 47.4 | 9.2 | 80,59% |
Totaal | 588.4 | 46.7 | 92.07% |
Optimalisatie vindt automatisch plaats wanneer u MapStaticAssets
gebruikt. Wanneer een bibliotheek wordt toegevoegd of bijgewerkt, bijvoorbeeld met nieuwe JavaScript of CSS, worden de assets geoptimaliseerd als onderdeel van de build. Optimalisatie is met name nuttig voor mobiele omgevingen die een lagere bandbreedte of onbetrouwbare verbindingen kunnen hebben.
Zie de volgende bronnen voor meer informatie over de nieuwe functies voor bestandslevering:
- statische bestanden in ASP.NET Core
- ASP.NET Core Blazor statische bestanden
Dynamische compressie op de server inschakelen versus MapStaticAssets
MapStaticAssets
heeft de volgende voordelen ten opzichte van dynamische compressie op de server:
- Is eenvoudiger omdat er geen serverspecifieke configuratie is.
- Is beter presterend omdat de assets tijdens de build worden gecomprimeerd.
- Hiermee kan de ontwikkelaar extra tijd besteden tijdens het buildproces om ervoor te zorgen dat de assets de minimale grootte hebben.
Bekijk de volgende tabel voor het vergelijken van MudBlazor-compressie met dynamische IIS-compressie en MapStaticAssets
:
IIS gzip | MapStaticAssets |
MapStaticAssets reductie |
---|---|---|
≅ 90 | 37,5 | 59% |
Blazor
In deze sectie worden nieuwe functies voor Blazorbeschreven.
sjabloon voor .NET MAUIBlazor Hybrid- en web-app-oplossingen
Met een nieuwe oplossingssjabloon kunt u eenvoudiger .NET MAUI systeemeigen en Blazor webclient-apps maken die dezelfde gebruikersinterface delen. Deze sjabloon laat zien hoe u client-apps maakt die het hergebruik van code maximaliseren en gericht zijn op Android-, iOS-, Mac-, Windows- en webplatforms.
Belangrijke functies van deze sjabloon zijn:
- De mogelijkheid om een Blazor interactieve weergavemodus voor de web-app te kiezen.
- Automatisch maken van de juiste projecten, waaronder een Blazor Web App (globale interactieve automatische rendering) en een .NET MAUIBlazor Hybrid-app.
- De gemaakte projecten maken gebruik van een gedeelde Razor klassebibliotheek (RCL) om de Razor onderdelen van de gebruikersinterface te onderhouden.
- Voorbeeldcode is opgenomen die laat zien hoe u afhankelijkheidsinjectie kunt gebruiken om verschillende interface-implementaties te bieden voor de Blazor Hybrid-app en de Blazor Web App.
Als u wilt beginnen, installeert u de .NET 9 SDK- en installeert u de .NET MAUI-workload, die de sjabloon bevat:
dotnet workload install maui
Maak een oplossing op basis van de projectsjabloon in een opdrachtshell met behulp van de volgende opdracht:
dotnet new maui-blazor-web
De sjabloon is ook beschikbaar in Visual Studio.
Notitie
Er treedt momenteel een uitzondering op als Blazor renderingmodi zijn gedefinieerd op het niveau per pagina/onderdeel. Zie BlazorWebView een manier nodig heeft om het overschrijven van ResolveComponentForRenderMode (dotnet/aspnetcore
#51235) in te schakelenvoor meer informatie.
Zie Een .NET MAUIBlazor Hybrid-app bouwen met een Blazor Web Appvoor meer informatie.
Renderinglocatie, interactiviteit en toegewezen rendermodus detecteren tijdens runtime
We hebben een nieuwe API geïntroduceerd die is ontworpen om het proces van het uitvoeren van query's op onderdeelstatussen tijdens runtime te vereenvoudigen. Deze API biedt de volgende mogelijkheden:
- De huidige uitvoeringslocatie van het onderdeelbepalen: dit kan handig zijn voor foutopsporing en het optimaliseren van de prestaties van onderdelen.
- Controleer of het onderdeel wordt uitgevoerd in een interactieve omgeving: dit kan handig zijn voor onderdelen die verschillende gedragingen hebben op basis van de interactiviteit van hun omgeving.
- De toegewezen rendermodus voor het onderdeel ophalen: Inzicht in de weergavemodus kan helpen bij het optimaliseren van het renderingproces en het verbeteren van de algehele prestaties van een onderdeel.
Zie ASP.NET Core Blazor rendermodivoor meer informatie.
Verbeterde herverbinding aan de server-zijde:
De volgende verbeteringen zijn aangebracht in de standaard server-side reconnectie-ervaring:
Wanneer de gebruiker terug navigeert naar een app met een niet-verbonden circuit, wordt er onmiddellijk opnieuw verbinding gemaakt in plaats van te wachten op de duur van het volgende interval voor opnieuw verbinden. Dit verbetert de gebruikerservaring bij het navigeren naar een app op een browsertabblad dat in de slaapstand is gegaan.
Wanneer een poging om opnieuw verbinding te maken de server bereikt, maar de server het circuit al heeft vrijgegeven, wordt automatisch een paginavernieuwing uitgevoerd. Hierdoor voorkomt u dat de gebruiker de pagina handmatig moet vernieuwen als dit waarschijnlijk leidt tot een geslaagde herverbinding.
De timing van opnieuw verbinden maakt gebruik van een berekende back-offstrategie. Standaard worden de eerste pogingen om opnieuw verbinding te maken, snel achter elkaar uitgevoerd zonder een interval voor opnieuw proberen voordat berekende vertragingen tussen pogingen worden geïntroduceerd. U kunt het gedrag van de herhalingsinterval aanpassen door een functie op te geven om het interval te berekenen, zoals het volgende voorbeeld van exponentieel uitstel demonstreert.
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
De stijl van de standaardinterface voor opnieuw verbinden is gemoderniseerd.
Zie ASP.NET Core BlazorSignalR guidancevoor meer informatie.
Vereenvoudigde serialisatie van verificatiestatussen voor Blazor Web Apps
Met nieuwe API's kunt u eenvoudiger verificatie toevoegen aan een bestaande Blazor Web App. Wanneer u een nieuwe Blazor Web App maakt met verificatie met behulp van Afzonderlijke accounts en u webassembly-interactiviteit inschakelt, bevat het project een aangepaste AuthenticationStateProvider in zowel de server- als clientprojecten.
Deze providers stromen de verificatiestatus van de gebruiker naar de browser. Door verificatie op de server in plaats van de client heeft de app tijdens het voorbereiden toegang tot de verificatiestatus en voordat de .NET WebAssembly-runtime wordt geïnitialiseerd.
De aangepaste AuthenticationStateProvider-implementaties maken gebruik van de Persistent Component State-service (PersistentComponentState) om de verificatiestatus te serialiseren in HTML-opmerkingen en deze terug te lezen van WebAssembly om een nieuw AuthenticationState exemplaar te maken.
Dit werkt goed als u bent gestart vanuit de Blazor Web App projectsjabloon en de optie afzonderlijke accounts hebt geselecteerd, maar het is veel code om uzelf te implementeren of te kopiëren als u verificatie wilt toevoegen aan een bestaand project. Er zijn nu API's, die nu deel uitmaken van de Blazor Web App-projectsjabloon, die kunnen worden aangeroepen in de server- en clientprojecten om deze functionaliteit toe te voegen:
- AddAuthenticationStateSerialization: voegt de benodigde services toe om de verificatiestatus op de server te serialiseren.
- AddAuthenticationStateDeserialization: voegt de benodigde services toe om de verificatiestatus in de browser te deserialiseren.
De API serialiseert standaard alleen de naam en rolclaims aan de serverzijde voor toegang in de browser. Een optie kan worden doorgegeven aan AddAuthenticationStateSerialization om alle claims op te nemen.
Zie de volgende secties van ASP.NET Core Blazor verificatie en autorisatievoor meer informatie:
Statische SSR-pagina's (Server Side Rendering) toevoegen aan een wereldwijd interactieve Blazor Web App
Met de release van .NET 9 is het nu eenvoudiger om statische SSR-pagina's toe te voegen aan apps die gebruikmaken van wereldwijde interactiviteit.
Deze aanpak is alleen handig wanneer de app specifieke pagina's heeft die niet kunnen werken met interactieve Server of WebAssembly-rendering. Gebruik deze benadering bijvoorbeeld voor pagina's die afhankelijk zijn van het lezen/schrijven van HTTP-cookies en die alleen kunnen werken in een aanvraag-/antwoordcyclus in plaats van interactieve rendering. Voor pagina's die werken met interactieve rendering, moet u ze niet dwingen om statische SSR-rendering te gebruiken, omdat deze minder efficiënt en minder responsief zijn voor de eindgebruiker.
Markeer een Razor-onderdeelpagina met het nieuwe kenmerk [ExcludeFromInteractiveRouting]
toegewezen aan de @attribute
Razor-instructie:
@attribute [ExcludeFromInteractiveRouting]
Door het kenmerk toe te passen, verlaat de navigatie naar de pagina interactieve routering. Inkomende navigatie wordt gedwongen om een volledige pagina opnieuw te laden in plaats van de pagina via interactieve navigatie op te lossen. Het opnieuw laden van de volledige pagina dwingt het hoofdonderdeel op het hoogste niveau af, meestal het App
onderdeel (App.razor
), om opnieuw te laden vanaf de server, zodat de app kan overschakelen naar een andere rendermodus op het hoogste niveau.
Met de RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting extensiemethode kan het onderdeel detecteren of het [ExcludeFromInteractiveRouting]
kenmerk wordt toegepast op de huidige pagina.
Gebruik in het App
onderdeel het patroon in het volgende voorbeeld:
- Pagina's die niet zijn geannoteerd met de attributen
[ExcludeFromInteractiveRouting]
vallen standaard in deInteractiveServer
weergavemodus met globale interactiviteit. U kuntInteractiveServer
vervangen doorInteractiveWebAssembly
ofInteractiveAuto
om een andere standaardmodus voor globale weergave op te geven. - Pagina's met het kenmerk
[ExcludeFromInteractiveRouting]
nemen statische SSR aan (PageRenderMode
wordt toegewezennull
).
<!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;
}
Een alternatief voor het gebruik van de RazorComponentsEndpointHttpContextExtensions.AcceptsInteractiveRouting-extensiemethode is het handmatig lezen van metagegevens van eindpunten met behulp van HttpContext.GetEndpoint()?.Metadata
.
Deze functie wordt gedekt door de referentiedocumentatie in ASP.NET Core Blazor rendermodi.
Constructorinjectie
De Razor-onderdelen ondersteunen constructorinjectie.
In het volgende voorbeeld injecteert de gedeeltelijke (code-behind) klasse de NavigationManager
-service met een primaire constructor .
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Zie ASP.NET Core Blazor dependency injectionvoor meer informatie.
Websocket-compressie voor interactieve serveronderdelen
Interactieve Server-onderdelen schakelen standaard compressie in voor WebSocket-verbindingen en stellen een frame-ancestors
CSP--instructie (Content Security Policy) in die is ingesteld op 'self'
, waardoor het alleen mogelijk is om de app in te sluiten in een <iframe>
van de oorsprong van waaruit de app wordt geleverd wanneer compressie wordt ingeschakeld of wanneer een configuratie voor de WebSocket-context wordt opgegeven.
Compressie kan worden uitgeschakeld door ConfigureWebSocketOptions
in te stellen op null
, wat het beveiligingsprobleem van de app vermindert om aan te vallen, maar kan leiden tot verminderde prestaties:
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Configureer een strengere frame-ancestors
CSP met een waarde van 'none'
(enkele aanhalingstekens vereist), waardoor WebSocket-compressie mogelijk is, maar voorkomt dat browsers de app insluiten in <iframe>
:
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Zie de volgende bronnen voor meer informatie:
- richtlijnen voor ASP.NET Core BlazorSignalR
- Richtlijnen voor bedreigingsbeperking voor ASP.NET Core Blazor interactieve rendering aan de serverzijde
Afhandelen van toetsenbordcompositie-gebeurtenissen in Blazor
De nieuwe eigenschap KeyboardEventArgs.IsComposing
geeft aan of de toetsenbord gebeurtenis deel uitmaakt van een compositiesessie. Het bijhouden van de samenstellingsstatus van toetsenbordevenementen is van cruciaal belang voor het verwerken van internationale tekeninvoermethoden.
Parameter OverscanCount
toegevoegd aan QuickGrid
Het QuickGrid
onderdeel maakt nu een OverscanCount
eigenschap beschikbaar die aangeeft hoeveel extra rijen worden weergegeven voor en na het zichtbare gebied wanneer virtualisatie is ingeschakeld.
De standaard OverscanCount
is 3. In het volgende voorbeeld wordt de OverscanCount
verhoogd tot 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumber
onderdeel ondersteunt het kenmerk type="range"
Het InputNumber<TValue> onderdeel ondersteunt nu het type="range"
kenmerk, waarmee een bereikinvoer wordt gemaakt die ondersteuning biedt voor modelbinding en formuliervalidatie, meestal weergegeven als een schuifregelaar of kiesbesturingselement in plaats van een tekstvak:
<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; }
}
}
Nieuwe verbeterde navigatie-gebeurtenissen
Activeer JavaScript-callbacks voor of na verbeterde navigatie met nieuwe gebeurtenisluisteraars:
blazor.addEventListener("enhancednavigationstart", {CALLBACK})
blazor.addEventListener("enhancednavigationend", {CALLBACK})
Voor meer informatie, zie ASP.NET Core Blazor JavaScript met statische server-side rendering (statische SSR).
SignalR
In deze sectie worden nieuwe functies voor SignalRbeschreven.
Ondersteuning voor polymorfe typen in SignalR Hubs
Hubmethoden kunnen nu een basisklasse accepteren in plaats van de afgeleide klasse om polymorfe scenario's mogelijk te maken. Het basistype moet worden geannoteerd om polymorfisme toe te staan.
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; }
}
Verbeterde activiteiten voor SignalR
SignalR heeft nu een ActivitySource voor zowel de hubserver als de client.
.NET SignalR server ActivitySource
De SignalR ActivitySource met de naam Microsoft.AspNetCore.SignalR.Server
verzendt gebeurtenissen voor hubmethode-aanroepen:
- Elke methode is een eigen activiteit, dus alles wat een activiteit verzendt tijdens de aanroep van de hubmethode, valt onder de hubmethodeactiviteit.
- Hub-methodeactiviteiten hebben geen bovenliggende ouder. Dit betekent dat ze niet worden gebundeld onder de langlopende SignalR-verbinding.
In het volgende voorbeeld worden de .NET Aspire dashboard- en de OpenTelemetry--pakketten gebruikt:
<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" />
Voeg de volgende opstartcode toe aan het Program.cs
-bestand:
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();
Hier volgt een voorbeeld van uitvoer van het Aspire Dashboard:
.NET SignalR client ActivitySource
De SignalR ActivitySource met de naam Microsoft.AspNetCore.SignalR.Client
gebeurtenissen verzendt voor een SignalR-client:
- De .NET-SignalR-client heeft een
ActivitySource
met de naamMicrosoft.AspNetCore.SignalR.Client
. Hub-aanroepen creëren nu een clientbereik. Houd er rekening mee dat andere SignalR-clients, zoals de JavaScript-client, geen ondersteuning bieden voor tracering. Deze functie wordt in toekomstige releases toegevoegd aan meer clients. - Hub-aanroepen op de client en server ondersteunen contextdoorgifte. Door de traceringscontext door te geven, wordt echte gedistribueerde tracering mogelijk gemaakt. Het is nu mogelijk om aanroepen van de client naar de server en terug te zien.
Deze nieuwe activiteiten zien er als volgt uit in het .NET Aspire dashboard:
gedeelde tracering
SignalR biedt ondersteuning voor bijsnijden en systeemeigen AOT
Door het Native AOT-traject voort te zetten, gestart in .NET 8, hebben we ondersteuning voor trimmen en systeemeigen AOT-compilatie ingeschakeld voor zowel SignalR client- als serverscenario's. U kunt nu profiteren van de prestatievoordelen van het gebruik van systeemeigen AOT in toepassingen die gebruikmaken van SignalR voor realtime webcommunicatie.
Aan de slag gaan
Installeer de nieuwste .NET 9 SDK-.
Maak een oplossing op basis van de webapiaot
-sjabloon in een opdrachtshell met behulp van de volgende opdracht:
dotnet new webapiaot -o SignalRChatAOTExample
Vervang de inhoud van het bestand Program.cs
door de volgende 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);
}
}
In het voorgaande voorbeeld wordt een systeemeigen Windows-uitvoerbare versie van 10 MB en een Linux-uitvoerbaar bestand van 10,9 MB geproduceerd.
Beperkingen
- Alleen het JSON-protocol wordt momenteel ondersteund:
- Zoals wordt weergegeven in de voorgaande code, moeten apps die gebruikmaken van JSON-serialisatie en Systeemeigen AOT de
System.Text.Json
Brongenerator gebruiken. - Dit volgt dezelfde benadering als minimale API's.
- Zoals wordt weergegeven in de voorgaande code, moeten apps die gebruikmaken van JSON-serialisatie en Systeemeigen AOT de
- Op de SignalR-server worden hubmethodeparameters van het type
IAsyncEnumerable<T>
enChannelReader<T>
niet ondersteund wanneerT
een ValueType (struct
) is. Het gebruik van deze typen leidt tot een runtime-uitzondering tijdens het opstarten in de ontwikkelingsfase en in de gepubliceerde app. Zie SignalRvoor meer informatie: IAsyncEnumerable<T> en ChannelReader<T> gebruiken met ValueTypes in native AOT (dotnet/aspnetcore
#56179). -
Sterk getypte hubs worden niet ondersteund met Native AOT (
PublishAot
). Het gebruik van sterk getypte hubs met native AOT resulteert in waarschuwingen tijdens het bouwen en publiceren en een runtime-uitzondering. Het gebruik van sterk getypte hubs met trimming (PublishedTrimmed
) wordt ondersteund. - Alleen
Task
,Task<T>
,ValueTask
ofValueTask<T>
worden ondersteund voor asynchrone retourtypen.
Minimale APIs
In deze sectie worden nieuwe functies voor minimale API's beschreven.
InternalServerError
en InternalServerError<TValue>
toegevoegd aan TypedResults
De TypedResults klasse is een handig voertuig voor het retourneren van sterk getypeerde HTTP-statuscodereacties van een minimale API.
TypedResults
bevat nu factorymethoden en -typen voor het retourneren van '500 Internal Server Error'-antwoorden van eindpunten. Hier volgt een voorbeeld dat een 500-antwoord retourneert:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Oproep ProducesProblem
en ProducesValidationProblem
in routegroepen
De ProducesProblem
- en ProducesValidationProblem
-extensiemethoden zijn bijgewerkt ter ondersteuning van het gebruik van routegroepen. Deze methoden geven aan dat alle eindpunten in een routegroep ProblemDetails
of ValidationProblemDetails
antwoorden kunnen retourneren voor het doel van OpenAPI-metagegevens.
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
en ValidationProblem
resultaattypen ondersteunen constructie met IEnumerable<KeyValuePair<string, object?>>
waarden
Vóór .NET 9 vereiste het construeren van de resultaten van Problem en ValidationProblem in minimale APIs dat de eigenschappen errors
en extensions
werden geïnitialiseerd met een implementatie van IDictionary<string, object?>
. In deze release ondersteunen deze bouw-API's overbelastingen die IEnumerable<KeyValuePair<string, object?>>
verbruiken.
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);
});
Dankzij GitHub-gebruiker joegoldman2 voor deze bijdrage!
OpenAPI
In deze sectie worden nieuwe functies voor OpenAPI beschreven
Ingebouwde ondersteuning voor het genereren van OpenAPI-documenten
De OpenAPI-specificatie is een standaard voor het beschrijven van HTTP-API's. Met de standaard kunnen ontwikkelaars de vorm van API's definiëren die kunnen worden aangesloten op clientgeneratoren, servergeneratoren, testhulpprogramma's, documentatie en meer. In .NET 9 biedt ASP.NET Core ingebouwde ondersteuning voor het genereren van OpenAPI-documenten die op controller gebaseerde of minimale API's vertegenwoordigen via het pakket Microsoft.AspNetCore.OpenApi.
De volgende gemarkeerde code-aanroepen:
-
AddOpenApi
om de vereiste afhankelijkheden te registreren in de DI-container van de app. -
MapOpenApi
om de vereiste OpenAPI-eindpunten te registreren in de routering van de app.
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) => $"Hello {name}"!);
app.Run();
Installeer het Microsoft.AspNetCore.OpenApi
-pakket in het project met behulp van de volgende opdracht:
dotnet add package Microsoft.AspNetCore.OpenApi
Voer de app uit en navigeer naar openapi/v1.json
om het gegenereerde OpenAPI-document weer te geven:
OpenAPI-documenten kunnen ook tijdens de build worden gegenereerd door het Microsoft.Extensions.ApiDescription.Server
-pakket toe te voegen:
dotnet add package Microsoft.Extensions.ApiDescription.Server
Als u de locatie van de verzonden OpenAPI-documenten wilt wijzigen, stelt u het doelpad in de eigenschap OpenApiDocumentsDirectory in het projectbestand van de app in:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>
Voer dotnet build
uit en inspecteer het gegenereerde JSON-bestand in de projectmap.
ASP.NET Core ingebouwde OpenAPI-documentgeneratie biedt ondersteuning voor verschillende aanpassingen en opties. Het biedt document-, bewerkings- en schematransformaties en heeft de mogelijkheid om meerdere OpenAPI-documenten voor dezelfde toepassing te beheren.
Zie de nieuwe Microsoft.AspNetCore.OpenApi-documentenvoor meer informatie over de nieuwe openAPI-documentmogelijkheden van ASP.NET Core.
Microsoft.AspNetCore.OpenApi biedt ondersteuning voor trimming en Native AOT
OpenAPI in ASP.NET Core biedt ondersteuning voor bijsnijden en systeemeigen AOT. Volg de volgende stappen om een OpenAPI-app te maken en te publiceren met trimming en native AOT:
Maak een nieuw ASP.NET Core Web API-project (Native AOT).
dotnet new webapiaot
Voeg het Microsoft.AspNetCore.OpenAPI-pakket toe.
dotnet add package Microsoft.AspNetCore.OpenApi
Werk Program.cs
bij om het genereren van OpenAPI-documenten in te schakelen.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Publiceer de app.
dotnet publish
Verificatie en autorisatie
In deze sectie worden nieuwe functies voor verificatie en autorisatie beschreven.
OpenIdConnectHandler voegt ondersteuning toe voor pushed authorization requests (PAR)
We willen Joe DeCock van Duende Software bedanken voor het toevoegen van pushed authorization requests (PAR) aan ASP.NET Core's OpenIdConnectHandler. Joe heeft de achtergrond en motivatie beschreven voor het inschakelen van PAR in zijn API-voorstel als volgt:
Pushed Authorization Requests (PAR) is een relatief nieuwe OAuth-standaard die de beveiliging van OAuth- en OIDC-stromen verbetert door autorisatieparameters van het frontkanaal naar het back-kanaal te verplaatsen. Dat wil zeggen: het verplaatsen van autorisatieparameters van omleidings-URL's in de browser naar rechtstreekse machine-tot-machine http-aanroepen in de back end.
Dit voorkomt dat een cyberaanvaller in de browser:
- Het zien van autorisatieparameters, die tot het lekken van PII kunnen leiden.
- Aanpassen van die parameters. De cyberaanval kan bijvoorbeeld het bereik van de aangevraagde toegang wijzigen.
Als u de autorisatieparameters pusht, blijven ook aanvraag-URL's kort. Het autoriseren van parameters kan erg lang worden wanneer u complexere functies van OAuth en OIDC gebruikt, zoals Rich Authorization Requests. Lange URLs veroorzaken problemen in veel browsers en netwerkinfrastructuren.
Het gebruik van PAR wordt aangemoedigd door de FAPI-werkgroep binnen de OpenID Foundation. bijvoorbeeld het FAPI2.0-beveiligingsprofiel het gebruik van PAR vereist. Dit beveiligingsprofiel wordt gebruikt door veel van de groepen die werken aan open banking (voornamelijk in Europa), gezondheidszorg en in andere sectoren met hoge beveiligingsvereisten.
PAR wordt ondersteund door een aantal identity providers, waaronder
Voor .NET 9 hebben we besloten par standaard in te schakelen als het detectiedocument van de identity provider ondersteuning voor PAR adverteert, omdat het verbeterde beveiliging moet bieden voor providers die dit ondersteunen. Het detectiedocument van de identity provider vindt u meestal op .well-known/openid-configuration
. Als dit problemen veroorzaakt, kunt u PAR als volgt uitschakelen via OpenIdConnectOptions.PushedAuthorizationBehavior:
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;
});
Gebruik in plaats daarvan PushedAuthorizationBehavior.Require om ervoor te zorgen dat de verificatie alleen slaagt als PAR wordt gebruikt. Deze wijziging introduceert ook een nieuwe OnPushAuthorization gebeurtenis voor OpenIdConnectEvents, die kan worden gebruikt om de push-autorisatieaanvraag aan te passen of handmatig te verwerken. Zie het API-voorstel voor meer informatie.
Aanpassing van OIDC- en OAuth-parameters
De OAuth- en OIDC-verificatiehandlers hebben nu een AdditionalAuthorizationParameters
optie om de parameters voor autorisatieberichten die meestal zijn opgenomen als onderdeel van de omleidingsquerytekenreeks gemakkelijker aan te passen. In .NET 8 en eerder vergt dit een aangepaste OnRedirectToIdentityProvider-callback of een overschreven BuildChallengeUrl-methode in een aangepaste verwerker. Hier volgt een voorbeeld van .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;
};
});
Het voorgaande voorbeeld kan nu worden vereenvoudigd tot de volgende code:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Uitgebreide verificatievlagmen configureren HTTP.sys
U kunt nu de HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
- en HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
HTTP.sys-vlaggen configureren met behulp van de nieuwe EnableKerberosCredentialCaching
- en CaptureCredentials
-eigenschappen op de HTTP.sys AuthenticationManager om te optimaliseren hoe Windows-verificatie wordt verwerkt. Bijvoorbeeld:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Allerlei
In de volgende secties worden diverse nieuwe functies beschreven.
Nieuwe HybridCache
bibliotheek
Belangrijk
HybridCache
is momenteel nog in preview, maar wordt volledig uitgebracht na .NET 9.0 in een toekomstige secundaire versie van .NET Extensions.
De HybridCache
API overbrugt enkele hiaten in de bestaande IDistributedCache en IMemoryCache API's. Er worden ook nieuwe mogelijkheden toegevoegd, zoals:
- 'Stampede'-beveiliging om parallelle ophaalt van hetzelfde werk te voorkomen.
- Configureerbare serialisatie.
HybridCache
is ontworpen als vervanging voor bestaande IDistributedCache
en IMemoryCache
gebruik, en biedt een eenvoudige API voor het toevoegen van nieuwe cachecode. Het biedt een geïntegreerde API voor zowel in-proces- als out-of-process caching.
Als u wilt zien hoe de HybridCache
-API wordt vereenvoudigd, vergelijkt u deze met code die gebruikmaakt van IDistributedCache
. Hier volgt een voorbeeld van hoe het gebruik van IDistributedCache
eruitziet:
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)
{ /* ... */ }
}
Dat is veel werk om elke keer goed te komen, inclusief zaken als serialisatie. En in het geval van een cache miss kunt u uiteindelijk meerdere gelijktijdige threads krijgen, die allemaal een cachemiss ervaren, allemaal de onderliggende gegevens ophalen, allemaal serialiseren, en allemaal die gegevens naar de cache verzenden.
Om deze code te vereenvoudigen en te verbeteren met HybridCache
, moeten we eerst de nieuwe bibliotheek toevoegen Microsoft.Extensions.Caching.Hybrid
:
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Registreer de HybridCache
-service, zoals u een IDistributedCache
-implementatie zou registreren:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
De meeste problemen met caching kunnen nu naar HybridCache
worden verzonden:
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
);
}
}
We bieden een concrete implementatie van de HybridCache
abstracte klasse via afhankelijkheidsinjectie, maar het is bedoeld dat ontwikkelaars aangepaste implementaties van de API kunnen bieden. De HybridCache
-implementatie behandelt alles met betrekking tot caching, inclusief gelijktijdige verwerking van bewerkingen. Het cancel
-token hier staat voor de gecombineerde annulering van alle gelijktijdige bellers, niet alleen de annulering van de beller die we kunnen zien (dat wil zeggen, token
).
Scenario's met hoge doorvoer kunnen verder worden geoptimaliseerd met behulp van het TState
patroon, om enige overhead van vastgelegde variabelen en callbacks per instantie te voorkomen:
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
de geconfigureerde IDistributedCache
-implementatie gebruikt, indien aanwezig, voor secundaire out-of-process caching, bijvoorbeeld met Redis. Maar zelfs zonder een IDistributedCache
biedt de HybridCache
-service nog steeds in-process caching en 'stampede'-beveiliging.
Een notitie over het hergebruik van objecten
In typische bestaande code die gebruikmaakt van IDistributedCache
, resulteert elk ophalen van een object uit de cache in de deserialisatie. Dit gedrag betekent dat elke gelijktijdige aanroeper een afzonderlijk exemplaar van het object krijgt, dat niet kan communiceren met andere exemplaren. Het resultaat is threadveiligheid, omdat er geen risico bestaat op gelijktijdige wijzigingen in hetzelfde objectexemplaar.
Omdat veel HybridCache
gebruik wordt aangepast aan bestaande IDistributedCache
code, behoudt HybridCache
dit gedrag standaard om gelijktijdigheidsfouten te voorkomen. Een bepaald toepassingsgeval is echter inherent thread-safe.
- Als de typen die in de cache worden opgeslagen onveranderbaar zijn.
- Als de code deze niet wijzigt.
In dergelijke gevallen laat u HybridCache
weten dat het veilig is om exemplaren opnieuw te gebruiken door:
- Het type markeren als
sealed
. Hetsealed
trefwoord in C# betekent dat de klasse niet kan worden overgenomen. -
[ImmutableObject(true)]
kenmerk toepassen. Het kenmerk[ImmutableObject(true)]
geeft aan dat de status van het object niet kan worden gewijzigd nadat het is gemaakt.
Door instanties opnieuw te gebruiken, kan HybridCache
de overhead van CPU- en objecttoewijzingen verminderen die zijn gekoppeld aan deserialisatie per aanroep. Dit kan leiden tot prestatieverbeteringen in scenario's waarbij de objecten in de cache groot zijn of regelmatig worden geopend.
Andere HybridCache
-functies
Net als IDistributedCache
ondersteunt HybridCache
verwijdering per sleutel met een RemoveKeyAsync
methode.
HybridCache
biedt ook optionele API's voor IDistributedCache
implementaties om byte[]
toewijzingen te voorkomen. Deze functie wordt geïmplementeerd door de preview-versies van de Microsoft.Extensions.Caching.StackExchangeRedis
- en Microsoft.Extensions.Caching.SqlServer
-pakketten.
Serialisatie wordt geconfigureerd als onderdeel van het registreren van de service, met ondersteuning voor typespecifieke en gegeneraliseerde serializers via de methoden WithSerializer
en .WithSerializerFactory
, gekoppeld aan de AddHybridCache
-aanroep. De bibliotheek verwerkt standaard string
en byte[]
intern en gebruikt System.Text.Json
voor al het andere, maar u kunt protobuf, xml of iets anders gebruiken.
HybridCache
ondersteunt oudere .NET-runtimes, tot .NET Framework 4.7.2 en .NET Standard 2.0.
Zie HybridCache
voor meer informatie over
Verbeteringen op de uitzonderingspagina voor ontwikkelaars
De ASP.NET Core Developer-uitzonderingspagina wordt weergegeven wanneer een app een onverwerkte uitzondering genereert tijdens de ontwikkeling. De uitzonderingspagina voor ontwikkelaars bevat gedetailleerde informatie over de uitzondering en aanvraag.
Preview 3 heeft eindpuntmetagegevens toegevoegd aan de uitzonderingspagina voor ontwikkelaars. ASP.NET Core gebruikt eindpuntmetagegevens om het gedrag van eindpunten te beheren, zoals routering, reactiecaching, frequentiebeperking, OpenAPI-generatie en meer. In de volgende afbeelding ziet u de nieuwe metagegevensgegevens in de sectie Routing
van de uitzonderingspagina voor ontwikkelaars:
Tijdens het testen van de uitzonderingspagina voor ontwikkelaars zijn kleine kwaliteitsverbeteringen geïdentificeerd. Ze zijn uitgebracht in Preview 4:
- Betere tekstomloop. Lange cookies, queryreekswaarden en methodenamen voegen geen horizontale schuifbalken meer toe.
- Grotere tekst die te vinden is in moderne ontwerpen.
- Consistentere tabelgrootten.
In de volgende geanimeerde afbeelding ziet u de nieuwe uitzonderingspagina voor ontwikkelaars:
Verbeteringen voor foutopsporing in woordenlijst
De layout van het debuggingscherm voor woordenlijsten en andere sleutelwaardeverzamelingen is verbeterd. De sleutel wordt weergegeven in de sleutelkolom van het foutopsporingsprogramma in plaats van te worden samengevoegd met de waarde. In de volgende afbeeldingen ziet u de oude en nieuwe weergave van een woordenlijst in het foutopsporingsprogramma.
Voor:
Na:
ASP.NET Core heeft veel sleutel-waardeverzamelingen. Deze verbeterde foutopsporingservaring is van toepassing op:
- HTTP headers
- Vraagreeksen
- Formulieren
- Koekjes
- Gegevens weergeven
- Routedata
- Functies
Oplossing voor 503-fouten tijdens het recyclen van apps in IIS
Standaard is er nu een vertraging van 1 seconde tussen het moment dat IIS op de hoogte wordt gebracht van een herstart of afsluiting en het moment dat ANCM de beheerde server aangeeft om met afsluiten te beginnen. De vertraging kan worden geconfigureerd via de omgevingsvariabele ANCM_shutdownDelay
of door de shutdownDelay
handler-instelling in te stellen. Beide waarden bevinden zich in milliseconden. De vertraging is voornamelijk om de kans op een race te verminderen waarbij:
- IIS is niet gestart met het in de wachtrij plaatsen van aanvragen om naar de nieuwe app te gaan.
- ANCM begint met het weigeren van nieuwe aanvragen die in de oude app worden geleverd.
Tragere machines of machines met zwaarder CPU-gebruik kunnen deze waarde aanpassen om de kans op 503 te verminderen.
Voorbeeld van het instellen van 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>
De oplossing bevindt zich in de wereldwijd geïnstalleerde ANCM-module die afkomstig is van de hostingbundel.
ASP0026: Een analyse om te waarschuwen wanneer [Authorize] wordt overschreven door [AllowAnonymous] van "verre afstand".
Het lijkt intuïtief dat wanneer een [Authorize]
kenmerk 'dichter' bij een MVC-actie wordt geplaatst dan een [AllowAnonymous]
kenmerk, het het [AllowAnonymous]
kenmerk overschrijft en zo autorisatie afdwingt. Dit is echter niet noodzakelijkerwijs het geval. Wat belangrijk is, is de relatieve volgorde van de kenmerken.
De volgende code toont voorbeelden, waarbij een naastgelegen [Authorize]
-kenmerk wordt overschreven door een [AllowAnonymous]
-kenmerk dat verder weg is.
[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 hebben we een analyzer geïntroduceerd die gevallen zoals deze zal markeren, waarbij een dichterbij zijnd [Authorize]
-kenmerk wordt overschreven door een verder wegzijnd [AllowAnonymous]
-kenmerk van een MVC-actie. De waarschuwing verwijst naar het overschreven [Authorize]
attribuut met het volgende bericht:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
De juiste actie die moet worden ondernomen als u deze waarschuwing ziet, is afhankelijk van de bedoeling achter de kenmerken. Het verder weg [AllowAnonymous]
kenmerk moet worden verwijderd als het onbedoeld het eindpunt zichtbaar maakt voor anonieme gebruikers. Als het kenmerk [AllowAnonymous]
is bedoeld om een dichter [Authorize]
kenmerk te overschrijven, kunt u het kenmerk [AllowAnonymous]
herhalen na het kenmerk [Authorize]
om de intentie te verduidelijken.
[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;
}
Verbeterde metrische gegevens voor Kestrel verbinding
We hebben een aanzienlijke verbetering aangebracht in de metrische gegevens over de verbinding van Kestreldoor metagegevens op te geven over waarom een verbinding is mislukt. De kestrel.connection.duration
-metric bevat nu de reden van het sluiten van de verbinding in het kenmerk error.type
.
Hier volgt een klein voorbeeld van de error.type
waarden:
-
tls_handshake_failed
: de verbinding vereist TLS en de TLS-handshake is mislukt. -
connection_reset
: de verbinding is onverwacht gesloten door de client terwijl aanvragen werden uitgevoerd. -
request_headers_timeout
- Kestrel heeft de verbinding gesloten omdat de verzoekheaders niet tijdig zijn ontvangen. -
max_request_body_size_exceeded
: Kestrel de verbinding gesloten omdat geüploade gegevens de maximale grootte hebben overschreden.
Voorheen vereist het diagnosticeren van Kestrel verbindingsproblemen een server om gedetailleerde logboekregistratie op laag niveau vast te leggen. Logboeken kunnen echter duur zijn om te genereren en op te slaan, en het kan lastig zijn om de juiste informatie te vinden onder de ruis.
Metrische gegevens zijn een veel goedkoper alternatief dat met minimale impact kan worden overgelaten in een productieomgeving. Verzamelde metrische gegevens kunnen dashboards en waarschuwingenaansturen. Zodra een probleem op hoog niveau met metrische gegevens is geïdentificeerd, kan verder onderzoek worden uitgevoerd met behulp van logboekregistratie en andere hulpprogramma's.
We verwachten dat verbeterde metrische verbindingsgegevens nuttig zijn in veel scenario's:
- Prestatieproblemen onderzoeken die worden veroorzaakt door korte levensduur van verbindingen.
- Het observeren van doorlopende externe aanvallen op Kestrel die van invloed zijn op prestaties en stabiliteit.
- Het registreren van pogingen tot externe aanvallen op Kestrel, die door de ingebouwde beveiliging van Kestrelwerden voorkomen.
Zie ASP.NET Core-metrische gegevensvoor meer informatie.
Kestrel genaamde pijp-eindpunten aanpassen
De ondersteuning voor named pipes van Kestrelis verbeterd met geavanceerde aanpassingsopties. De nieuwe CreateNamedPipeServerStream
-methode voor de benoemde pipe-opties maakt het mogelijk om pipes per eindpunt aan te passen.
Een voorbeeld van waar dit nuttig is, is een Kestrel-app waarvoor twee pijpeindpunten met verschillende toegangsbeveiligingzijn vereist. De optie CreateNamedPipeServerStream
kan worden gebruikt voor het maken van pijpen met aangepaste beveiligingsinstellingen, afhankelijk van de naam van de pijp.
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
optie om de statuscode te kiezen op basis van het uitzonderingstype
Met een nieuwe optie bij het configureren van de ExceptionHandlerMiddleware
kunnen app-ontwikkelaars kiezen welke statuscode moet worden geretourneerd wanneer er een uitzondering optreedt tijdens het verwerken van aanvragen. De nieuwe optie wijzigt de statuscode die wordt ingesteld in het ProblemDetails
-antwoord afkomstig van de ExceptionHandlerMiddleware
.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Afmelden voor HTTP-metrische gegevens voor bepaalde eindpunten en aanvragen
.NET 9 introduceert de mogelijkheid om zich af te wijzen voor HTTP-metrische gegevens voor specifieke eindpunten en aanvragen. Het afmelden van metrische gegevens is nuttig voor eindpunten die vaak worden aangeroepen door geautomatiseerde systemen, zoals statuscontroles. Het vastleggen van metrische gegevens voor deze aanvragen is over het algemeen overbodig.
HTTP-aanvragen voor een eindpunt kunnen worden uitgesloten van metrische gegevens door metagegevens toe te voegen. Hetzij:
- Voeg het kenmerk
[DisableHttpMetrics]
toe aan de web-API-controller, SignalR hub of gRPC-service. - Roep DisableHttpMetrics aan bij het configureren van eindpunten tijdens het opstarten van de app.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
De eigenschap MetricsDisabled
is toegevoegd aan IHttpMetricsTagsFeature
voor:
- Geavanceerde scenario's waarbij een verzoek niet aan een eindpunt wordt gekoppeld.
- Verzameling metrische gegevens dynamisch uitschakelen voor specifieke HTTP-aanvragen.
// 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);
});
Ondersteuning voor gegevensbescherming voor het verwijderen van sleutels
Vóór .NET 9 waren gegevensbeveiligingssleutels niet standaard te verwijderen om gegevensverlies te voorkomen. Het verwijderen van een sleutel maakt zijn beveiligde gegevens onherroepelijk verloren. Gezien hun kleine omvang, de accumulatie van deze sleutels heeft over het algemeen minimale impact. Voor extreem langlopende services hebben we echter de optie voor het verwijderen van sleutels geïntroduceerd. Over het algemeen moeten alleen oude sleutels worden verwijderd. Verwijder alleen sleutels wanneer u het risico op gegevensverlies in ruil voor opslagbesparingen kunt accepteren. We raden aan sleutels voor gegevensbeveiliging te niet worden verwijderd.
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 ondersteunt Keyed DI
Middleware ondersteunt nu Keyed DI- in zowel de constructor als de Invoke
/InvokeAsync
functie.
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);
}
Vertrouw het ASP.NET Core HTTPS-ontwikkelingscertificaat op Linux
Op Linux-distributies op basis van Ubuntu en Fedora configureert dotnet dev-certs https --trust
nu ASP.NET Core HTTPS-ontwikkelingscertificaat als een vertrouwd certificaat voor:
- Chromium-browsers, bijvoorbeeld Google Chrome, Microsoft Edge en Chromium.
- Mozilla Firefox en browsers op basis van Mozilla.
- .NET-API's, bijvoorbeeld HttpClient
Voorheen werkte --trust
alleen in Windows en macOS. Certificaatvertrouwen wordt per gebruiker toegepast.
Het dev-certs
-hulpprogramma om vertrouwen in OpenSSL tot stand te brengen:
- Plaatst het certificaat in
~/.aspnet/dev-certs/trust
- Hiermee wordt een vereenvoudigde versie van het hulpprogramma c_rehash van OpenSSL uitgevoerd in de map.
- Vraagt de gebruiker om de omgevingsvariabele
SSL_CERT_DIR
bij te werken.
Om vertrouwen in dotnet tot stand te brengen, plaatst het hulpprogramma het certificaat in het My/Root
certificaatarchief.
Om vertrouwen op te bouwen in NSS-databases, indien aanwezig, doorzoekt de tool in de home directory naar Firefox-profielen, ~/.pki/nssdb
en ~/snap/chromium/current/.pki/nssdb
. Voor elke gevonden map voegt het hulpprogramma een vermelding toe aan de nssdb
.
Sjablonen zijn bijgewerkt naar de nieuwste versies bootstrap, jQuery en jQuery-validatie
De ASP.NET Core-projectsjablonen en -bibliotheken zijn bijgewerkt voor het gebruik van de nieuwste versies van Bootstrap, jQuery en jQuery-validatie, met name:
- Bootstrap 5.3.3
- jQuery 3.7.1
- jQuery-validatie 1.21.0