Sdílet prostřednictvím


Novinky v ASP.NET Core 9.0

Tento článek popisuje nejvýznamnější změny v ASP.NET Core 9.0 s odkazy na příslušnou dokumentaci.

Tento článek byl aktualizován pro .NET 9 Preview 7.

Blazor

Tato část popisuje nové funkce pro Blazor.

.NET MAUIBlazor Hybrid a šablona řešení webové aplikace

Nová šablona řešení usnadňuje vytváření .NET MAUI nativních a Blazor webových klientských aplikací, které sdílejí stejné uživatelské rozhraní. Tato šablona ukazuje, jak vytvářet klientské aplikace, které maximalizují opakované použití kódu a cílí na Android, iOS, Mac, Windows a Web.

Mezi klíčové funkce této šablony patří:

  • Možnost zvolit Blazor interaktivní režim vykreslování pro webovou aplikaci
  • Automatické vytváření příslušných projektů, včetně Blazor webové aplikace (globálního interaktivního automatického .NET MAUIBlazor Hybrid vykreslování) a aplikace
  • Vytvořené projekty používají ke správě komponent uživatelského rozhraní Razor knihovnu sdílených Razor tříd (RCL).
  • Součástí ukázkového kódu je použití injektáže závislostí k poskytování různých implementací rozhraní pro Blazor Hybrid aplikaci a Blazor webovou aplikaci.

Pokud chcete začít, nainstalujte sadu .NET 9 SDK a nainstalujte .NET MAUI úlohu, která obsahuje šablonu:

dotnet workload install maui

Pomocí následujícího příkazu vytvořte řešení ze šablony projektu v příkazovém prostředí:

dotnet new maui-blazor-web

Šablona je také k dispozici v sadě Visual Studio.

Poznámka:

