Co nowego w programie ASP.NET Core 9.0
W tym artykule przedstawiono najważniejsze zmiany w programie ASP.NET Core 9.0 z linkami do odpowiedniej dokumentacji.
Ten artykuł został zaktualizowany dla programu .NET 9 Release Candidate 2.
Blazor
W tej sekcji opisano nowe funkcje programu Blazor.
.NET MAUIBlazor Hybrid szablon rozwiązania i aplikacja internetowa
Nowy szablon rozwiązania ułatwia tworzenie .NET MAUI natywnych i Blazor internetowych aplikacji klienckich korzystających z tego samego interfejsu użytkownika. W tym szablonie pokazano, jak tworzyć aplikacje klienckie, które maksymalizują ponowne użycie kodu oraz docelowe systemy Android, iOS, Mac, Windows i Web.
Najważniejsze funkcje tego szablonu obejmują:
- Możliwość wybrania interaktywnego Blazor trybu renderowania dla aplikacji internetowej.
- Automatyczne tworzenie odpowiednich projektów, w tym Blazor Web App globalne renderowanie interakcyjne i .NET MAUIBlazor Hybrid aplikacja.
- Utworzone projekty używają biblioteki klas udostępnionych Razor (RCL) do obsługi składników interfejsu Razor użytkownika.
- Dołączono przykładowy kod, który pokazuje, jak używać wstrzykiwania zależności w celu zapewnienia różnych implementacji interfejsu dla Blazor Hybrid aplikacji i .Blazor Web App
Aby rozpocząć, zainstaluj zestaw SDK platformy .NET 9 i zainstaluj .NET MAUI obciążenie, które zawiera szablon:
dotnet workload install maui
Utwórz rozwiązanie na podstawie szablonu projektu w powłoce poleceń przy użyciu następującego polecenia:
dotnet new maui-blazor-web
Szablon jest również dostępny w programie Visual Studio.
Uwaga
Obecnie wyjątek występuje, jeśli Blazor tryby renderowania są definiowane na poziomie poszczególnych stron/składników. Aby uzyskać więcej informacji, zobacz BlazorWebView wymaga sposobu włączenia zastępowania ResolveComponentForRenderMode (dotnet/aspnetcore
#51235).
Aby uzyskać więcej informacji, zobacz Tworzenie .NET MAUIBlazor Hybrid aplikacji przy użyciu elementu Blazor Web App.
Optymalizacja dostarczania zasobów statycznych
MapStaticAssets
to nowe oprogramowanie pośredniczące, które pomaga zoptymalizować dostarczanie zasobów statycznych w dowolnej aplikacji ASP.NET Core, w tym Blazor aplikacji.
Aby uzyskać więcej informacji, zobacz jeden z następujących zasobów:
- Sekcja Optymalizowanie statycznego dostarczania zasobów internetowych w tym artykule.
- ASP.NET Core Blazor plików statycznych.
Wykrywanie lokalizacji renderowania, interakcyjności i przypisanego trybu renderowania w czasie wykonywania
Wprowadziliśmy nowy interfejs API zaprojektowany w celu uproszczenia procesu wykonywania zapytań dotyczących stanów składników w czasie wykonywania. Ten interfejs API zapewnia następujące możliwości:
- Określ bieżącą lokalizację wykonywania składnika: może to być szczególnie przydatne podczas debugowania i optymalizowania wydajności składników.
- Sprawdź, czy składnik jest uruchomiony w środowisku interaktywnym: może to być przydatne w przypadku składników, które mają różne zachowania w oparciu o interakcyjność ich środowiska.
- Pobieranie przypisanego trybu renderowania dla składnika: Zrozumienie trybu renderowania może pomóc w optymalizacji procesu renderowania i poprawie ogólnej wydajności składnika.
Aby uzyskać więcej informacji, zobacz tryby renderowania ASP.NET CoreBlazor.
Ulepszone środowisko ponownego łączenia po stronie serwera:
Następujące ulepszenia zostały wprowadzone do domyślnego środowiska ponownego nawiązywania połączenia po stronie serwera:
Gdy użytkownik przechodzi z powrotem do aplikacji z odłączonym obwodem, następuje natychmiastowe ponowne nawiązanie połączenia, a nie oczekiwanie na czas trwania następnego interwału ponownego połączenia. Poprawia to środowisko użytkownika podczas przechodzenia do aplikacji na karcie przeglądarki, która przeszła w stan uśpienia.
Gdy próba ponownego nawiązania połączenia osiągnie serwer, ale serwer już zwolnił obwód, nastąpi automatyczne odświeżenie strony. Uniemożliwia to użytkownikowi ręczne odświeżenie strony, jeśli prawdopodobnie spowoduje to pomyślne ponowne nawiązanie połączenia.
Czas ponownego nawiązywania połączenia używa obliczonej strategii wycofywania. Domyślnie pierwsze kilka ponownych prób nawiązania połączenia odbywa się w krótkim odstępie czasu bez interwału ponawiania prób przed wprowadzeniem obliczonych opóźnień między próbami. Zachowanie interwału ponawiania prób można dostosować, określając funkcję w celu obliczenia interwału ponawiania, jak pokazano w poniższym przykładzie wykładniczego wycofywania:
Blazor.start({ circuit: { reconnectionOptions: { retryIntervalMilliseconds: (previousAttempts, maxRetries) => previousAttempts >= maxRetries ? null : previousAttempts * 1000 }, }, });
Styl domyślnego interfejsu użytkownika ponownego łączenia został zmodernizowany.
Aby uzyskać więcej informacji, zobacz wskazówki dotyczące platformy ASP.NET CoreBlazorSignalR.
Uproszczona serializacji stanu uwierzytelniania dla s Blazor Web App
Nowe interfejsy API ułatwiają dodawanie uwierzytelniania do istniejącego Blazor Web Appelementu . Po utworzeniu nowego Blazor Web App z uwierzytelnianiem przy użyciu indywidualnych kont i włączeniu interakcyjności opartej na zestawie WebAssembly projekt zawiera niestandardowy AuthenticationStateProvider zarówno w projektach serwera, jak i klienta.
Ci dostawcy przepływają stan uwierzytelniania użytkownika do przeglądarki. Uwierzytelnianie na serwerze zamiast klienta umożliwia aplikacji dostęp do stanu uwierzytelniania podczas prerenderingu i przed zainicjowanym środowiskiem uruchomieniowym zestawu WebAssembly platformy .NET.
Implementacje niestandardowe AuthenticationStateProvider używają usługi stanu trwałego składnika (PersistentComponentState), aby serializować stan uwierzytelniania w komentarzach HTML i odczytywać go z powrotem z zestawu WebAssembly w celu utworzenia nowego AuthenticationState wystąpienia.
Działa to dobrze, jeśli rozpoczęto pracę z szablonu Blazor Web App projektu i wybrano opcję Indywidualne konta , ale jest to dużo kodu implementowania siebie lub kopiowania, jeśli próbujesz dodać uwierzytelnianie do istniejącego projektu. Teraz istnieją interfejsy API, które są teraz częścią Blazor Web App szablonu projektu, które można wywoływać w projektach serwera i klienta, aby dodać tę funkcję:
AddAuthenticationStateSerialization
: dodaje niezbędne usługi do serializacji stanu uwierzytelniania na serwerze.AddAuthenticationStateDeserialization
: dodaje niezbędne usługi do deserializacji stanu uwierzytelniania w przeglądarce.
Domyślnie interfejs API serializuje tylko nazwę po stronie serwera i oświadczenia roli w celu uzyskania dostępu w przeglądarce. Można przekazać opcję w celu AddAuthenticationStateSerialization
uwzględnienia wszystkich oświadczeń.
Aby uzyskać więcej informacji, zobacz następujące sekcje bezpiecznego ASP.NET Core aplikacji po stronie Blazor serwera:
- BlazorIdentity Interfejs użytkownika (indywidualne konta)
- Zarządzanie stanem uwierzytelniania w s Blazor Web App
Dodawanie statycznych stron renderowania po stronie serwera (SSR) do strony globalnie interakcyjnej Blazor Web App
W wersji platformy .NET 9 jest teraz prostsze dodawanie statycznych stron SSR do aplikacji, które przyjmują globalną interakcyjność.
Takie podejście jest przydatne tylko wtedy, gdy aplikacja ma określone strony, które nie mogą pracować z interaktywnym serwerem lub renderowaniem zestawu WebAssembly. Na przykład zastosuj to podejście dla stron, które zależą od odczytywania/zapisywania plików cookie HTTP i mogą działać tylko w cyklu żądania/odpowiedzi zamiast renderowania interakcyjnego. W przypadku stron, które działają z renderowaniem interaktywnym, nie należy wymuszać używania statycznego renderowania SSR, ponieważ jest mniej wydajne i mniej elastyczne dla użytkownika końcowego.
Oznacz dowolną Razor stronę składnika przy użyciu nowego [ExcludeFromInteractiveRouting]
atrybutu przypisanego @attribute
Razor do dyrektywy:
@attribute [ExcludeFromInteractiveRouting]
Zastosowanie atrybutu powoduje przejście do strony w celu zakończenia routingu interakcyjnego. Nawigacja przychodząca jest zmuszona do ponownego załadowania pełnej strony zamiast tego rozpoznawania strony za pośrednictwem routingu interakcyjnego. Ponowne ładowanie pełnej strony wymusza składnik główny najwyższego poziomu, zazwyczaj App
składnik (App.razor
), aby rerender z serwera, dzięki czemu aplikacja może przełączyć się do innego trybu renderowania najwyższego poziomu.
HttpContext.AcceptsInteractiveRouting
Metoda rozszerzenia umożliwia składnikowi wykrywanie, czy [ExcludeFromInteractiveRouting]
jest stosowane do bieżącej strony.
W składniku App
użyj wzorca w poniższym przykładzie:
- Strony, które nie są oznaczone adnotacją domyślną
[ExcludeFromInteractiveRouting]
do trybu renderowaniaInteractiveServer
z globalną interakcyjnością. Możesz zastąpićInteractiveServer
ciąg ciągiemInteractiveWebAssembly
lubInteractiveAuto
określić inny domyślny tryb renderowania globalnego. - Strony oznaczone adnotacjami przy
[ExcludeFromInteractiveRouting]
użyciu statycznego przewodnika SSR (PageRenderMode
jest przypisywane ).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;
}
Alternatywą dla metody HttpContext.AcceptsInteractiveRouting
rozszerzenia jest ręczne odczytywanie metadanych punktu końcowego przy użyciu polecenia HttpContext.GetEndpoint()?.Metadata
.
Ta funkcja jest objęta dokumentacją referencyjną w trybach renderowania ASP.NET CoreBlazor.
Wstrzykiwanie konstruktora
Razor składniki obsługują wstrzykiwanie konstruktora.
W poniższym przykładzie klasa częściowa (code-behind) wprowadza usługę NavigationManager
przy użyciu podstawowego konstruktora:
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Aby uzyskać więcej informacji, zobacz ASP.NET Core dependency injection (Wstrzykiwanie zależności ASP.NET CoreBlazor).
Kompresja protokołu Websocket dla składników interactive server
Domyślnie składniki interaktywnego serwera umożliwiają kompresję dla połączeń protokołu WebSocket i ustawiają frame-ancestors
dyrektywę zasad zabezpieczeń zawartości (CSP) ustawioną na 'self'
, która zezwala tylko na osadzanie aplikacji w miejscu <iframe>
pochodzenia, z którego aplikacja jest obsługiwana, gdy kompresja jest włączona lub gdy jest udostępniana konfiguracja kontekstu protokołu WebSocket.
Kompresję można wyłączyć, ustawiając wartość ConfigureWebSocketOptions
null
, co zmniejsza lukę w zabezpieczeniach aplikacji do ataku , ale może spowodować zmniejszenie wydajności:
.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)
Skonfiguruj bardziej frame-ancestors
rygorystyczny dostawca CSP z wartością 'none'
(wymagany pojedynczy cudzysłów), który umożliwia kompresję protokołu WebSocket, ale uniemożliwia przeglądarkom osadzanie aplikacji w dowolnym elemencie <iframe>
:
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Aby uzyskać więcej informacji, zobacz następujące zasoby:
- wskazówki dotyczące platformy ASP.NET Core BlazorSignalR
- Wskazówki dotyczące ograniczania zagrożeń na potrzeby interaktywnego renderowania po stronie serwera ASP.NET Core Blazor
Obsługa zdarzeń kompozycji klawiatury w programie Blazor
Nowa KeyboardEventArgs.IsComposing
właściwość wskazuje, czy zdarzenie klawiatury jest częścią sesji kompozycji. Śledzenie stanu kompozycji zdarzeń klawiatury ma kluczowe znaczenie dla obsługi metod wprowadzania znaków międzynarodowych.
Dodano parametr OverscanCount
do elementu QuickGrid
Składnik QuickGrid
uwidacznia teraz właściwość określającą OverscanCount
, ile dodatkowych wierszy jest renderowanych przed i po widocznym regionie po włączeniu wirtualizacji.
Wartość domyślna OverscanCount
to 3. Poniższy przykład zwiększa OverscanCount
wartość do 4:
<QuickGrid ItemsProvider="itemsProvider" Virtualize="true" OverscanCount="4">
...
</QuickGrid>
InputNumber
składnik obsługuje type="range"
atrybut
Składnik InputNumber<TValue> obsługuje type="range"
teraz atrybut, który tworzy dane wejściowe zakresu obsługujące powiązanie modelu i walidację formularza, zazwyczaj renderowane jako suwak lub kontrolka wybierania numerów, a nie pole tekstowe:
<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; }
}
}
Wiele Blazor Web Apps na projekt serwera
Obsługa wielu Blazor Web Apps na projekt serwera zostanie uwzględniona w przypadku platformy .NET 10 (listopad 2025).
Aby uzyskać więcej informacji, zobacz Obsługa wielu Blazor aplikacji internetowych na projekt serwera (dotnet/aspnetcore
#52216).
SignalR
W tej sekcji opisano nowe funkcje programu SignalR.
Obsługa typu polimorficznego w centrach SignalR
Metody koncentratora mogą teraz akceptować klasę bazową zamiast klasy pochodnej w celu włączenia scenariuszy polimorficznych. Typ podstawowy musi być adnotacjami, aby umożliwić polimorfizm.
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; }
}
Ulepszone działania dla SignalR
SignalR Element ActivitySource ma teraz nazwę Microsoft.AspNetCore.SignalR.Server
ActivitySource, która emituje zdarzenia dla wywołań metody centrum:
- Każda metoda jest własną aktywnością, więc wszystko, co emituje działanie podczas wywołania metody centrum, jest w ramach działania metody centrum.
- Działania metody centrum nie mają elementu nadrzędnego. Oznacza to, że nie są one połączone w ramach długotrwałego SignalR połączenia.
W poniższym przykładzie użyto pulpitu nawigacyjnego .NET Aspire i pakietów 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" />
Dodaj następujący kod startowy Program.cs
do pliku:
// 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());
Poniżej przedstawiono przykładowe dane wyjściowe z pulpitu nawigacyjnego Aspire:
SignalR obsługuje przycinanie i natywną funkcję AOT
Kontynuowanie natywnej podróży AOT rozpoczętej na platformie .NET 8 umożliwiło przycinanie i natywną obsługę kompilacji przed czasem (AOT) zarówno SignalR dla scenariuszy klienta, jak i serwera. Teraz możesz skorzystać z zalet wydajności korzystania z natywnej funkcji AOT w aplikacjach, które korzystają z SignalR komunikacji internetowej w czasie rzeczywistym.
Wprowadzenie
Zainstaluj najnowszy zestaw .NET 9 SDK.
Utwórz rozwiązanie na podstawie szablonu webapiaot
w powłoce poleceń przy użyciu następującego polecenia:
dotnet new webapiaot -o SignalRChatAOTExample
Zastąp zawartość Program.cs
pliku następującym SignalR kodem:
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);
}
}
W poprzednim przykładzie jest generowany natywny plik wykonywalny systemu Windows o wartości 10 MB i plik wykonywalny systemu Linux o wartości 10,9 MB.
Ograniczenia
- Obecnie obsługiwany jest tylko protokół JSON:
- Jak pokazano w poprzednim kodzie, aplikacje korzystające z serializacji JSON i Natywna funkcja AOT muszą używać generatora
System.Text.Json
źródła. - Jest to zgodne z tym samym podejściem co minimalne interfejsy API.
- Jak pokazano w poprzednim kodzie, aplikacje korzystające z serializacji JSON i Natywna funkcja AOT muszą używać generatora
- SignalR Na serwerze parametry metody centrum typu
IAsyncEnumerable<T>
iChannelReader<T>
gdzieT
jest valueType (struct
) nie są obsługiwane. Użycie tych typów powoduje wyjątek środowiska uruchomieniowego podczas uruchamiania podczas tworzenia i w opublikowanej aplikacji. Aby uzyskać więcej informacji, zobacz : Using IAsyncEnumerable<T and ChannelReader<T>> with ValueTypes in native AOT (dotnet/aspnetcore
#56179).SignalR - Silnie typizowane koncentratory nie są obsługiwane w przypadku natywnej AOT (
PublishAot
). Używanie silnie typiowanych centrów z natywną funkcją AOT spowoduje ostrzeżenia podczas kompilacji i publikowania oraz wyjątek środowiska uruchomieniowego. Używanie silnie typiowanych koncentratorów z przycinaniem (PublishedTrimmed
) jest obsługiwane. - Tylko
Task
,Task<T>
,ValueTask
lubValueTask<T>
są obsługiwane w przypadku typów zwracanych asynchronicznych.
Minimalne interfejsy API
W tej sekcji opisano nowe funkcje dla minimalnych interfejsów API.
Dodano InternalServerError
polecenie i InternalServerError<TValue>
do TypedResults
Klasa TypedResults jest przydatnym pojazdem do zwracania silnie typiowanych odpowiedzi opartych na kodzie stanu HTTP z minimalnego interfejsu API. TypedResults
Teraz zawiera metody fabryczne i typy zwracania odpowiedzi "500 Wewnętrzny błąd serwera" z punktów końcowych. Oto przykład, który zwraca odpowiedź 500:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Wywoływanie ProducesProblem
i ProducesValidationProblem
w grupach tras
Metody ProducesProblem
i ProducesValidationProblem
rozszerzenia zostały zaktualizowane w celu obsługi ich użycia w grupach tras. Te metody wskazują, że wszystkie punkty końcowe w grupie tras mogą zwracać ProblemDetails
lub ValidationProblemDetails
odpowiadać na potrzeby metadanych interfejsu 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
W tej sekcji opisano nowe funkcje interfejsu OpenAPI
Wbudowana obsługa generowania dokumentów OpenAPI
Specyfikacja interfejsu OpenAPI jest standardem opisującym interfejsy API HTTP. Standard umożliwia deweloperom definiowanie kształtu interfejsów API, które mogą być podłączone do generatorów klientów, generatorów serwerów, narzędzi do testowania, dokumentacji i nie tylko. W wersji zapoznawczej platformy .NET 9 program ASP.NET Core zapewnia wbudowaną obsługę generowania dokumentów OpenAPI reprezentujących interfejsy API oparte na kontrolerach lub minimalnych interfejsach API za pośrednictwem pakietu Microsoft.AspNetCore.OpenApi .
Następujące wyróżnione wywołania kodu:
AddOpenApi
aby zarejestrować wymagane zależności w kontenerze di aplikacji.MapOpenApi
aby zarejestrować wymagane punkty końcowe interfejsu OpenAPI w trasach aplikacji.
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
Zainstaluj pakiet w projekcie przy użyciu następującego polecenia:
dotnet add package Microsoft.AspNetCore.OpenApi --prerelease
Uruchom aplikację i przejdź do openapi/v1.json
strony , aby wyświetlić wygenerowany dokument OpenAPI:
Dokumenty OpenAPI można również wygenerować w czasie kompilacji Microsoft.Extensions.ApiDescription.Server
, dodając pakiet:
dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease
W pliku projektu aplikacji dodaj następujące elementy:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
</PropertyGroup>
Uruchom dotnet build
i sprawdź wygenerowany plik JSON w katalogu projektu.
Wbudowana generacja dokumentów OpenAPI platformy ASP.NET Core zapewnia obsługę różnych dostosowań i opcji. Zapewnia ona przekształcanie dokumentów i operacji oraz umożliwia zarządzanie wieloma dokumentami OpenAPI dla tej samej aplikacji.
Aby dowiedzieć się więcej o nowych możliwościach dokumentów openAPI w programie ASP.NET Core, zobacz nowe dokumenty Microsoft.AspNetCore.OpenApi.
Ulepszenia uzupełniania funkcji IntelliSense dla pakietu OpenAPI
obsługa interfejsu OpenAPI platformy ASP.NET Core jest teraz bardziej dostępna i przyjazna dla użytkownika. Interfejsy API interfejsu OpenAPI są dostarczane jako niezależny pakiet, niezależnie od struktury udostępnionej. Do tej pory oznaczało to, że deweloperzy nie mają wygody funkcji uzupełniania kodu, takich jak IntelliSense dla interfejsów API openAPI.
Rozpoznając tę lukę, wprowadziliśmy nowego dostawcę uzupełniania i poprawkę kodu. Te narzędzia zostały zaprojektowane w celu ułatwienia odnajdywania i używania interfejsów API interfejsu OpenAPI, co ułatwia deweloperom integrowanie interfejsu OpenAPI ze swoimi projektami. Dostawca uzupełniania oferuje sugestie kodu w czasie rzeczywistym, podczas gdy poprawka kodu pomaga w poprawianiu typowych błędów i ulepszaniu użycia interfejsu API. To ulepszenie jest częścią naszego ciągłego zaangażowania w ulepszanie środowiska deweloperów i usprawnianie przepływów pracy związanych z interfejsem API.
Gdy użytkownik wpisze instrukcję, w której jest dostępny interfejs API związany z interfejsem OpenAPI, dostawca uzupełniania wyświetla zalecenie dotyczące interfejsu API. Na przykład na poniższych zrzutach ekranu uzupełniania dodatków AddOpenApi i MapOpenApi są udostępniane, gdy użytkownik wprowadza instrukcję wywołania w obsługiwanym typie, takim jak IEndpointConventionBuilder:
Po zaakceptowaniu ukończenia i nie zainstalowaniu Microsoft.AspNetCore.OpenApi
pakietu narzędzie codefixer udostępnia skrót do automatycznego instalowania zależności w projekcie.
[Required]
Obsługa atrybutów i [DefaultValue]
parametrów i właściwości
Gdy [Required]
atrybuty i [DefaultValue]
są stosowane dla parametrów lub właściwości w typach złożonych, implementacja interfejsu OpenAPI mapuje je na required
właściwości i default
w dokumencie OpenAPI skojarzonym z parametrem lub schematem typu.
Na przykład następujący interfejs API generuje towarzyszący schemat dla Todo
typu.
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"
}
}
}
Obsługa przekształcania schematów w dokumentach OpenAPI
Wbudowana obsługa interfejsu OpenAPI jest teraz dostarczana z obsługą przekształcania schematów, które mogą służyć do modyfikowania schematów generowanych przez plik System.Text.Json i implementację interfejsu OpenAPI. Podobnie jak transformatory dokumentów i operacji, transformatory schematu można zarejestrować w obiekcie OpenApiOptions . Na przykład w poniższym przykładzie kodu pokazano użycie funkcji przekształcania schematu w celu dodania przykładu do schematu typu.
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.OpenApi.Any;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(Todo))
{
schema.Example = new OpenApiObject
{
["id"] = new OpenApiInteger(1),
["title"] = new OpenApiString("A short title"),
["description"] = new OpenApiString("A long description"),
["createdOn"] = new OpenApiDateTime(DateTime.Now)
};
}
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapPost("/todos", (Todo todo) => { });
app.Run();
class Todo
{
public int Id { get; init; }
public required string Title { get; init; }
[DefaultValue("A new todo")]
public required string Description { get; init; }
[Required]
public DateTime CreatedOn { get; init; }
}
Ulepszenia interfejsów API rejestracji przekształcania w interfejsach Microsoft.AspNetCore.OpenApi
Transformatory interfejsu OpenAPI obsługują modyfikowanie dokumentu OpenAPI, operacji w dokumencie lub schematów skojarzonych z typami w interfejsie API. Interfejsy API do rejestrowania transformatorów w dokumencie OpenAPI zapewniają różne opcje rejestrowania transformatorów.
Wcześniej dostępne były następujące interfejsy API do rejestrowania transformatorów:
OpenApiOptions AddDocumentTransformer(Func<OpenApiDocument, OpenApiDocumentTransformerContext, CancellationToken, Task> transformer)
OpenApiOptions AddDocumentTransformer(IOpenApiDocumentTransformer transformer)
OpenApiOptions AddDocumentTransformer<IOpenApiDocumentTransformer>()
OpenApiOptions UseSchemaTransformer(Func<OpenApiSchema, OpenApiSchemaTransformerContext, CancellationToken, Task>)
OpenApiOptions AddOperationTransformer(Func<OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken, Task>)
Nowy zestaw interfejsu API jest następujący:
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>()
Rozwiązanie Microsoft.AspNetCore.OpenApi obsługuje przycinanie i natywną AOT
Nowa wbudowana obsługa interfejsu OpenAPI w programie ASP.NET Core obsługuje teraz również przycinanie i natywną funkcję AOT.
Rozpocznij
Utwórz nowy projekt ASP.NET Core Web API (Native AOT).
dotnet new webapiaot
Dodaj pakiet Microsoft.AspNetCore.OpenAPI.
dotnet add package Microsoft.AspNetCore.OpenApi --prerelease
W tej wersji zapoznawczej należy również dodać najnowszy pakiet Microsoft.OpenAPI, aby uniknąć ostrzeżeń dotyczących przycinania.
dotnet add package Microsoft.OpenApi
Zaktualizuj Program.cs , aby umożliwić generowanie dokumentów OpenAPI.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Opublikuj aplikację.
dotnet publish
Aplikacja publikuje przy użyciu natywnej funkcji AOT bez ostrzeżeń.
Obsługa połączeń ProducesProblem
i ProducesValidationProblem
grup tras
Metody rozszerzenia ProducesProblem i ProducesValidationProblem zostały zaktualizowane dla grup tras. Metody te mogą służyć do wskazywania, że wszystkie punkty końcowe w grupie tras mogą zwracać ProblemDetails
lub ValidationProblemDetails
odpowiadać na potrzeby metadanych interfejsu 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
typy ValidationProblem
wyników obsługują konstrukcję z wartościami IEnumerable<KeyValuePair<string, object?>>
Przed platformą .NET 9 konstruowanie typów wyników Problem i WalidacjaProblem w minimalnych interfejsach API wymaganych do errors
zainicjowania właściwości i extensions
przy użyciu implementacji IDictionary<string, object?>
elementu . W tej wersji te interfejsy API konstrukcji obsługują przeciążenia, które zużywają wartość 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);
});
Dzięki użytkownikowi GitHub joegoldman2 za ten wkład!
Uwierzytelnianie i autoryzacja
W tej sekcji opisano nowe funkcje uwierzytelniania i autoryzacji.
Program OpenIdConnectHandler dodaje obsługę wypychanych żądań autoryzacji (PAR)
Chcielibyśmy podziękować Joe DeCock z Duende Software za dodanie wypychanych żądań autoryzacji (PAR) do ASP.NET Core OpenIdConnectHandler. Joe opisał tło i motywację do włączenia par w swojej propozycji interfejsu API w następujący sposób:
Wypychane żądania autoryzacji (PAR) to stosunkowo nowy standard OAuth, który poprawia bezpieczeństwo przepływów OAuth i OIDC przez przeniesienie parametrów autoryzacji z kanału frontu do kanału wstecznego. Oznacza to, że przeniesienie parametrów autoryzacji z adresów URL przekierowania w przeglądarce w celu przekierowania maszyny do maszyny http wywołań na zapleczu.
Zapobiega to cyberatakom w przeglądarce:
- Wyświetlanie parametrów autoryzacji, które mogą wyciekać dane osobowe.
- Manipulowanie tymi parametrami. Na przykład cyberatak może zmienić żądany zakres dostępu.
Wypychanie parametrów autoryzacji powoduje również krótkie adresy URL żądań. Parametry autoryzacji mogą być bardzo długie w przypadku korzystania z bardziej złożonych funkcji OAuth i OIDC, takich jak zaawansowane żądania autoryzacji. Adresy URL, które są długo przyczyną problemów w wielu przeglądarkach i infrastruktur sieciowych.
Korzystanie z par jest zachęcane przez grupę roboczą FAPI w ramach OpenID Foundation. Na przykład profil zabezpieczeń FAPI2.0 wymaga użycia par. Ten profil zabezpieczeń jest używany przez wiele grup pracujących nad otwartą bankowością (przede wszystkim w Europie), w opiece zdrowotnej i w innych branżach z wysokimi wymaganiami dotyczącymi bezpieczeństwa.
Par jest obsługiwany przez wielu identity dostawców, w tym
W przypadku platformy .NET 9 postanowiliśmy domyślnie włączyć par, jeśli identity dokument odnajdywania dostawcy ogłasza obsługę par, ponieważ powinien zapewnić zwiększone zabezpieczenia dla dostawców, którzy go obsługują. Dokument identity odnajdywania dostawcy znajduje się zwykle pod adresem .well-known/openid-configuration
. Jeśli powoduje to problemy, możesz wyłączyć par za pośrednictwem openIdConnectOptions.PushedAuthorizationBehavior w następujący sposób:
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;
});
Aby upewnić się, że uwierzytelnianie powiedzie się tylko w przypadku użycia par, zamiast tego użyj polecenia PushedAuthorizationBehavior.Require . Ta zmiana wprowadza również nowe zdarzenie OnPushAuthorization do biblioteki OpenIdConnectEvents, które może służyć do dostosowywania wypychanego żądania autoryzacji lub obsługi go ręcznie. Zobacz propozycję interfejsu API, aby uzyskać więcej szczegółów.
Dostosowywanie parametrów OIDC i OAuth
Programy obsługi uwierzytelniania OAuth i OIDC mają AdditionalAuthorizationParameters
teraz możliwość ułatwienia dostosowywania parametrów komunikatów autoryzacji, które są zwykle uwzględniane jako część ciągu zapytania przekierowania. W programie .NET 8 i starszych wersjach wymaga to niestandardowego OnRedirectToIdentityProvider wywołania zwrotnego lub zastąpienia BuildChallengeUrl metody w niestandardowej procedurze obsługi. Oto przykład kodu platformy .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;
};
});
Powyższy przykład można teraz uprościć do następującego kodu:
builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
options.AdditionalAuthorizationParameters.Add("prompt", "login");
options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});
Konfigurowanie flag uwierzytelniania rozszerzonego HTTP.sys
Teraz można skonfigurować HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
flagi i HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL
HTTP.sys przy użyciu nowych EnableKerberosCredentialCaching
właściwości i CaptureCredentials
w HTTP.sys AuthenticationManager , aby zoptymalizować sposób obsługi uwierzytelniania systemu Windows. Na przykład:
webBuilder.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
options.Authentication.EnableKerberosCredentialCaching = true;
options.Authentication.CaptureCredentials = true;
});
Różne
W poniższych sekcjach opisano różne nowe funkcje.
Nowa HybridCache
biblioteka
Interfejs HybridCache
API łączy pewne luki w istniejących IDistributedCache interfejsach API i IMemoryCache . Dodaje również nowe funkcje, takie jak:
- Ochrona "Stampede", aby zapobiec równoległym pobieraniu tych samych zadań.
- Konfigurowalna serializacji.
HybridCache
jest przeznaczony do zastąpienia upuszczania dla istniejących IDistributedCache
i IMemoryCache
użycia, a także udostępnia prosty interfejs API do dodawania nowego kodu buforowania. Zapewnia ujednolicony interfejs API zarówno w przypadku buforowania w procesie, jak i poza procesem.
Aby zobaczyć, jak HybridCache
interfejs API jest uproszczony, porównaj go z kodem, który używa metody IDistributedCache
. Oto przykład użycia 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 dużo pracy, aby uzyskać rację za każdym razem, w tym takie rzeczy jak serializacja. W scenariuszu miss pamięci podręcznej można znaleźć wiele współbieżnych wątków, wszystkie chybienie pamięci podręcznej, wszystkie pobieranie danych bazowych, wszystkie serializacji i wszystkie wysyłanie tych danych do pamięci podręcznej.
Aby uprościć i ulepszyć ten kod za pomocą HybridCache
polecenia , najpierw musimy dodać nową bibliotekę Microsoft.Extensions.Caching.Hybrid
:
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Zarejestruj usługę HybridCache
, aby zarejestrować implementację IDistributedCache
:
builder.Services.AddHybridCache(); // Not shown: optional configuration API.
Teraz większość problemów z buforowaniem można odciążyć do :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
);
}
}
Udostępniamy konkretną implementację klasy abstrakcyjnej HybridCache
za pośrednictwem wstrzykiwania zależności, ale deweloperzy mogą udostępniać niestandardowe implementacje interfejsu API. Implementacja HybridCache
dotyczy wszystkich elementów związanych z buforowaniem, w tym obsługą operacji współbieżnych. Token cancel
w tym miejscu reprezentuje połączone anulowanie wszystkich współbieżnych wywołań — nie tylko anulowanie obiektu wywołującego, które widzimy (czyli token
).
Scenariusze o wysokiej przepływności można dodatkowo zoptymalizować przy użyciu TState
wzorca, aby uniknąć narzutów z przechwyconych zmiennych i wywołań zwrotnych dla poszczególnych wystąpień:
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
używa skonfigurowanej IDistributedCache
implementacji, jeśli istnieje, na potrzeby pomocniczego buforowania poza procesem, na przykład przy użyciu usługi Redis. Ale nawet bez IDistributedCache
, HybridCache
usługa nadal zapewnia buforowanie w procesie i "stampede" ochrony.
Uwaga dotycząca ponownego użycia obiektu
W typowym istniejącym kodzie, który używa IDistributedCache
metody , każde pobieranie obiektu z pamięci podręcznej powoduje deserializacji. To zachowanie oznacza, że każdy współbieżny obiekt wywołujący otrzymuje oddzielne wystąpienie obiektu, które nie może wchodzić w interakcje z innymi wystąpieniami. Wynikiem jest bezpieczeństwo wątków, ponieważ nie ma ryzyka współbieżnych modyfikacji w tym samym wystąpieniu obiektu.
Ponieważ wiele użycia zostanie dostosowanych z istniejącego HybridCache
IDistributedCache
kodu, zachowuje to zachowanie domyślnie, HybridCache
aby uniknąć wprowadzania usterek współbieżności. Jednak dany przypadek użycia jest z natury bezpieczny wątkowo:
- Jeśli buforowane typy są niezmienne.
- Jeśli kod nie zmodyfikuje ich.
W takich przypadkach należy poinformować HybridCache
, że ponowne użycie wystąpień jest bezpieczne przez:
- Oznaczanie typu jako
sealed
. Słowosealed
kluczowe w języku C# oznacza, że nie można dziedziczyć klasy. - Zastosowanie do niego atrybutu
[ImmutableObject(true)]
. Atrybut[ImmutableObject(true)]
wskazuje, że nie można zmienić stanu obiektu po jego utworzeniu.
Przez ponowne użycie wystąpień HybridCache
może zmniejszyć obciążenie związane z alokacją procesora CPU i obiektu skojarzonymi z deserializacji poszczególnych wywołań. Może to prowadzić do poprawy wydajności w scenariuszach, w których buforowane obiekty są duże lub często używane.
Inne HybridCache
funkcje
Podobnie jak IDistributedCache
, HybridCache
obsługuje usuwanie według klucza za pomocą RemoveKeyAsync
metody .
HybridCache
Udostępnia również opcjonalne interfejsy API dla IDistributedCache
implementacji, aby uniknąć byte[]
alokacji. Ta funkcja jest implementowana przez wersje Microsoft.Extensions.Caching.StackExchangeRedis
zapoznawcza pakietów i Microsoft.Extensions.Caching.SqlServer
.
Serializacja jest konfigurowana w ramach rejestrowania usługi z obsługą serializatorów specyficznych dla typu i uogólnionych za pośrednictwem WithSerializer
metod i .WithSerializerFactory
, w łańcuchu od wywołania AddHybridCache
. Domyślnie biblioteka obsługuje string
i byte[]
wewnętrznie używa System.Text.Json
wszystkich innych elementów, ale możesz użyć narzędzia protobuf, xml lub innego elementu.
HybridCache
obsługuje starsze środowiska uruchomieniowe platformy .NET w dół do programów .NET Framework 4.7.2 i .NET Standard 2.0.
Aby uzyskać więcej informacji na temat HybridCache
usługi , zobacz biblioteka HybridCache w programie ASP.NET Core
Ulepszenia strony wyjątków dla deweloperów
Strona wyjątku dewelopera platformy ASP.NET Core jest wyświetlana, gdy aplikacja zgłasza nieobsługiwany wyjątek podczas programowania. Strona wyjątku dla deweloperów zawiera szczegółowe informacje o wyjątku i żądaniu.
Wersja zapoznawcza 3 dodała metadane punktu końcowego do strony wyjątku dewelopera. ASP.NET Core używa metadanych punktu końcowego do kontrolowania zachowania punktu końcowego, takich jak routing, buforowanie odpowiedzi, ograniczanie szybkości, generowanie interfejsu OpenAPI i nie tylko. Na poniższej ilustracji przedstawiono nowe informacje o metadanych w Routing
sekcji strony wyjątku dewelopera:
Podczas testowania strony wyjątku dla deweloperów zidentyfikowano niewielką jakość życia. Zostały one wysłane w wersji zapoznawczej 4:
- Lepsze zawijanie tekstu. Długie pliki cookie, wartości ciągu zapytania i nazwy metod nie dodają już poziomych pasków przewijania przeglądarki.
- Większy tekst, który znajduje się w nowoczesnych projektach.
- Bardziej spójne rozmiary tabel.
Na poniższej animowanej ilustracji przedstawiono nową stronę wyjątku dla deweloperów:
Ulepszenia debugowania słownika
Debugowanie wyświetlania słowników i innych kolekcji klucz-wartość ma ulepszony układ. Klucz jest wyświetlany w kolumnie klucza debugera zamiast łączyć się z wartością. Na poniższych obrazach przedstawiono stary i nowy ekran słownika w debugerze.
Przed:
Po:
ASP.NET Core ma wiele kolekcji klucz-wartość. To ulepszone środowisko debugowania ma zastosowanie do:
- Nagłówki HTTP
- Ciągi zapytań
- Formularze
- Pliki cookie
- Wyświetlanie danych
- Dane trasy
- Funkcje
Poprawka dotycząca funkcji 503 podczas recyklingu aplikacji w usługach IIS
Domyślnie występuje 1-sekundowe opóźnienie między powiadomieniem usług IIS o koszu lub zamknięciu, a gdy usługa ANCM informuje serwer zarządzany o rozpoczęciu zamykania. Opóźnienie można skonfigurować za pomocą zmiennej środowiskowej ANCM_shutdownDelay
lub przez ustawienie shutdownDelay
ustawienia programu obsługi. Obie wartości znajdują się w milisekundach. Opóźnienie polega głównie na zmniejszeniu prawdopodobieństwa wyścigu, w którym:
- Usługi IIS nie uruchomiły kolejkowania żądań, aby przejść do nowej aplikacji.
- Usługa ANCM rozpoczyna odrzucanie nowych żądań, które wchodzą do starej aplikacji.
Wolniejsze maszyny lub maszyny z większym użyciem procesora CPU mogą chcieć dostosować tę wartość, aby zmniejszyć prawdopodobieństwo 503.
Przykład ustawienia 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>
Poprawka znajduje się w globalnie zainstalowanym module ANCM pochodzącym z pakietu hostingu.
Optymalizowanie statycznego dostarczania zasobów internetowych
Przestrzeganie najlepszych rozwiązań produkcyjnych dotyczących obsługi zasobów statycznych wymaga znacznej ilości pracy i wiedzy technicznej. Bez optymalizacji, takich jak kompresja, buforowanie i odciski palców:
- Przeglądarka musi wysyłać dodatkowe żądania na każdym ładowaniu strony.
- Więcej bajtów niż jest to konieczne jest transferowanych za pośrednictwem sieci.
- Czasami nieaktualne wersje plików są obsługiwane klientom.
Tworzenie wydajnych aplikacji internetowych wymaga optymalizacji dostarczania zasobów do przeglądarki. Możliwe optymalizacje obejmują:
- Obsłuż dany zasób raz, dopóki plik nie ulegnie zmianie lub przeglądarka wyczyści jego pamięć podręczną. Ustaw nagłówek ETag.
- Uniemożliwiaj przeglądarce używanie starych lub nieaktualnych zasobów po zaktualizowaniu aplikacji. Ustaw nagłówek Ostatnia modyfikacja.
- Skonfiguruj odpowiednie nagłówki buforowania.
- Użyj oprogramowania pośredniczącego buforowania.
- Jeśli to możliwe, obsłuż skompresowane wersje zasobów.
- Użyj sieci CDN , aby udostępnić zasoby bliżej użytkownika.
- Zminimalizuj rozmiar zasobów obsługiwanych w przeglądarce. Ta optymalizacja nie obejmuje minification.
MapStaticAssets
to nowe oprogramowanie pośredniczące, które pomaga zoptymalizować dostarczanie zasobów statycznych w aplikacji. Jest ona przeznaczona do pracy ze wszystkimi strukturami interfejsu użytkownika, w tym Blazorz elementami , Razor Pages i MVC. Zazwyczaj jest to zamiana drop-in dla 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
działa przez połączenie procesów kompilowania i publikowania w celu zbierania informacji o wszystkich zasobach statycznych w aplikacji. Te informacje są następnie wykorzystywane przez bibliotekę środowiska uruchomieniowego do wydajnego udostępniania tych plików w przeglądarce.
MapStaticAssets
program może zastąpić UseStaticFiles
w większości sytuacji, jednak jest zoptymalizowany pod kątem obsługi zasobów, o których aplikacja ma wiedzę w czasie kompilacji i publikowania. Jeśli aplikacja obsługuje zasoby z innych lokalizacji, takich jak dyski lub zasoby osadzone, UseStaticFiles
powinny być używane.
MapStaticAssets
zapewnia następujące korzyści, których nie znaleziono w programie UseStaticFiles
:
- Kompresja czasu kompilacji dla wszystkich zasobów w aplikacji:
gzip
podczas opracowywania igzip + brotli
publikowania.- Wszystkie zasoby są kompresowane w celu zmniejszenia rozmiaru zasobów do minimum.
- Oparte na
ETags
zawartości: dlaEtags
każdego zasobu są ciągiem zakodowanym w formacie Base64 skrótu SHA-256 zawartości. Dzięki temu przeglądarka ponownie pobierze plik tylko wtedy, gdy jego zawartość uległa zmianie.
W poniższej tabeli przedstawiono oryginalne i skompresowane rozmiary plików CSS i JS plików w domyślnym Razor szablonie stron:
Plik | Oryginalne | Skompresowane | % redukcji |
---|---|---|---|
bootstrap.min.css | 163 | 17.5 | 89.26% |
jquery.js | 89.6 | 28 | 68.75% |
bootstrap.min.js | 78.5 | 20 | 74.52% |
Łącznie | 331.1 | 65.5 | 80.20% |
W poniższej tabeli przedstawiono oryginalne i skompresowane rozmiary przy użyciu biblioteki składników interfejsu użytkownika Blazor Fluent:
Plik | Oryginalne | Skompresowane | % redukcji |
---|---|---|---|
biegły.js | 384 | 73 | 80.99% |
fluent.css | 94 | 11 | 88.30% |
Łącznie | 478 | 84 | 82.43% |
W sumie 478 KB nieskompresowane do 84 KB skompresowane.
W poniższej tabeli przedstawiono oryginalne i skompresowane rozmiary przy użyciu biblioteki składników MudBlazorBlazor :
Plik | Oryginalne | Skompresowane | Redukcja |
---|---|---|---|
MudBlazor.min.css | 541 | 37.5 | 93.07% |
MudBlazor.min.js | 47.4 | 9,2 | 80.59% |
Łącznie | 588.4 | 46,7 | 92.07% |
Optymalizacja odbywa się automatycznie w przypadku korzystania z programu MapStaticAssets
. Po dodaniu lub zaktualizowaniu biblioteki, na przykład przy użyciu nowego kodu JavaScript lub CSS, zasoby są optymalizowane w ramach kompilacji. Optymalizacja jest szczególnie korzystna dla środowisk mobilnych, które mogą mieć niższą przepustowość lub zawodne połączenia.
Aby uzyskać więcej informacji na temat nowych funkcji dostarczania plików, zobacz następujące zasoby:
Włączanie kompresji dynamicznej na serwerze a przy użyciu MapStaticAssets
MapStaticAssets
ma następujące zalety niż kompresja dynamiczna na serwerze:
- Jest prostsze, ponieważ nie ma określonej konfiguracji serwera.
- Jest bardziej wydajne, ponieważ zasoby są kompresowane w czasie kompilacji.
- Umożliwia deweloperowi spędzenie dodatkowego czasu podczas procesu kompilacji, aby upewnić się, że zasoby są minimalnym rozmiarem.
Rozważmy poniższą tabelę porównującą kompresję MudBlazor z kompresją dynamiczną usług IIS i MapStaticAssets
:
Program IIS gzip | MapStaticAssets | Redukcja zasobów mapstatycznych |
---|---|---|
≅ 90 | 37.5 | 59% |
ASP0026: Analizator ostrzega, gdy [Autoryzuj] jest zastępowany przez [AllowAnonymous] z "dalej"
Wydaje się intuicyjne, że [Authorize]
atrybut umieszczony "bliżej" akcji MVC niż [AllowAnonymous]
atrybut zastąpi [AllowAnonymous]
atrybut i wymusza autoryzację. Jednak niekoniecznie tak jest. To, co ma znaczenie, to względna kolejność atrybutów.
Poniższy kod pokazuje przykłady, w których bliżej [Authorize]
atrybut jest zastępowany przez [AllowAnonymous]
atrybut, który jest dalej.
[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
{
}
W programie .NET 9 (wersja zapoznawcza 6) wprowadziliśmy analizator, który będzie wyróżniał wystąpienia takie jak te, w których atrybut bliżej [Authorize]
zostaje zastąpiony przez [AllowAnonymous]
atrybut, który jest dalej od akcji MVC. Ostrzeżenie wskazuje zastąpiony [Authorize]
atrybut z następującym komunikatem:
ASP0026 [Authorize] overridden by [AllowAnonymous] from farther away
Prawidłowa akcja do wykonania, jeśli to ostrzeżenie zależy od intencji atrybutów. Atrybut odejścia [AllowAnonymous]
powinien zostać usunięty, jeśli nieumyślnie uwidacznia punkt końcowy użytkownikom anonimowym. [AllowAnonymous]
Jeśli atrybut miał zastąpić bliżej [Authorize]
atrybutu, można powtórzyć [AllowAnonymous]
atrybut po atrybucie[Authorize]
, aby wyjaśnić intencję.
[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;
}
Ulepszone Kestrel metryki połączenia
Wprowadziliśmy znaczącą poprawę Kestrelmetryk połączeń, włączając metadane dotyczące przyczyny niepowodzenia połączenia. kestrel.connection.duration
Metryka zawiera teraz przyczynę zamknięcia połączenia w atrybucie error.type
.
Oto mała próbka error.type
wartości:
tls_handshake_failed
— Połączenie wymaga protokołu TLS, a uzgadnianie protokołu TLS nie powiodło się.connection_reset
— Połączenie zostało nieoczekiwanie zamknięte przez klienta, gdy żądania były w toku.request_headers_timeout
- Kestrel zamknął połączenie, ponieważ nie odebrał nagłówków żądań na czas.max_request_body_size_exceeded
— Kestrel połączenie zostało zamknięte, ponieważ przekazane dane przekroczyły maksymalny rozmiar.
Wcześniej diagnozowanie Kestrel problemów z połączeniem wymagało od serwera rejestrowania szczegółowego, niskiego poziomu rejestrowania. Jednak dzienniki mogą być kosztowne do wygenerowania i przechowywania i może być trudne do znalezienia odpowiednich informacji wśród szumu.
Metryki to znacznie tańsza alternatywa, którą można pozostawić w środowisku produkcyjnym z minimalnym wpływem. Zebrane metryki mogą obsługiwać pulpity nawigacyjne i alerty. Po zidentyfikowaniu problemu na wysokim poziomie z metrykami można rozpocząć dalsze badanie przy użyciu rejestrowania i innych narzędzi.
Oczekujemy, że ulepszone metryki połączenia będą przydatne w wielu scenariuszach:
- Badanie problemów z wydajnością spowodowanych krótkim okresem istnienia połączenia.
- Obserwowanie ciągłych ataków zewnętrznych na Kestrel ten wpływ na wydajność i stabilność.
- Nagranie próbowało ataków zewnętrznych na Kestrel Kestrelzapobieganie wbudowanym zabezpieczeniom zabezpieczeń.
Aby uzyskać więcej informacji, zobacz ASP.NET Core metrics (Metryki podstawowe ASP.NET).
Dostosowywanie Kestrel nazwanych punktów końcowych potoku
KestrelObsługa nazwanych potoków została ulepszona dzięki zaawansowanym opcjom dostosowywania. Nowa CreateNamedPipeServerStream
metoda w nazwanych opcjach potoku umożliwia dostosowywanie potoków dla poszczególnych punktów końcowych.
Przykładem, gdzie jest to przydatne, jest Kestrel aplikacja, która wymaga dwóch punktów końcowych potoku z różnymi zabezpieczeniami dostępu. Opcja CreateNamedPipeServerStream
może służyć do tworzenia potoków z niestandardowymi ustawieniami zabezpieczeń w zależności od nazwy potoku.
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
opcja wyboru kodu stanu na podstawie typu wyjątku
Nowa opcja podczas konfigurowania ExceptionHandlerMiddleware
programu umożliwia deweloperom aplikacji wybranie kodu stanu, który ma być zwracany, gdy wystąpi wyjątek podczas obsługi żądań. Nowa opcja zmienia kod stanu ustawiany w ProblemDetails
odpowiedzi z .ExceptionHandlerMiddleware
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Rezygnacja z metryk HTTP w niektórych punktach końcowych i żądaniach
Platforma .NET 9 wprowadza możliwość rezygnacji z metryk HTTP dla określonych punktów końcowych i żądań. Rezygnacja z rejestrowania metryk jest korzystna dla punktów końcowych często wywoływanych przez zautomatyzowane systemy, takie jak kontrole kondycji. Rejestrowanie metryk dla tych żądań jest zwykle niepotrzebne.
Żądania HTTP do punktu końcowego można wykluczyć z metryk, dodając metadane. Dowolny z następujących elementów:
- Dodaj atrybut do kontrolera internetowego interfejsu
[DisableHttpMetrics]
API, SignalR centrum lub usługi gRPC. - Wywołaj metodę DisableHttpMetrics podczas mapowania punktów końcowych podczas uruchamiania aplikacji:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
Właściwość MetricsDisabled
została dodana do IHttpMetricsTagsFeature
elementu dla:
- Zaawansowane scenariusze, w których żądanie nie jest mapowe na punkt końcowy.
- Dynamiczne wyłączanie kolekcji metryk dla określonych żądań 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);
});
Obsługa ochrony danych na potrzeby usuwania kluczy
Przed platformą .NET 9 klucze ochrony danych nie zostały usunięte zgodnie z projektem, aby zapobiec utracie danych. Usunięcie klucza powoduje, że jego chronione dane są nieodwracalne. Biorąc pod uwagę ich mały rozmiar, akumulacja tych kluczy zwykle stwarzała minimalny wpływ. Jednak w celu obsługi bardzo długotrwałych usług wprowadziliśmy opcję usuwania kluczy. Ogólnie rzecz biorąc, należy usunąć tylko stare klucze. Usuń klucze tylko wtedy, gdy można zaakceptować ryzyko utraty danych w zamian za oszczędności magazynu. Zalecamy, aby klucze ochrony danych nie zostały usunięte.
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.");
}
Oprogramowanie pośredniczące obsługuje klucz di
Oprogramowanie pośredniczące obsługuje teraz klucz di zarówno w konstruktorze, jak i metodzie/Invoke
InvokeAsync
:
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);
}
Ufaj certyfikatowi programistycznemu ASP.NET Core HTTPS w systemie Linux
W dystrybucjach systemu Linux opartych na systemach dotnet dev-certs https --trust
Ubuntu i Fedora teraz konfiguruje certyfikat programowania ASP.NET Core HTTPS jako zaufany certyfikat dla:
- Przeglądarki Chromium, na przykład Google Chrome, Microsoft Edge i Chromium.
- Mozilla Firefox i Mozilla pochodne przeglądarki.
- Interfejsy API platformy .NET, na przykład HttpClient
--trust
Wcześniej pracował tylko w systemach Windows i macOS. Zaufanie certyfikatu jest stosowane dla poszczególnych użytkowników.
Aby ustanowić zaufanie w programie OpenSSL, dev-certs
narzędzie:
- Umieszcza certyfikat w
~/.aspnet/dev-certs/trust
- Uruchamia uproszczoną wersję narzędzia c_rehash openSSL w katalogu.
- Prosi użytkownika o zaktualizowanie zmiennej środowiskowej
SSL_CERT_DIR
.
Aby ustanowić zaufanie w aplikacji dotnet, narzędzie umieszcza certyfikat w My/Root
magazynie certyfikatów.
Aby ustanowić zaufanie do baz danych NSS, jeśli istnieją, narzędzie wyszukuje home katalog dla profilów Firefox, ~/.pki/nssdb
i ~/snap/chromium/current/.pki/nssdb
. Dla każdego znalezionego katalogu narzędzie dodaje wpis do pliku nssdb
.
Szablony zaktualizowane do najnowszych wersji bootstrap, jQuery i jQuery Validation
Szablony i biblioteki projektów ASP.NET Core zostały zaktualizowane w celu używania najnowszych wersji bootstrap, jQuery i jQuery Validation, w szczególności:
- Bootstrap 5.3.3
- jQuery 3.7.1
- jQuery Validation 1.21.0