V současné době dojde k výjimce, pokud Blazor jsou režimy vykreslování definovány na úrovni jednotlivých stránek nebo komponent. Další informace naleznete v tématu BlazorWebView potřebuje způsob, jak povolit přepsání ResolveComponentForRenderMode (dotnet/aspnetcore #51235).

Další informace najdete v tématu Vytvoření .NET MAUIBlazor Hybrid aplikace s webovou Blazor aplikací.

Optimalizace doručování statických prostředků

MapStaticAssets je nový middleware, který pomáhá optimalizovat doručování statických prostředků v libovolné aplikaci ASP.NET Core, včetně Blazor aplikací.

Další informace najdete v některém z následujících zdrojů informací:

Zjišťování umístění vykreslování, interaktivity a přiřazeného režimu vykreslování za běhu

Zavedli jsme nové rozhraní API navržené tak, aby zjednodušilo proces dotazování stavů komponent za běhu. Toto rozhraní API poskytuje následující možnosti:

  • Určete aktuální umístění spuštění komponenty: To může být zvlášť užitečné pro ladění a optimalizaci výkonu komponent.
  • Zkontrolujte, jestli je komponenta spuštěná v interaktivním prostředí: To může být užitečné pro komponenty, které mají různá chování na základě interaktivity jejich prostředí.
  • Načtení přiřazeného režimu vykreslování pro komponentu: Pochopení režimu vykreslování může pomoct optimalizovat proces vykreslování a zlepšit celkový výkon komponenty.

Další informace najdete v tématu ASP.NET režimy vykreslování coreBlazor.

Vylepšené možnosti opětovného připojení na straně serveru:

Ve výchozím prostředí pro opětovné připojení na straně serveru jsme provedli následující vylepšení:

  • Když se uživatel vrátí do aplikace s odpojeným okruhem, pokusí se o opětovné připojení okamžitě, a nečeká na dobu trvání dalšího intervalu opětovného připojení. Tím se zlepší uživatelské prostředí při navigaci do aplikace na kartě prohlížeče, která přešla do režimu spánku.

  • Když pokus o opětovné připojení dosáhne serveru, ale server už okruh vydal, dojde k automatické aktualizaci stránky. Tím zabráníte uživateli v ruční aktualizaci stránky, pokud pravděpodobně dojde k úspěšnému opětovnému připojení.

  • Časování opětovného připojení využívá vypočítanou strategii zpětného odpojení. Ve výchozím nastavení dochází k prvním několika pokusům o opětovné připojení v rychlém sledu bez intervalu opakování, než se mezi pokusy zavádějí vypočítaná zpoždění. Chování intervalu opakování můžete přizpůsobit zadáním funkce pro výpočet intervalu opakování, jak ukazuje následující příklad exponenciálního opakování:

    Blazor.start({
      circuit: {
        reconnectionOptions: {
          retryIntervalMilliseconds: (previousAttempts, maxRetries) => 
            previousAttempts >= maxRetries ? null : previousAttempts * 1000
        },
      },
    });
    
  • Styl výchozího uživatelského rozhraní pro opětovné připojení byl modernizován.

Další informace najdete v pokynech k ASP.NET CoreBlazorSignalR.

Zjednodušená serializace stavu ověřování pro Blazor Web Apps

Nová rozhraní API usnadňují přidávání ověřování do existující Blazor webové aplikace. Když vytvoříte novou Blazor webovou aplikaci s ověřováním pomocí jednotlivých účtů a povolíte interaktivitu založenou na WebAssembly, projekt zahrnuje vlastní AuthenticationStateProvider v serverových i klientských projektech.

Tito zprostředkovatelé přetékají stav ověřování uživatele do prohlížeče. Ověřování na serveru místo klienta umožňuje aplikaci přístup ke stavu ověřování během předkončování a před inicializováním Blazor WebAssembly modulu runtime.

Vlastní AuthenticationStateProvider implementace používají službu Persistent Component State Service (PersistentComponentState) k serializaci stavu ověřování do komentářů HTML a čtení zpět z WebAssembly k vytvoření nové AuthenticationState instance.

To funguje dobře, pokud jste začali ze Blazor šablony projektu Webové aplikace a vybrali možnost Individuální účty , ale pokud se pokoušíte přidat ověřování do existujícího projektu, je to hodně kódu, který se má implementovat sami nebo zkopírovat. Teď jsou k dispozici rozhraní API, která jsou teď součástí Blazor šablony projektu Webové aplikace, která se dají volat na serverových a klientských projektech, aby se přidala tato funkce:

  • AddAuthenticationStateSerialization: Přidá potřebné služby pro serializaci stavu ověřování na serveru.
  • AddAuthenticationStateDeserialization: Přidá nezbytné služby pro deserializaci stavu ověřování v prohlížeči.

Ve výchozím nastavení rozhraní API serializuje pouze název na straně serveru a deklarace rolí pro přístup v prohlížeči. Možnost může být předána tak, aby AddAuthenticationStateSerialization zahrnovala všechny deklarace identity.

Další informace najdete v následujících částech článku **:

Přidání stránek vykreslování na straně statického serveru (SSR) do globálně interaktivní Blazor webové aplikace

S vydáním .NET 9 je teď jednodušší přidat statické stránky SSR do aplikací, které přijímají globální interaktivitu.

Tento přístup je užitečný jenom v případě, že aplikace obsahuje konkrétní stránky, které nemůžou pracovat s interaktivním vykreslováním serveru nebo webAssembly. Můžete například použít tento přístup pro stránky, které jsou závislé na čtení a zápisu HTTP cookiea můžou pracovat pouze v cyklu požadavků a odpovědí místo interaktivního vykreslování. U stránek, které pracují s interaktivním vykreslováním, byste je neměli vynutit, aby používaly statické vykreslování SSR, protože je pro koncového uživatele méně efektivní a méně responzivní.

Označte libovolnou Razor stránku komponenty s novým [ExcludeFromInteractiveRouting] atributem přiřazeným direktivou @attributeRazor :

@attribute [ExcludeFromInteractiveRouting]

Použití atributu způsobí, že navigace na stránce se ukončí z interaktivního směrování. Příchozí navigace je nucena provést opětovné načtení celé stránky místo toho, aby se stránka přeložila prostřednictvím interaktivního směrování. Opětovné načtení celé stránky vynutí opětovné načtení kořenovou komponentu nejvyšší úrovně, obvykle komponentu (App.razorkomponentuApp), aby se znovu vyrovnala ze serveru, což aplikaci umožňuje přepnout do jiného režimu vykreslování nejvyšší úrovně.

Metoda HttpContext.AcceptsInteractiveRouting rozšíření umožňuje komponentě zjistit, zda [ExcludeFromInteractiveRouting] je použita na aktuální stránku.

V komponentě App použijte vzor v následujícím příkladu:

  • Stránky, které nejsou označené [ExcludeFromInteractiveRouting] jako výchozí režim InteractiveServer vykreslování s globální interaktivitou. Můžete nahradit InteractiveServer InteractiveWebAssembly nebo InteractiveAuto zadat jiný výchozí globální režim vykreslování.
  • Stránky s poznámkami o [ExcludeFromInteractiveRouting] přijetí statického SSR (PageRenderMode je přiřazeno null).
<!DOCTYPE html>
<html>
<head>
    ...
    <HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
    <Routes @rendermode="@PageRenderMode" />
    ...
</body>
</html>

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

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

Alternativou k použití HttpContext.AcceptsInteractiveRouting metody rozšíření je ruční čtení metadat koncového bodu pomocí HttpContext.GetEndpoint()?.Metadata.

Tato funkce se zabývá referenční dokumentací v režimech vykreslování ASP.NET CoreBlazor.

Injektáž konstruktoru

Razor komponenty podporují injektáž konstruktoru.

V následujícím příkladu částečná třída (za kódem) vloží NavigationManager službu pomocí primárního konstruktoru:

public partial class ConstructorInjection(NavigationManager navigation)
{
    protected NavigationManager Navigation { get; } = navigation;
}

Další informace najdete v tématu ASP.NET injektáž závislostí jádraBlazor.

Komprese protokolu Websocket pro komponenty interaktivního serveru

Komponenty interaktivního serveru ve výchozím nastavení umožňují kompresi připojení WebSocket a nastavují 'self'direktivu frame-ancestors Content Security Policy (CSP), která povoluje pouze vkládání aplikace do <iframe> zdroje, ze kterého se aplikace obsluhuje při povolení komprese nebo při poskytnutí konfigurace kontextu Protokolu WebSocket.

Kompresi je možné zakázat nastavením na nullhodnotu ConfigureWebSocketOptions , která snižuje ohrožení zabezpečení aplikace na útok, ale může vést ke snížení výkonu:

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

Nakonfigurujte přísnější frame-ancestors CSP s hodnotou 'none' (vyžaduje se jednoduché uvozovky), která umožňuje kompresi Protokolu WebSocket, ale brání prohlížečům v vložení aplikace do libovolné <iframe>:

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

Další informace naleznete v následujících zdrojích:

Zpracování událostí složení klávesnice v Blazor

Nová KeyboardEventArgs.IsComposing vlastnost označuje, jestli je událost klávesnice součástí relace složení. Sledování stavu složení událostí klávesnice je zásadní pro zpracování mezinárodních metod zadávání znaků.

Přidání parametru OverscanCount do QuickGrid

Komponenta QuickGrid nyní zveřejňuje OverscanCount vlastnost, která určuje, kolik dalších řádků se vykresluje před a za viditelnou oblastí, když je povolená virtualizace.

Výchozí hodnota OverscanCount je 3. Následující příklad zvýší OverscanCount na 4:

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

SignalR

Tato část popisuje nové funkce pro SignalR.

Podpora polymorfního typu ve SignalR službě Hubs

Metody centra teď můžou místo odvozené třídy přijmout základní třídu, aby bylo možné polymorfní scénáře. Základní typ musí být opatřen poznámkami, aby bylo možné polymorfismus.

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; }
}

Vylepšené aktivity pro SignalR

SignalR Nyní má název ActivitySource Microsoft.AspNetCore.SignalR.Server , který generuje události pro volání metody centra:

  • Každá metoda je vlastní aktivita, takže vše, co generuje aktivitu během volání metody centra, je pod aktivitou metody centra.
  • Aktivity metody centra nemají nadřazený objekt. To znamená, že nejsou součástí dlouhotrvajícího SignalR připojení.

Následující příklad používá řídicí panel .NET Aspire a balíčky OpenTelemetry :

<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" />

Do Program.cs souboru přidejte následující spouštěcí kód:

// 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());

Následuje příklad výstupu z řídicího panelu Aspire:

Seznam aktivit pro SignalR události volání metody centra

SignalR podporuje oříznutí a nativní AOT.

Pokračováním na cestě nativní AOT, která začala v .NET 8, jsme povolili podporu oříznutí a nativní kompilace předem (AOT) pro SignalR scénáře klienta i serveru. Nyní můžete využít výhody výkonu při použití nativní AOT v aplikacích, které používají SignalR pro webovou komunikaci v reálném čase.

Začínáme

Nainstalujte nejnovější sadu .NET 9 SDK.

Pomocí následujícího příkazu vytvořte řešení ze webapiaot šablony v příkazovém prostředí:

dotnet new webapiaot -o SignalRChatAOTExample

Program.cs Obsah souboru nahraďte následujícím SignalR kódem:

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);
    }
}

Předchozí příklad vytvoří nativní spustitelný soubor Windows 10 MB a spustitelný soubor Linuxu o velikosti 10,9 MB.

Omezení

  • JSV současné době se podporuje pouze protokol ON:
    • Jak je znázorněno v předchozím kódu, aplikace, které používají JSserializaci ON a Nativní AOT, musí používat System.Text.Json generátor zdrojů.
    • To se řídí stejným přístupem jako minimální rozhraní API.
  • SignalR Na serveru nejsou podporovány parametry metody centra typu IAsyncEnumerable<T> a ChannelReader<T> kde T je ValueType (tj. struct) . Použití těchto typů vede k výjimce za běhu při spuštění při vývoji a v publikované aplikaci. Další informace naleznete v tématu SignalR: Použití IAsyncEnumerable<T> a ChannelReader<T> s ValueTypes v nativní AOT (dotnet/aspnetcore #56179).
  • Nativní AOT (Native AOTPublishAot) nepodporuje rozbočovače silného typu. Použití center silného typu s nativní AOT způsobí upozornění během sestavování a publikování a výjimku za běhu. Použití rozbočovačů silného typu s ořezáváním (PublishedTrimmed) je podporováno.
  • Pouze Task, , ValueTaskTask<T>nebo ValueTask<T> jsou podporovány pro asynchronní návratové typy.

Minimální rozhraní API

Tato část popisuje nové funkce pro minimální rozhraní API.

Přidáno InternalServerError a InternalServerError<TValue> do TypedResults

Třída TypedResults je užitečným vozidlem pro vrácení odpovědí na stavový kód HTTP se silnými typy z minimálního rozhraní API. TypedResults nyní obsahuje metody a typy továrny pro vrácení odpovědí "500 Internal Server Error" z koncových bodů. Tady je příklad, který vrátí odpověď 500:

var app = WebApplication.Create();

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

app.Run();

Volání ProducesProblem a ProducesValidationProblem skupiny tras

Tyto ProducesProblem metody a ProducesValidationProblem metody rozšíření byly aktualizovány tak, aby podporovaly jejich použití ve skupinách tras. Tyto metody označují, že všechny koncové body ve skupině tras můžou vracet ProblemDetails nebo ValidationProblemDetails odpovídat za účelem metadat OpenAPI.

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

Tato část popisuje nové funkce pro OpenAPI.

Integrovaná podpora generování dokumentů OpenAPI

Specifikace OpenAPI je standard pro popis rozhraní HTTP API. Standard umožňuje vývojářům definovat tvar rozhraní API, která se dají připojit k generátorům klientů, generátorům serverů, testovacím nástrojům, dokumentaci a dalším funkcím. V .NET 9 Preview poskytuje ASP.NET Core integrovanou podporu pro generování dokumentů OpenAPI představujících rozhraní API založená na kontroleru nebo minimální rozhraní API prostřednictvím balíčku Microsoft.AspNetCore.OpenApi .

Následující zvýrazněná volání kódu:

  • AddOpenApi a zaregistrujte požadované závislosti do kontejneru DI aplikace.
  • MapOpenApi a zaregistrujte požadované koncové body OpenAPI v trasách aplikace.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Microsoft.AspNetCore.OpenApi Nainstalujte balíček do projektu pomocí následujícího příkazu:

dotnet add package Microsoft.AspNetCore.OpenApi --prerelease

Spusťte aplikaci a přejděte k openapi/v1.json zobrazení vygenerovaného dokumentu OpenAPI:

Dokument OpenAPI

Dokumenty OpenAPI lze také vygenerovat v době sestavení přidáním Microsoft.Extensions.ApiDescription.Server balíčku:

dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease

Do souboru projektu aplikace přidejte následující:

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

Spusťte a zkontrolujte dotnet build vygenerovaný JSsoubor ON v adresáři projektu.

Generování dokumentů OpenAPI v době sestavení

ASP.NET integrované generování dokumentů OpenAPI core poskytuje podporu pro různá přizpůsobení a možnosti. Poskytuje transformátory dokumentů a operací a umožňuje spravovat více dokumentů OpenAPI pro stejnou aplikaci.

Další informace o nových funkcích dokumentů OpenAPI pro ASP.NET Core najdete v nových dokumentech Microsoft.AspNetCore.OpenApi.

Vylepšení dokončování IntelliSense pro balíček OpenAPI

ASP.NET podpora OpenAPI core je teď přístupnější a uživatelsky přívětivější. Rozhraní API OpenAPI se dodávají jako nezávislý balíček odděleně od sdílené architektury. Doteď to znamenalo, že vývojáři neměli pohodlí funkcí dokončování kódu, jako je IntelliSense pro rozhraní OPENAPI API.

Při rozpoznávání této mezery jsme zavedli nového zprostředkovatele dokončování a opravy kódu. Tyto nástroje jsou navržené tak, aby usnadnily zjišťování a používání rozhraní OpenAPI API, což vývojářům usnadňuje integraci OpenAPI do svých projektů. Poskytovatel dokončování nabízí návrhy kódu v reálném čase, zatímco nástroj pro opravu kódu pomáhá opravit běžné chyby a zlepšit využití rozhraní API. Toto vylepšení je součástí našeho průběžného závazku ke zlepšení vývojářského prostředí a zjednodušení pracovních postupů souvisejících s rozhraním API.

Když uživatel zadá příkaz, ve kterém je k dispozici rozhraní API související s OpenAPI, poskytovatel dokončení zobrazí doporučení pro rozhraní API. Například na následujících snímcích obrazovky jsou dokončování pro AddOpenApi a MapOpenApi k dispozici, když uživatel zadává příkaz vyvolání pro podporovaný typ, například IEndpointConventionBuilder:

Dokončování OpenAPI

Když je dokončení přijato a Microsoft.AspNetCore.OpenApi balíček není nainstalován,fixer kódu poskytuje zástupce pro automatickou instalaci závislosti v projektu.

Automatická instalace balíčku

Podpora parametrů [Required] a vlastností a [DefaultValue] atributů

Pokud [Required] a [DefaultValue] atributy jsou použity u parametrů nebo vlastností v rámci komplexních typů, implementace OpenAPI je mapuje na required vlastnosti default v dokumentu OpenAPI přidruženém k parametru nebo schématu typu.

Například následující rozhraní API vytvoří doprovodné schéma pro 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"
	  }
	}
}

Podpora transformátorů schématu v dokumentech OpenAPI

Integrovaná podpora OpenAPI je nyní dodávána s podporou transformátorů schématu, které lze použít k úpravě schémat generovaných system.Text.Json a implementací OpenAPI. Podobně jako transformátory dokumentů a operací lze transformátory schématu zaregistrovat na objektu OpenApiOptions . Například následující ukázka kódu ukazuje použití transformátoru schématu k přidání příkladu do schématu typu.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.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; }
}

Vylepšení rozhraní API pro registraci transformátoru v Microsoft.AspNetCore.OpenApi

Transformátory OpenAPI podporují úpravu dokumentu OpenAPI, operací v rámci dokumentu nebo schémat přidružených k typům v rozhraní API. Rozhraní API pro registraci transformátorů v dokumentu OpenAPI poskytují řadu možností pro registraci transformátorů.

Dříve byly pro registraci transformátorů k dispozici následující rozhraní API:

OpenApiOptions UseTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions UseTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions UseTransformer<IOpenApiDocumentTransformer>()
OpenApiOptions UseSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task>)
OpenApiOptions UseOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task>)

Nová sada rozhraní API je následující:

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 podporuje oříznutí a nativní AOT.

Nová integrovaná podpora OpenAPI v ASP.NET Core teď podporuje také oříznutí a nativní AOT.

Začínáme

Vytvořte nový projekt webového rozhraní API ASP.NET Core (nativní AOT).

dotnet new webapiaot

Přidejte balíček Microsoft.AspNetCore.OpenAPI.

dotnet add package Microsoft.AspNetCore.OpenApi --prerelease

Pro tuto verzi Preview musíte také přidat nejnovější balíček Microsoft.OpenAPI, abyste se vyhnuli upozorněním na oříznutí.

dotnet add package Microsoft.OpenApi

Aktualizujte Program.cs , aby bylo možné generovat dokumenty OpenAPI.

+ builder.Services.AddOpenApi();

var app = builder.Build();

+ app.MapOpenApi();

Umožňuje publikovat aplikaci.

dotnet publish

Aplikace publikuje nativní AOT bez upozornění.

Podpora volání ProducesProblem a ProducesValidationProblem skupin tras

Metody rozšíření ProducesProblem a ProducesValidationProblem byly aktualizovány pro skupiny tras. Tyto metody lze použít k označení, že všechny koncové body ve skupině tras mohou vracet ProblemDetails nebo ValidationProblemDetails odpovídat pro účely metadat OpenAPI.

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);

Problem a ValidationProblem typy výsledků podporují výstavbu s IEnumerable<KeyValuePair<string, object?>> hodnotami

Před rozhraním .NET 9 vyžadovalo vytvoření typů výsledků Problém a ValidationProblem v minimálních rozhraních API, aby se inicializovaly errors vlastnosti extensions s implementací IDictionary<string, object?>. V této verzi tato rozhraní API pro vytváření podporují přetížení, která spotřebovávají IEnumerable<KeyValuePair<string, object?>>.

var app = WebApplication.Create();

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

Díky uživateli GitHubu joegoldman2 za tento příspěvek!

Ověřování a autorizace

Tato část popisuje nové funkce pro ověřování a autorizaci.

OpenIdConnectHandler přidává podporu pro nabízené žádosti o autorizaci (PAR)

Chtěli bychom poděkovat Joeu DeCockovi ze společnosti Duende Software za přidání nabízených žádostí o autorizaci (PAR) do ASP.NET Core OpenIdConnectHandler. Joe popsal pozadí a motivaci pro povolení PAR v návrhu rozhraní API následujícím způsobem:

Pushed Authorization Requests (PAR) je relativně nový standard OAuth, který zlepšuje zabezpečení toků OAuth a OIDC přesunutím parametrů autorizace z předního kanálu do zadního kanálu. To znamená, že přesunutím parametrů autorizace z adres URL přesměrování v prohlížeči nasměrujte počítač na volání HTTP na back-endu.

Tím zabráníte útočníkovi v prohlížeči:

  • Zobrazují se parametry autorizace, které by mohly vytékat piI.
  • Manipulace s těmito parametry, například útočník může změnit rozsah požadovaného přístupu.

Nasdílením ověřovacích parametrů se zachovají také krátké adresy URL požadavků. Při použití složitějších funkcí OAuth a OIDC, jako jsou požadavky na bohatou autorizaci, můžou být parametry autorizace velmi dlouhé. Adresy URL, které jsou dlouhé příčiny problémů v mnoha prohlížečích a síťových infrastrukturách.

Použití funkce PAR doporučuje pracovní skupina FAPI v rámci Nadace OpenID. Například profil zabezpečení FAPI2.0 vyžaduje použití funkce PAR. Tento profil zabezpečení používá řada skupin pracujících na otevřeném bankovnictví (především v Evropě), zdravotní péči a v jiných odvětvích s vysokými požadavky na zabezpečení.

Par je podporováno řadou zprostředkovatelů identity, včetně

Pro .NET 9 jsme se rozhodli ve výchozím nastavení povolit par, pokud dokument zjišťování zprostředkovatele identity inzeruje podporu par, protože by měl poskytovat rozšířené zabezpečení pro poskytovatele, kteří ho podporují. Dokument zjišťování zprostředkovatele identity se obvykle nachází na adrese .well-known/openid-configuration. Pokud to způsobí problémy, můžete par zakázat prostřednictvím OpenIdConnectOptions.PushedAuthorizationBehavior následujícím způsobem:

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;
    });

Pokud chcete zajistit, aby ověřování proběhlo úspěšně pouze v případě použití funkce PAR, použijte místo toho funkci PushedAuthorizationBehavior.Require . Tato změna také zavádí novou událost OnPushAuthorization pro OpenIdConnectEvents , která se dá použít k přizpůsobení žádosti o nabízenou autorizaci nebo k jeho ručnímu zpracování. Další podrobnosti najdete v návrhu rozhraní API.

Přizpůsobení parametrů OIDC a OAuth

Obslužné rutiny ověřování OAuth a OIDC teď mají AdditionalAuthorizationParameters možnost usnadnit přizpůsobení parametrů autorizační zprávy, které jsou obvykle součástí řetězce dotazu přesměrování. V .NET 8 a starších verzích to vyžaduje vlastní OnRedirectToIdentityProvider metodu zpětného volání nebo přepsání BuildChallengeUrl ve vlastní obslužné rutině. Tady je příklad kódu .NET 8:

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;
    };
});

Předchozí příklad je teď možné zjednodušit následujícím kódem:

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

Konfigurace HTTP.sys rozšířených příznaků ověřování

Teď můžete nakonfigurovat HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING příznaky a HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL HTTP.sys pomocí nových EnableKerberosCredentialCaching vlastností CaptureCredentials v HTTP.sys AuthenticationManager a optimalizovat způsob zpracování ověřování systému Windows. Příklad:

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

Různé

Následující části popisují různé nové funkce.

Nová HybridCache knihovna

Rozhraní HybridCache API přemísní některé mezery v existujících IDistributedCache rozhraních API a IMemoryCache rozhraních API. Přidává také nové funkce, například:

  • Ochrana "Stampede", aby se zabránilo paralelnímu načítání stejné práce.
  • Konfigurovatelná serializace

HybridCache je navržený tak, aby nahradil stávající IDistributedCache a použití, IMemoryCache a poskytuje jednoduché rozhraní API pro přidání nového kódu do mezipaměti. Poskytuje jednotné rozhraní API pro ukládání do mezipaměti v procesu i mimo proces.

Pokud chcete zjistit, jak HybridCache je rozhraní API zjednodušené, porovnejte ho s kódem, který používá IDistributedCache. Tady je příklad, jak vypadá použití IDistributedCache :

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)
    { /* ... */ }
}

To je hodně práce, abyste se dostali správně pokaždé, včetně věcí, jako je serializace. A ve scénáři neúspěšné mezipaměti můžete skončit s několika souběžnými vlákny, všechna získání chybějící mezipaměti, všechna načtení podkladových dat, veškerá serializace a odeslání dat do mezipaměti.

Abychom tento kód HybridCachezjednodušili a vylepšili, musíme nejprve přidat novou knihovnu Microsoft.Extensions.Caching.Hybrid:

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

HybridCache Zaregistrujte službu, jako byste zaregistrovali implementaciIDistributedCache:

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

Teď je možné většinu problémů s ukládáním do mezipaměti přesměrovat na HybridCache:

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

Poskytujeme konkrétní implementaci HybridCache abstraktní třídy prostřednictvím injektáže závislostí, ale je zamýšleno, aby vývojáři mohli poskytovat vlastní implementace rozhraní API. Implementace HybridCache se zabývá vším, co souvisí s ukládáním do mezipaměti, včetně zpracování souběžných operací. Token cancel zde představuje kombinované zrušení všech souběžných volajících – nejen zrušení volajícího vidíme (to znamená token).

Scénáře s vysokou propustností je možné dále optimalizovat pomocí TState modelu, aby nedocházelo k určitým režijním nákladům na zachycené proměnné a zpětná volání jednotlivých instancí:

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 používá nakonfigurovanou IDistributedCache implementaci ( pokud existuje) pro sekundární mezipaměť mimo proces, například pomocí Redis. Ale i bez IDistributedCache, HybridCache služba bude i nadále poskytovat mezipaměť v procesu a "razítko" ochranu.

Poznámka k opakovanému použití objektu

V typickém existujícím kódu, který používá IDistributedCache, každé načtení objektu z mezipaměti má za následek deserializaci. Toto chování znamená, že každý souběžný volající získá samostatnou instanci objektu, která nemůže komunikovat s jinými instancemi. Výsledkem je bezpečnost vláken, protože neexistuje riziko souběžných úprav stejné instance objektu.

Vzhledem k tomu, že se hodně HybridCache využití přizpůsobí existujícímu IDistributedCache kódu, zachová toto chování ve výchozím nastavení, HybridCache aby nedocházelo k chybám souběžnosti. Daný případ použití je však ze své podstaty bezpečný pro přístup z více vláken:

  • Pokud jsou typy uložené v mezipaměti neměnné.
  • Pokud kód neupraví.

V takových případech informujte HybridCache , že je bezpečné opakovaně používat instance pomocí:

  • Označení typu jako sealed. Klíčové sealed slovo v jazyce C# znamená, že třídu nelze dědit.
  • Použití atributu [ImmutableObject(true)] na něj Atribut [ImmutableObject(true)] označuje, že stav objektu nelze po vytvoření změnit.

Opětovným použitím instancí HybridCache může snížit režii přidělení procesoru a objektů spojených s deserializací podle volání. To může vést ke zlepšení výkonu ve scénářích, kdy jsou objekty uložené v mezipaměti velké nebo často přístupné.

Další HybridCache funkce

Podobně jako IDistributedCachepodporuje HybridCache odebrání pomocí klíče pomocí RemoveKeyAsync metody.

HybridCache poskytuje také volitelná rozhraní API pro IDistributedCache implementace, aby se zabránilo byte[] přidělení. Tuto funkci implementují verze Microsoft.Extensions.Caching.StackExchangeRedis Preview a Microsoft.Extensions.Caching.SqlServer balíčky.

Serializace se konfiguruje jako součást registrace služby s podporou typově specifických a generalizovaných serializátorů prostřednictvím WithSerializer metod a .WithSerializerFactory metod zřetězených AddHybridCache z volání. Ve výchozím nastavení knihovna zpracovává string a byte[] interně a používá System.Text.Json se pro všechno ostatní, ale můžete použít protobuf, xml nebo cokoli jiného.

HybridCache podporuje starší moduly runtime .NET až .NET Framework 4.7.2 a .NET Standard 2.0.

Další informace o HybridCacheknihovně HybridCache v ASP.NET Core

Vylepšení stránky výjimek pro vývojáře

Stránka výjimky pro vývojáře ASP.NET Core se zobrazí, když aplikace během vývoje vyvolá neošetřenou výjimku. Stránka výjimky vývojáře obsahuje podrobné informace o výjimce a požadavku.

Verze Preview 3 přidala metadata koncového bodu na stránku výjimky vývojáře. ASP.NET Core používá metadata koncového bodu k řízení chování koncových bodů, jako je směrování, ukládání odpovědí do mezipaměti, omezování rychlosti, generování OpenAPI a další. Následující obrázek znázorňuje nové informace o metadatech v Routing části stránky výjimky vývojáře:

Nové informace o metadatech na stránce výjimky vývojáře

Při testování stránky výjimek pro vývojáře byly identifikovány malé vylepšení životního cyklu. Odeslali je ve verzi Preview 4:

  • Lepší obtékání textu. Dlouhé cookiehodnoty, hodnoty řetězce dotazu a názvy metod už nepřidají vodorovné posuvníky prohlížeče.
  • Větší text, který se nachází v moderních designech.
  • Konzistentnější velikosti tabulek

Následující animovaný obrázek znázorňuje novou stránku výjimky vývojáře:

Stránka s výjimkou nového vývojáře

Vylepšení ladění slovníku

Zobrazení ladění slovníků a dalších kolekcí klíč-hodnota má vylepšené rozložení. Klíč se místo zřetězení s hodnotou zobrazí ve sloupci klíče ladicího programu. Následující obrázky ukazují starý a nový displej slovníku v ladicím programu.

Před:

Předchozí prostředí ladicího programu

Po:

Nové prostředí ladicího programu

ASP.NET Core obsahuje mnoho kolekcí klíč-hodnota. Toto vylepšené možnosti ladění platí pro:

  • Záhlaví HTTP
  • Řetězce dotazů
  • Formuláře
  • Cookies
  • Zobrazit data
  • Směrování dat
  • Funkce

Oprava chyby 503 během recyklace aplikace ve službě IIS

Ve výchozím nastavení mezi oznámením služby IIS o recyklaci nebo vypnutí dojde k 1sekundovém zpoždění a když služba ANCM řekne spravovanému serveru, aby se spustilo vypnutí. Zpoždění je možné konfigurovat prostřednictvím ANCM_shutdownDelay proměnné prostředí nebo nastavením nastavení obslužné rutiny shutdownDelay . Obě hodnoty jsou v milisekundách. Zpoždění spočívá hlavně ve snížení pravděpodobnosti závodu, kdy:

  • Služba IIS nezačala začínat požadavky na řazení do fronty pro přechod do nové aplikace.
  • ANCM začne odmítat nové požadavky, které přicházejí do staré aplikace.

Pomalejší počítače nebo počítače s těžším využitím procesoru můžou chtít tuto hodnotu upravit, aby se snížila pravděpodobnost 503.

Příklad nastavení 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>

Oprava je v globálně nainstalovaném modulu ANCM, který pochází z hostitelské sady.

Optimalizace doručování statických webových prostředků

Dodržování osvědčených postupů v produkčním prostředí pro poskytování statických prostředků vyžaduje značné množství pracovních a technických odborných znalostí. Bez optimalizace, jako je komprese, ukládání do mezipaměti a otisky prstů:

  • Prohlížeč musí při každém načtení stránky vyhovět dalším požadavkům.
  • Více bajtů, než je potřeba, se přenáší přes síť.
  • Někdy se klientům obsluhují zastaralé verze souborů.

Vytváření výkonných webových aplikací vyžaduje optimalizaci doručování prostředků do prohlížeče. Mezi možné optimalizace patří:

  • Posílejte daný prostředek jednou, dokud se soubor nezmění nebo prohlížeč vymaže jeho mezipaměť. Nastavte záhlaví značky ETag .
  • Zabrání prohlížeči v používání starých nebo zastaralých prostředků po aktualizaci aplikace. Nastavte hlavičku Naposledy změněno .
  • Nastavte správné hlavičky ukládání do mezipaměti.
  • Použijte middleware pro ukládání do mezipaměti.
  • Pokud je to možné, obsluhujte komprimované verze prostředků.
  • Použijte CDN k poskytování prostředků blíže k uživateli.
  • Minimalizujte velikost prostředků obsluhovaných v prohlížeči. Tato optimalizace nezahrnuje minifikace.

MapStaticAssets je nový middleware, který pomáhá optimalizovat doručování statických prostředků v aplikaci. Je navržená tak, aby fungovala se všemi architekturami uživatelského rozhraní, včetně Blazor, Razor Pages a MVC. Obvykle se jedná o nahrazení příkazu drop-in pro 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 funguje tak, že zkombinuje procesy sestavení a publikování a shromažďuje informace o všech statických prostředcích v aplikaci. Tyto informace pak knihovna modulu runtime využívá k efektivnímu poskytování těchto souborů do prohlížeče.

MapStaticAssets může nahradit UseStaticFiles ve většině situací, ale je optimalizovaná pro poskytování prostředků, o kterých má aplikace znalosti při sestavování a publikování. Pokud aplikace obsluhuje prostředky z jiných umístění, jako jsou disky nebo vložené prostředky, UseStaticFiles by se měly použít.

MapStaticAssetsposkytuje následující výhody, které nebyly nalezeny:UseStaticFiles

  • Komprese času sestavení pro všechny prostředky v aplikaci:
    • gzip během vývoje a gzip + brotli během publikování.
    • Všechny prostředky jsou komprimovány s cílem zmenšit velikost prostředků na minimum.
  • Obsah založenýETags: Pro Etags každý prostředek jsou řetězec kódovaný kódem Base64 hodnoty hash SHA-256 obsahu. Tím se zajistí, že prohlížeč znovu načte soubor pouze v případě, že se změnil jeho obsah.

Následující tabulka ukazuje původní a komprimované velikosti šablon stylů CSS a JS souborů ve výchozí Razor šabloně Stránky:

Soubor Původní Komprimované % redukce
bootstrap.min.css 163 17.5 89.26%
jquery.js 89.6 28 68.75%
bootstrap.min.js 78,5 20 74.52%
Celkem 331.1 65.5 80.20%

Následující tabulka uvádí původní a komprimované velikosti pomocí knihovny komponent uživatelského rozhraní Blazor Fluent:

Soubor Původní Komprimované % redukce
fluent.js 384 73 80.99%
fluent.css 94 11 88.30%
Celkem 478 84 82.43%

Pro celkem 478 KB nekomprimované na 84 kB komprimované.

Následující tabulka ukazuje původní a komprimované velikosti pomocí knihovny komponent MudBlazorBlazor :

Soubor Původní Komprimované Redukce
BlátoBlazor.min.css 541 37.5 93.07%
BlátoBlazor.min.js 47.4 9.2 80.59%
Celkem 588.4 46,7 92.07%

Optimalizace probíhá automaticky při použití MapStaticAssets. Při přidání nebo aktualizaci knihovny, například pomocí nového JavaScriptu nebo šablon stylů CSS, se prostředky optimalizují jako součást sestavení. Optimalizace je zvlášť přínosná pro mobilní prostředí, která můžou mít nižší šířku pásma nebo nespolehlivé připojení.

Další informace o nových funkcích doručování souborů najdete v následujících zdrojích informací:

Povolení dynamické komprese na serveru vs. MapStaticAssets

MapStaticAssets má následující výhody oproti dynamické kompresi na serveru:

  • Je jednodušší, protože neexistuje žádná konfigurace specifická pro server.
  • Je výkonnější, protože prostředky se komprimují v době sestavení.
  • Umožňuje vývojáři strávit během procesu sestavení více času, aby se zajistilo, že prostředky mají minimální velikost.

Podívejte se na následující tabulku, která porovnává kompresi MudBlazor s dynamickou kompresí služby IIS a MapStaticAssets:

IIS gzip MapStaticAssets Redukce MapStaticAssets
≅ 90 37.5 59%

ASP0026: Analyzátor, který upozorní, když je [Authorize] přepsán uživatelem [AllowAnonymous] z "daleko"

Zdá se intuitivní, že [Authorize] atribut umístěný "blíž" k akci MVC než [AllowAnonymous] atribut by přepsal [AllowAnonymous] atribut a vynutit autorizaci. To ale nemusí být nutně případ. Záleží na relativním pořadí atributů.

Následující kód ukazuje příklady, kde se blíže [Authorize] atribut přepíše atributem [AllowAnonymous] , který je vzdálenější.

[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
{
}

V .NET 9 Preview 6 jsme představili analyzátor, který zvýrazní instance, jako jsou ty, kdy se blíže [Authorize] atribut přepíše atributem [AllowAnonymous] , který je daleko od akce MVC. Upozornění odkazuje na přepsaný [Authorize] atribut s následující zprávou:

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

Správná akce, která se má provést, pokud se zobrazí toto upozornění, závisí na záměru atributů. Pokud se koncový bod nechtěně vystavuje anonymním uživatelům, měl by být atribut odsud [AllowAnonymous] odebrán. [AllowAnonymous] Pokud byl atribut určen k přepsání bližšího [Authorize] atributu, můžete atribut opakovat [AllowAnonymous] za [Authorize] atributem a objasnit záměr.

[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;
}

Vylepšené Kestrel metriky připojení

Výrazně jsme vylepšili Kestrelmetriky připojení zahrnutím metadat o tom, proč připojení selhalo. Metrika kestrel.connection.duration teď obsahuje důvod uzavření připojení v atributu error.type .

Tady je malý vzorek error.type hodnot:

  • tls_handshake_failed – Připojení vyžaduje protokol TLS a metoda handshake protokolu TLS se nezdařila.
  • connection_reset – Během probíhajících požadavků klient připojení neočekávaně zavřel.
  • request_headers_timeout – Kestrel připojení zavřelo, protože v čase neobdrželo hlavičky požadavků.
  • max_request_body_size_exceeded - Kestrel zavřel připojení, protože nahraná data překročila maximální velikost.

Kestrel Diagnostika problémů s připojením dříve vyžadovala, aby server zaznamenával podrobné protokolování nízké úrovně. Generování a ukládání protokolů ale může být nákladné a může být obtížné najít správné informace mezi šumem.

Metriky jsou mnohem levnější alternativou, která může zůstat v produkčním prostředí s minimálním dopadem. Shromážděné metriky můžou řídit řídicí panely a výstrahy. Jakmile je problém identifikován na vysoké úrovni s metrikami, může začít další šetření pomocí protokolování a dalších nástrojů.

Očekáváme, že lepší metriky připojení budou užitečné v mnoha scénářích:

  • Zkoumání problémů s výkonem způsobených krátkou životností připojení
  • Dochází k probíhajícím externím útokům, které mají vliv na Kestrel výkon a stabilitu.
  • Zaznamenávání pokusů o externí útoky na Kestrel Kestrelintegrované posílení zabezpečení, kterým se zabránilo.

Další informace najdete v tématu ASP.NET základní metriky.

Přizpůsobení Kestrel koncových bodů pojmenovaného kanálu

KestrelPodpora pojmenovaných kanálu byla vylepšena s pokročilými možnostmi přizpůsobení. Nová CreateNamedPipeServerStream metoda na možnostech pojmenovaného kanálu umožňuje přizpůsobit kanály podle koncového bodu.

Příkladem, kde je to užitečné, je Kestrel aplikace, která vyžaduje dva koncové body kanálu s odlišným zabezpečením přístupu. Možnost CreateNamedPipeServerStream lze použít k vytváření kanálů s vlastním nastavením zabezpečení v závislosti na názvu kanálu.

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 možnost zvolit stavový kód na základě typu výjimky

Nová možnost při konfiguraci ExceptionHandlerMiddleware aplikací umožňuje vývojářům aplikací zvolit, jaký stavový kód se má vrátit, když během zpracování žádosti dojde k výjimce. Nová možnost změní stavový kód, který je nastaven v ProblemDetails odpovědi z ExceptionHandlerMiddleware.

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

Odhlášení z metrik HTTP u určitých koncových bodů a požadavků

.NET 9 zavádí možnost vyjádřit výslovný nesouhlas s metrikami HTTP pro konkrétní koncové body a požadavky. Odhlášení z zaznamenávání metrik je výhodné pro koncové body často volané automatizovanými systémy, jako jsou kontroly stavu. Zaznamenávání metrik pro tyto požadavky je obecně zbytečné.

Požadavky HTTP na koncový bod je možné vyloučit z metrik přidáním metadat. Buď:

  • [DisableHttpMetrics] Přidejte atribut do kontroleru webového rozhraní API, SignalR centra nebo služby gRPC.
  • Volání DisableHttpMetrics při mapování koncových bodů při spuštění aplikace:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();

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

Vlastnost MetricsDisabled byla přidána pro IHttpMetricsTagsFeature :

  • Pokročilé scénáře, kdy se požadavek nemapuje na koncový bod.
  • Dynamické zakazování shromažďování metrik pro konkrétní požadavky HTTP
// 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);
});

Podpora ochrany dat pro odstranění klíčů

Před rozhraním .NET 9 nebyly klíče ochrany dat záměrně znemožněny, aby se zabránilo ztrátě dat. Odstranění klíče vykreslí chráněná data nenapravitelným. Vzhledem k jejich malé velikosti, akumulace těchto klíčů obecně představovala minimální dopad. Abychom však vyhověli extrémně dlouhotrvajícím službám, zavedli jsme možnost odstranit klíče. Obecně platí, že by se měly odstranit jenom staré klíče. Odstraňte klíče pouze v případě, že můžete přijmout riziko ztráty dat výměnou za úspory úložiště. Doporučujeme, aby klíče ochrany dat nebyly odstraněny.

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.");
}