Apa yang baru dalam ASP.NET Core di .NET 10

Artikel ini menyoroti perubahan paling signifikan dalam ASP.NET Core di .NET 10 dengan tautan ke dokumentasi yang relevan.

Blazor

Bagian ini menjelaskan fitur baru untuk Blazor.

Sampel keamanan baru dan yang diperbarui Blazor Web App

Kami telah menambahkan dan memperbarui sampel keamanan yang Blazor Web App ditautkan dalam artikel berikut:

Semua solusi sampel OIDC dan Entra kami sekarang menyertakan proyek API web terpisah (MinimalApiJwt) untuk menunjukkan cara mengonfigurasi dan memanggil API web eksternal dengan aman. Pemanggilan API web ditunjukkan dengan handler token dan klien HTTP bernama untuk penyedia identitas OIDC atau Microsoft Identity Paket Web/API untuk Microsoft Entra ID.

Solusi sampel dikonfigurasi dalam kode C# dalam file mereka Program . Untuk mengonfigurasi solusi dari file pengaturan aplikasi (misalnya, appsettings.json) lihat bagian Konfigurasi pasokan baru dengan penyedia konfigurasi JSON (pengaturan aplikasi) dari artikel OIDC atau Entra.

Artikel Entra dan aplikasi sampel kami juga menyertakan panduan baru tentang pendekatan berikut:

parameter QuickGridRowClass

Aplikasikan kelas stylesheet ke baris grid berdasarkan item baris menggunakan parameter RowClass yang baru. Dalam contoh berikut, metode GetRowCssClass dipanggil pada setiap baris untuk menerapkan kelas lembar gaya secara kondisional berdasarkan item baris:

<QuickGrid ... RowClass="GetRowCssClass">
    ...
</QuickGrid>

@code {
    private string GetRowCssClass(MyGridItem item) =>
        item.IsArchived ? "row-archived" : null;
}

Untuk informasi selengkapnya, lihat komponen ASP.NET Core Blazor 'QuickGrid'.

Skrip Blazor sebagai aset web statis

Dalam rilis .NET sebelumnya, skrip Blazor disajikan dari sumber daya yang disematkan dalam kerangka kerja bersama ASP.NET Core. Dalam .NET 10 atau lebih baru, skrip Blazor disajikan sebagai aset web statis dengan kompresi dan sidik jari otomatis.

Blazor Skrip (blazor.web.js atau blazor.server.js) disertakan oleh framework jika proyek berisi minimal satu berkas komponen Razor (.razor). Jika aplikasi Anda memerlukan Blazor skrip tetapi tidak berisi setidaknya satu komponen, tambahkan properti MSBuild berikut ke file proyek aplikasi untuk memaksa penyertaan skrip tanpa syarat:

<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>

Untuk informasi selengkapnya, lihat sumber daya berikut ini:

Sorotan pola rute

Atribut[Route] sekarang mendukung penyorotan sintaks rute untuk membantu memvisualisasikan struktur templat rute:

Pola template atribut rute untuk nilai counter menunjukkan penyorotan sintaks

Sebelumnya, NavigationManager.NavigateTo bergulir ke bagian atas halaman untuk navigasi dalam halaman yang sama. Perilaku ini telah diubah dalam .NET 10 sehingga browser tidak lagi menggulir ke bagian atas halaman saat menavigasi ke halaman yang sama. Ini berarti viewport tidak lagi diatur ulang saat membuat pembaruan pada alamat untuk halaman saat ini, seperti mengubah string kueri atau fragmen.

Komponen antarmuka pengguna untuk koneksi ulang ditambahkan ke templat proyek Blazor Web App

Templat proyek Blazor Web App sekarang menyertakan komponen ReconnectModal, termasuk file lembar gaya dan JavaScript yang terintegrasi, untuk meningkatkan kontrol pengembang atas antarmuka pengguna untuk koneksi ulang ketika klien terputus dari koneksi WebSocket ke server. Komponen tidak menyisipkan gaya melalui kode program, dengan demikian memastikan kepatuhan terhadap pengaturan Kebijakan Keamanan Konten (CSP) yang lebih ketat untuk kebijakan style-src. Dalam rilis sebelumnya, UI koneksi ulang default dibuat oleh kerangka kerja dengan cara yang dapat menyebabkan pelanggaran CSP. Perhatikan bahwa antarmuka pengguna pemulihan sambungan bawaan tetap digunakan sebagai cadangan saat aplikasi tidak menentukan antarmuka pengguna pemulihan sambungan, misalnya dengan menggunakan komponen ReconnectModal dari templat proyek atau komponen kustom serupa.

Fitur antarmuka pengguna rekoneksi baru:

  • Selain menunjukkan status koneksi ulang dengan mengatur kelas CSS tertentu pada elemen antarmuka pengguna untuk koneksi ulang, peristiwa baru components-reconnect-state-changed dikirimkan ketika ada perubahan pada status koneksi ulang.
  • Kode dapat dengan lebih baik membedakan tahapan proses rekoneksi menggunakan status rekoneksi baru "retrying," yang ditunjukkan oleh kelas CSS dan peristiwa baru.

Untuk informasi selengkapnya, lihat panduan ASP.NET Core BlazorSignalR.

Abaikan string dan fragmen kueri saat menggunakan NavLinkMatch.All

Komponen NavLink sekarang mengabaikan string dan fragmen kueri saat menggunakan nilai NavLinkMatch.All untuk parameter Match. Ini berarti bahwa tautan mempertahankan kelas active jika jalur URL cocok tetapi string kueri atau fragmen berubah. Untuk kembali ke perilaku asli, gunakan saklar Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentAppContext yang diatur ke true.

Anda juga dapat mengambil alih metode ShouldMatch pada NavLink untuk menyesuaikan perilaku yang cocok:

public class CustomNavLink : NavLink
{
    protected override bool ShouldMatch(string currentUriAbsolute)
    {
        // Custom matching logic
    }
}

Untuk informasi selengkapnya, lihat navigasi ASP.NET Core Blazor.

Tutup kolom QuickGrid opsi

Sekarang Anda dapat menutup antarmuka pengguna opsi kolom QuickGrid menggunakan metode HideColumnOptionsAsync baru.

Contoh berikut menggunakan metode HideColumnOptionsAsync untuk menutup antarmuka pengguna opsi kolom segera setelah filter judul diterapkan:

<QuickGrid @ref="movieGrid" Items="movies">
    <PropertyColumn Property="@(m => m.Title)" Title="Title">
        <ColumnOptions>
            <input type="search" @bind="titleFilter" placeholder="Filter by title" 
                @bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
        </ColumnOptions>
    </PropertyColumn>
    <PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
    <PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>

@code {
    private QuickGrid<Movie>? movieGrid;
    private string titleFilter = string.Empty;
    private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
    private IQueryable<Movie> filteredMovies => 
        movies.Where(m => m.Title!.Contains(titleFilter));
}

Streaming respons HttpClient diaktifkan secara default

Dalam rilis sebelumnya Blazor, streaming respons untuk permintaan HttpClient memerlukan pendaftaran. Sekarang, streaming respons diaktifkan secara default.

Ini adalah perubahan besar karena memanggil HttpContent.ReadAsStreamAsync untuk HttpResponseMessage.Content (response.Content.ReadAsStreamAsync()) mengembalikan BrowserHttpReadStream dan bukan lagi MemoryStream. BrowserHttpReadStream tidak mendukung operasi sinkron, seperti Stream.Read(Span<Byte>). Jika kode Anda menggunakan operasi sinkron, Anda dapat memilih untuk tidak menggunakan streaming respons atau menyalin Stream ke dalam MemoryStream sendiri.

Untuk menolak streaming respons secara global, gunakan salah satu pendekatan berikut:

  • <WasmEnableStreamingResponse> Tambahkan properti ke file proyek dengan nilai false:

    <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
    
  • Atur DOTNET_WASM_ENABLE_STREAMING_RESPONSE variabel lingkungan ke false atau 0.

Untuk menolak streaming respons untuk permintaan individual, atur SetBrowserResponseStreamingEnabled ke false pada HttpRequestMessage (requestMessage dalam contoh berikut):

requestMessage.SetBrowserResponseStreamingEnabled(false);

Untuk informasi selengkapnya, lihat HttpClient dan HttpRequestMessage dengan opsi permintaan Fetch API (artikel Panggil API web ).

Sidik jari sisi klien

Rilis .NET 9 memperkenalkan fingerprinting sisi server aset statis di s dengan pengenalan konvensi titik akhir perutean Map Static Assets (Blazor Web App), komponen MapStaticAssets, dan properti (ImportMap) untuk menyelesaikan modul JavaScript yang difingerprint (). Untuk .NET 10, Anda dapat mengaktifkan pembuatan sidik jari di sisi klien untuk modul JS dalam aplikasi mandiri Blazor WebAssembly.

Di aplikasi mandiri Blazor WebAssembly selama build dan penerbitan, kerangka kerja menggantikan tempat penampung index.html dengan nilai yang dihitung selama build untuk mensidik jari aset statis. Sidik jari ditempatkan ke dalam nama file skrip blazor.webassembly.js.

Markup berikut harus ada dalam wwwroot/index.html file untuk mengadopsi fitur sidik jari:

<head>
    ...
+   <script type="importmap"></script>
</head>

<body>
    ...
-   <script src="_framework/blazor.webassembly.js"></script>
+   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>

</html>

Dalam file proyek (.csproj), tambahkan properti <OverrideHtmlAssetPlaceholders> atur ke true.

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
+   <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
  </PropertyGroup>
</Project>

Dalam contoh berikut, semua file yang disediakan JS pengembang adalah modul dengan .js ekstensi file.

Modul bernama scripts.js di folder wwwroot/js aplikasi ditandai dengan menambahkan #[.{fingerprint}] sebelum ekstensi file (.js).

<script type="module" src="js/scripts#[.{fingerprint}].js"></script>

Tentukan ekspresi sidik jari dengan <StaticWebAssetFingerprintPattern> properti dalam file proyek aplikasi (.csproj):

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

File apa pun JS (*.js) di index.html dengan penanda sidik jari diperiksa oleh kerangka kerja, termasuk saat aplikasi diterbitkan.

Jika Anda mengadopsi ekstensi file .mjs untuk modul JS, aturlah ekstensi file tersebut menggunakan parameter Pattern.

<ItemGroup>
  <StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
    Expression="#[.{fingerprint}]!" />
</ItemGroup>

File ditempatkan ke dalam peta impor:

  • Penyajian sisi klien (CSR) dilakukan secara otomatis.
  • Saat memilih untuk berpartisipasi dalam fingerprinting modul di aplikasi mandiri Blazor WebAssembly sesuai dengan instruksi sebelumnya.

Pada saat menyelesaikan impor untuk interop JavaScript, peta impor digunakan oleh browser untuk memproses file yang diberi sidik jari.

Aset statis kerangka kerja yang dimuat sebelumnya Blazor

Dalam Blazor Web Appkerangka, aset statis secara otomatis dipra-muat menggunakan Link header, yang memungkinkan browser memuat sumber daya sebelum halaman awal dimuat dan dirender.

Aplikasi mandiri Blazor WebAssembly , aset framework dijadwalkan untuk pengunduhan dan penyimpanan sementara prioritas tinggi di awal pemrosesan halaman browser index.html saat:

  • Properti OverrideHtmlAssetPlaceholders MSBuild dalam file proyek aplikasi (.csproj) diatur ke true:

    <PropertyGroup>
      <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
    </PropertyGroup>
    
  • Elemen berikut <link> yang berisi rel="preload" ada dalam <head> konten wwwroot/index.html:

    <link rel="preload" id="webassembly" />
    

Untuk informasi selengkapnya, lihat file statis ASP.NET Core Blazor.

Mengatur lingkungan di aplikasi mandiri Blazor WebAssembly

Header Blazor-Environment dan file Properties/launchSettings.json (variabel lingkungan ASPNETCORE_ENVIRONMENT) tidak lagi digunakan untuk mengendalikan lingkungan pada aplikasi mandiri Blazor WebAssembly.

Mulai dari .NET 10, atur lingkungan dengan properti <WasmApplicationEnvironmentName> di file proyek aplikasi (.csproj).

Contoh berikut mengatur lingkungan aplikasi ke Staging:

<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>

Lingkungan defaultnya adalah:

  • Development untuk membangun.
  • Production untuk publikasi.

Untuk informasi selengkapnya, lihat lingkungan ASP.NET Core Blazor.

File konfigurasi boot disisipkan

konfigurasi boot Blazor, yang sebelum rilis .NET 10 ada dalam file bernama blazor.boot.json, telah disebarkan ke dalam skrip dotnet.js. Ini hanya memengaruhi pengembang yang berinteraksi langsung dengan blazor.boot.json file, seperti ketika pengembang:

Saat ini, tidak ada strategi penggantian yang didokumentasikan untuk pendekatan sebelumnya. Jika Anda memerlukan salah satu strategi sebelumnya, buka masalah dokumentasi baru yang menjelaskan skenario Anda menggunakan tautan Buka masalah dokumentasi di bagian bawah salah satu artikel.

Model deklaratif untuk mempertahankan status dari komponen dan layanan

Anda sekarang dapat secara deklaratif menentukan keadaan yang perlu dipertahankan dari komponen dan layanan dengan menggunakan atribut [PersistentState]. Properti publik (public) dengan atribut ini dipertahankan secara otomatis menggunakan layanan PersistentComponentState selama prarendering. Status diambil ketika komponen dirender secara interaktif atau layanan dibuat.

Dalam rilis Blazor sebelumnya, mempertahankan status komponen selama prarender dengan menggunakan layanan PersistentComponentState melibatkan sejumlah besar kode, seperti yang ditunjukkan contoh berikut:

@page "/movies"
@implements IDisposable
@inject IMovieService MovieService
@inject PersistentComponentState ApplicationState

@if (MoviesList == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <QuickGrid Items="MoviesList.AsQueryable()">
        ...
    </QuickGrid>
}

@code {
    public List<Movie>? MoviesList { get; set; }
    private PersistingComponentStateSubscription? persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList), 
            out var movies))
        {
            MoviesList = await MovieService.GetMoviesAsync();
        }
        else
        {
            MoviesList = movies;
        }

        persistingSubscription = ApplicationState.RegisterOnPersisting(() =>
        {
            ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
            return Task.CompletedTask;
        });
    }

    public void Dispose() => persistingSubscription?.Dispose();
}

Kode ini sekarang dapat disederhanakan menggunakan model deklaratif baru:

@page "/movies"
@inject IMovieService MovieService

@if (MoviesList == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <QuickGrid Items="MoviesList.AsQueryable()">
        ...
    </QuickGrid>
}

@code {
    [PersistentState]
    public List<Movie>? MoviesList { get; set; }

    protected override async Task OnInitializedAsync()
    {
        MoviesList ??= await MovieService.GetMoviesAsync();
    }
}

Gunakan public properti karena refleksi digunakan oleh kerangka kerja untuk tugas seperti pemangkasan kode yang tidak digunakan dan pembuatan sumber.

State dapat diserialisasikan untuk beberapa komponen dengan jenis yang sama, dan Anda dapat menetapkan keadaan deklaratif dalam layanan untuk digunakan di seluruh aplikasi dengan memanggil RegisterPersistentService pada penyusun komponen (Razor) dengan jenis layanan kustom dan mode penyajian. Untuk informasi selengkapnya, lihat ASP.NET Core Blazor persistensi status yang telah dirender.

Fitur interop JavaScript baru

Blazor menambahkan dukungan untuk fitur interop berikut JS :

  • Buat instans dari objek JS menggunakan fungsi konstruktor dan dapatkan penunjuk IJSObjectReference/IJSInProcessObjectReference .NET untuk mereferensikan instans tersebut.
  • Membaca atau mengubah nilai JS properti objek, baik properti data maupun aksesor.

Metode asinkron berikut tersedia di IJSRuntime dan IJSObjectReference dengan perilaku cakupan yang sama dengan metode yang ada IJSRuntime.InvokeAsync :

  • InvokeConstructorAsync(string identifier, object?[]? args): Memanggil fungsi konstruktor yang ditentukan JS secara asinkron. Fungsi ini dipanggil dengan new operator. Dalam contoh berikut, jsInterop.TestClass adalah kelas dengan fungsi konstruktor, dan classRef merupakan IJSObjectReference:

    var classRef = await JSRuntime.InvokeConstructorAsync("jsInterop.TestClass", "Blazor!");
    var text = await classRef.GetValueAsync<string>("text");
    var textLength = await classRef.InvokeAsync<int>("getTextLength");
    
  • GetValueAsync<TValue>(string identifier): Membaca nilai properti yang ditentukan JS secara asinkron. Properti tidak boleh hanya menjadi properti set. A JSException dilemparkan jika properti tidak ada. Contoh berikut mengembalikan nilai dari properti data:

    var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>(
      "jsInterop.testObject.num");
    
  • SetValueAsync<TValue>(string identifier, TValue value): Memperbarui nilai properti yang ditentukan JS secara asinkron. Properti tidak boleh hanya menjadi properti get. Jika properti tidak ditentukan pada objek target, properti akan dibuat. JSException dilemparkan jika properti ada tetapi tidak dapat ditulis atau ketika properti baru tidak dapat ditambahkan ke objek. Pada contoh berikut, num dibuat pada testObject dengan nilai 30 jika belum ada:

    await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
    

Overload tersedia untuk setiap metode yang disebutkan sebelumnya yang mengambil argumen CancellationToken atau argumen batas waktu TimeSpan.

Metode sinkron berikut tersedia di IJSInProcessRuntime dan IJSInProcessObjectReference dengan perilaku cakupan yang sama dengan metode yang ada IJSInProcessObjectReference.Invoke :

  • InvokeConstructor(string identifier, object?[]? args): Memanggil fungsi konstruktor yang ditentukan JS secara sinkron. Fungsi ini dipanggil dengan new operator. Dalam contoh berikut, jsInterop.TestClass adalah kelas dengan fungsi konstruktor, dan classRef merupakan IJSInProcessObjectReference:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    var classRef = inProcRuntime.InvokeConstructor("jsInterop.TestClass", "Blazor!");
    var text = classRef.GetValue<string>("text");
    var textLength = classRef.Invoke<int>("getTextLength");
    
  • GetValue<TValue>(string identifier): Membaca nilai properti yang ditentukan JS secara sinkron. Properti tidak boleh hanya menjadi properti set. A JSException dilemparkan jika properti tidak ada. Contoh berikut mengembalikan nilai dari properti data:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    var valueFromDataProperty = inProcRuntime.GetValue<int>(
      "jsInterop.testObject.num");
    
  • SetValue<TValue>(string identifier, TValue value): Memperbarui nilai properti yang ditentukan JS secara sinkron. Properti tidak boleh hanya menjadi properti get. Jika properti tidak ditentukan pada objek target, properti akan dibuat. JSException dilemparkan jika properti ada tetapi tidak dapat ditulis atau ketika properti baru tidak dapat ditambahkan ke objek. Dalam contoh berikut, num dibuat pada testObject dengan nilai 20 jika tidak ada:

    var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
    inProcRuntime.SetValue("jsInterop.testObject.num", 20);
    

Untuk informasi lebih lanjut, lihat bagian berikut dalam artikel Memanggil fungsi JavaScript dari metode .NET:

Blazor WebAssembly pembuatan profil performa dan penghitung diagnostik

Profil kinerja baru dan penghitung diagnostik tersedia untuk aplikasi Blazor WebAssembly. Untuk informasi lebih lanjut, baca artikel berikut:

Memilih untuk menghindari NavigationException selama rendering sisi server statis dengan NavigationManager.NavigateTo

Panggilan NavigationManager.NavigateTo selama penyajian sisi server statis (SSR statis) melempar NavigationException, mengganggu eksekusi sebelum dikonversi ke respons pengalihan. Ini dapat menyebabkan kebingungan selama penelusuran kesalahan dan tidak konsisten dengan perilaku penyajian interaktif, di mana kode setelah NavigateTo terus dijalankan secara normal.

Dalam .NET 10, Anda dapat mengatur properti MSBuild <BlazorDisableThrowNavigationException> ke true dalam file proyek aplikasi untuk menghindari melempar pengecualian selama SSR statis:

<PropertyGroup>
  <BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>

Dengan properti MSBuild diatur, memanggil fungsi NavigationManager.NavigateTo selama SSR statis tidak lagi menghasilkan NavigationException. Sebaliknya, ia bertingkah konsisten dengan penyajian interaktif dengan melakukan navigasi tanpa melemparkan pengecualian. Kode setelah NavigationManager.NavigateTo dijalankan sebelum pengalihan terjadi.

Templat proyek .NET 10 Blazor Web App mengatur properti MSBuild ke true secara default. Sebaiknya aplikasi memperbarui ke .NET 10 menggunakan properti MSBuild baru dan menghindari perilaku sebelumnya.

Jika properti MSBuild digunakan, kode yang mengandalkan penggunaan NavigationException harus diperbarui. Dalam antarmuka pengguna BlazorIdentity default dari templat proyek Blazor Web App sebelum rilis .NET 10, IdentityRedirectManager melempar InvalidOperationException setelah memanggil RedirectTo untuk memastikan bahwa metode tidak dipanggil selama penyajian interaktif. Pengecualian ini dan [DoesNotReturn] atribut sekarang harus dihapus ketika properti MSBuild digunakan. Untuk informasi selengkapnya, lihat Memigrasi dari ASP.NET Core di .NET 9 ke ASP.NET Core di .NET 10.

Blazor router memiliki NotFoundPage parameter

Blazor sekarang menyediakan cara yang ditingkatkan untuk menampilkan halaman "Tidak Ditemukan" saat menavigasi ke halaman yang tidak ada. Anda dapat menentukan halaman yang akan dirender ketika NavigationManager.NotFound (dijelaskan di bagian berikutnya) dipanggil dengan meneruskan jenis halaman ke Router komponen menggunakan NotFoundPage parameter . Fitur ini mendukung perutean, berfungsi pada Middleware Eksekusi Ulang Halaman Kode Status, dan kompatibel bahkan dengan skenario selain Blazor.

Fragmen render NotFound (<NotFound>...</NotFound>) tidak didukung di .NET 10 atau yang lebih baru.

<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>

Template proyek kini menyertakan halaman Blazor secara default. Halaman ini secara otomatis dirender setiap kali NotFound dipanggil di aplikasi Anda, sehingga lebih mudah untuk menangani rute yang hilang dengan pengalaman pengguna yang konsisten.

Untuk informasi selengkapnya, lihat navigasi ASP.NET Core Blazor.

Respons Tidak Ditemukan menggunakan NavigationManager untuk SSR statis dan penyajian interaktif global

Sekarang NavigationManager termasuk NotFound metode untuk menangani skenario di mana sumber daya yang diminta tidak ditemukan selama penyajian sisi server statis (SSR statis) atau penyajian interaktif global:

  • Penyajian sisi server statis (SSR statis): Panggilan NotFound mengatur kode status HTTP ke 404.

  • Penyajian interaktif: Memberi sinyal router Blazor (Router komponen) untuk merender konten Tidak Ditemukan.

  • Streaming rendering: Jika navigasi yang ditingkatkan aktif, streaming rendering merender konten Tidak Ditemukan tanpa memuat ulang halaman. Ketika navigasi yang disempurnakan diblokir, kerangka kerja mengalihkan ke konten Tidak Ditemukan dengan refresh halaman.

Penyajian streaming hanya dapat merender komponen yang memiliki rute, seperti NotFoundPage penugasan (NotFoundPage="...") atau penugasan halaman Middleware untuk Eksekusi Ulang Kode Status (UseStatusCodePagesWithReExecute). DefaultNotFound Konten dengan status 404 ("teks biasa"Not found) tidak memiliki rute, sehingga tidak dapat digunakan selama proses rendering streaming.

NotFound penyajian konten menggunakan hal berikut, terlepas dari apakah respons telah dimulai atau tidak (secara berurutan):

  • Jika NotFoundEventArgs.Path telah diatur, render konten halaman yang ditetapkan.
  • Jika Router.NotFoundPage diatur, tampilkan halaman yang ditetapkan.
  • Halaman Kode Status Eksekusi Ulang Halaman Middleware, jika dikonfigurasi.
  • Tidak ada tindakan jika tidak ada pendekatan sebelumnya yang diadopsi.

Middleware Eksekusi Ulang Halaman Kode Status dengan UseStatusCodePagesWithReExecute diutamakan untuk masalah perutean alamat berbasis browser, seperti URL yang salah yang diketik ke bilah alamat browser atau memilih tautan yang tidak memiliki titik akhir di aplikasi.

Anda dapat menggunakan event NavigationManager.OnNotFound untuk pemberitahuan ketika NotFound dipanggil.

Untuk informasi dan contoh selengkapnya, lihat navigasi ASP.NET Core Blazor.

Dukungan untuk respons "Tidak Ditemukan" di aplikasi tanpa Blazor router

Aplikasi yang menerapkan router kustom dapat menggunakan NotFound. Ada dua cara untuk memberi tahu perender halaman apa yang harus dirender ketika NotFound dipanggil.

Pendekatan yang direkomendasikan yang berfungsi terlepas dari status respons adalah memanggil UseStatusCodePagesWithReExecute. Ketika NotFound dipanggil, middleware merender jalur yang diteruskan ke metode :

app.UseStatusCodePagesWithReExecute(
    "/not-found", createScopeForStatusCodePages: true);

Jika Anda tidak ingin menggunakan UseStatusCodePagesWithReExecute, aplikasi masih dapat mendukung pemrosesan NotFound untuk respons yang telah dimulai. Daftarkan OnNotFoundEvent dalam router dan atur jalur halaman Tidak Ditemukan ke NotFoundEventArgs.Path untuk memberi tahu perender konten apa yang harus dirender ketika NotFound dipanggil.

CustomRouter.razor:

@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Http
@implements IDisposable
@inject NavigationManager NavigationManager

@code {
    protected override void OnInitialized() =>
        NavigationManager.OnNotFound += OnNotFoundEvent;

    [CascadingParameter]
    public HttpContext? HttpContext { get; set; }

    private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
    {
        // Only execute the logic if HTTP response has started
        // because setting NotFoundEventArgs.Path blocks re-execution
        if (HttpContext?.Response.HasStarted == false)
        {
            return;
        }

        e.Path = GetNotFoundRoutePath();
    }

    // Return the path of the Not Found page that you want to display
    private string GetNotFoundRoutePath()
    {
        ...
    }

    public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
}

Jika Anda menggunakan kedua pendekatan di aplikasi, jalur Tidak Ditemukan yang ditentukan dalam OnNotFoundEvent handler lebih diutamakan daripada jalur yang dikonfigurasi di middleware eksekusi ulang.

Metrik dan pelacakan

Rilis ini memperkenalkan metrik komprehensif dan kemampuan pelacakan untuk Blazor aplikasi, memberikan pengamatan terperinci tentang siklus hidup komponen, navigasi, penanganan peristiwa, dan manajemen sirkuit.

Untuk informasi selengkapnya, lihat praktik terbaik performa ASP.NET Core Blazor.

Dukungan penggabung JavaScript

BlazorHasil build tidak kompatibel dengan bundler JavaScript, seperti Gulp, Webpack, dan Rollup. Blazor sekarang dapat menghasilkan output ramah bundler saat penerbitan dengan mengatur WasmBundlerFriendlyBootConfig properti MSBuild ke true.

Untuk informasi selengkapnya, lihat Host dan sebarkan ASP.NET Core Blazor.

Blazor WebAssembly pramuat aset statis dalam Blazor Web Apps

Kami mengganti header <link> dengan komponen ResourcePreloader (<ResourcePreloader />) untuk memuat lebih dahulu aset WebAssembly pada Blazor Web App. Ini memungkinkan konfigurasi jalur dasar aplikasi (<base href="..." />) untuk mengidentifikasi root aplikasi dengan benar.

Menghapus komponen akan menonaktifkan fitur jika aplikasi menggunakan loadBootResource panggilan balik untuk memodifikasi URL.

Templat Blazor Web App mengadopsi fitur secara default di .NET 10, dan aplikasi yang ditingkatkan ke .NET 10 dapat menerapkan fitur dengan menempatkan komponen ResourcePreloader setelah tag URL dasar (<base>) di konten kepala komponen App (App.razor):

<head>
    ...
    <base href="/" />
+   <ResourcePreloader />
    ...
</head>

Untuk informasi selengkapnya, lihat Host dan sebarkan aplikasi Blazor sisi server ASP.NET Core.

Validasi formulir yang disempurnakan

Blazor sekarang telah meningkatkan kemampuan validasi formulir, termasuk dukungan untuk memvalidasi properti objek berlapis dan item koleksi.

Untuk membuat formulir yang divalidasi, gunakan DataAnnotationsValidator komponen di dalam EditForm komponen, sama seperti sebelumnya.

Untuk memilih fitur validasi baru:

  1. AddValidation Panggil metode ekstensi di Program file di mana layanan didaftarkan.
  2. Deklarasikan jenis model formulir dalam file kelas C#, bukan dalam Razor komponen (.razor).
  3. Anotasi tipe model bentuk akar dengan atribut [ValidatableType].

Tanpa mengikuti langkah-langkah sebelumnya, perilaku validasi tetap sama seperti pada rilis .NET sebelumnya.

Contoh berikut menunjukkan pesanan pelanggan dengan validasi formulir yang ditingkatkan (detail dihilangkan untuk singkatnya):

Di Program.cs, panggil AddValidation pada kumpulan layanan untuk mengaktifkan perilaku validasi baru:

builder.Services.AddValidation();

Di kelas Order berikut, atribut [ValidatableType] diperlukan pada jenis model tingkat tertinggi. Jenis lainnya ditemukan secara otomatis. OrderItem dan ShippingAddress tidak ditampilkan demi singkatnya, tetapi validasi tersarang dan koleksi bekerja dengan cara yang sama untuk tipe tersebut jika ditampilkan.

Order.cs:

[ValidatableType]
public class Order
{
    public Customer Customer { get; set; } = new();
    public List<OrderItem> OrderItems { get; set; } = [];
}

public class Customer
{
    [Required(ErrorMessage = "Name is required.")]
    public string? FullName { get; set; }

    [Required(ErrorMessage = "Email is required.")]
    public string? Email { get; set; }

    public ShippingAddress ShippingAddress { get; set; } = new();
}

Dalam OrderPage komponen berikut, komponen DataAnnotationsValidator ada dalam komponen EditForm.

OrderPage.razor:

<EditForm Model="Model">
    <DataAnnotationsValidator />

    <h3>Customer Details</h3>
    <div class="mb-3">
        <label>
            Full Name
            <InputText @bind-Value="Model!.Customer.FullName" />
        </label>
        <ValidationMessage For="@(() => Model!.Customer.FullName)" />
    </div>

    @* ... form continues ... *@
</EditForm>

@code {
    public Order? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    // ... code continues ...
}

Persyaratan untuk mendeklarasikan jenis model di luar Razor komponen (.razor file) disebabkan oleh fakta bahwa fitur validasi baru dan Razor pengkompilasi itu sendiri menggunakan generator sumber. Saat ini, output dari satu generator sumber tidak dapat digunakan sebagai input untuk generator sumber lain.

Dukungan validasi sekarang mencakup:

  • Validasi objek dan koleksi kompleks berlapis sekarang didukung.
    • Ini termasuk aturan validasi yang ditentukan oleh atribut properti, atribut kelas, dan IValidatableObject implementasi.
    • Atribut [SkipValidation] dapat mengecualikan properti atau jenis dari validasi.
  • Validasi sekarang menggunakan implementasi berbasis generator sumber alih-alih implementasi berbasis refleksi untuk meningkatkan performa dan kompatibilitas dengan kompilasi ahead-of-time (AOT).

Komponen DataAnnotationsValidator sekarang memiliki urutan validasi dan perilaku sirkuit pendek yang sama dengan System.ComponentModel.DataAnnotations.Validator. Aturan berikut diterapkan saat memvalidasi instans jenis T:

  1. Properti anggota T divalidasi, termasuk memvalidasi objek berlapis secara rekursif.
  2. Atribut tingkat tipe T divalidasi.
  3. Metode IValidatableObject.Validate dijalankan, jika T mengimplementasikannya.

Jika salah satu langkah sebelumnya menghasilkan kesalahan validasi, langkah-langkah yang tersisa akan dilewati.

Gunakan model validasi dari assembly yang berbeda

Anda dapat memvalidasi formulir dengan model yang ditentukan dalam assembly yang berbeda, seperti pustaka atau proyek .Client, dengan membuat metode di pustaka atau proyek Blazor Web App yang menerima instance .Client sebagai argumen dan memanggil IServiceCollection padanya.

  • Di aplikasi, panggil metode dan AddValidation.

Untuk informasi selengkapnya dan contohnya, lihat validasi formulir ASP.NET Core Blazor.

Cache kustom Blazor dan BlazorCacheBootResources properti MSBuild dihapus

Sekarang setelah semua Blazor file sisi klien ditandai dan di-cache (disimpan sementara) oleh browser, mekanisme penyimpanan sementara kustom Blazor dan properti MSBuild BlazorCacheBootResources telah dihapus dari kerangka kerja ini. Jika file proyek sisi klien berisi properti MSBuild, hapus properti , karena tidak lagi berpengaruh:

- <BlazorCacheBootResources>...</BlazorCacheBootResources>

Untuk informasi selengkapnya, lihat kegagalan cache ASP.NET Core Blazor WebAssembly dan pemeriksaan integritas.

Dukungan API Autentikasi Web (passkey) untuk ASP.NET Core Identity

Dukungan API Autentikasi Web (WebAuthn), yang dikenal luas sebagai kode akses, adalah metode autentikasi modern dan tahan phishing yang meningkatkan keamanan dan pengalaman pengguna dengan memanfaatkan kriptografi kunci publik dan autentikasi berbasis perangkat. ASP.NET Core Identity sekarang mendukung autentikasi kode akses berdasarkan standar WebAuthn dan FIDO2. Fitur ini memungkinkan pengguna untuk masuk tanpa kata sandi, menggunakan metode autentikasi berbasis perangkat yang aman, seperti biometrik atau kunci keamanan.

Blazor Web App Templat proyek menyediakan manajemen kunci akses dan fungsionalitas masuk secara langsung.

Untuk informasi lebih lanjut, baca artikel berikut:

Persistensi keadaan sirkuit

Selama rendering sisi server, Blazor Web Apps sekarang dapat mempertahankan status sesi (sirkuit) pengguna ketika koneksi ke server hilang untuk jangka waktu yang lama atau dijeda secara proaktif, selama refresh halaman penuh tidak dipicu. Ini memungkinkan pengguna untuk melanjutkan sesi mereka tanpa kehilangan pekerjaan yang tidak disimpan dalam skenario berikut:

  • Pembatasan kecepatan tab browser
  • Pengguna perangkat seluler beralih aplikasi
  • Gangguan jaringan
  • Manajemen sumber daya proaktif (menjeda sirkuit tidak aktif)
  • Navigasi yang disempurnakan

Untuk informasi selengkapnya, lihat ASP.NET Core Blazor manajemen status sisi server.

Hot Reload untuk Blazor WebAssembly dan .NET di WebAssembly

SDK dimigrasikan ke tujuan umum Hot Reload untuk skenario WebAssembly. Ada properti MSBuild baru WasmEnableHotReload yang true secara default untuk konfigurasi Debug (Configuration == "Debug") yang memungkinkan Hot Reload.

Untuk konfigurasi lain dengan nama konfigurasi kustom, atur nilai ke true dalam file proyek aplikasi untuk mengaktifkan Hot Reload:

<PropertyGroup>
  <WasmEnableHotReload>true</WasmEnableHotReload>
</PropertyGroup>

Untuk menonaktifkan Hot Reload untuk konfigurasi Debug, atur nilai ke false:

<PropertyGroup>
  <WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>

Pendaftaran pekerja layanan PWA yang diperbarui untuk mencegah masalah penembolokan

Pendaftaran pekerja layanan dalam Blazor templat proyek Aplikasi Web Progresif (PWA) sekarang menyertakan updateViaCache: 'none' pilihan, yang mencegah masalah cache selama pembaruan pekerja layanan.

- navigator.serviceWorker.register('service-worker.js');
+ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });

Opsi memastikan bahwa:

  • Browser tidak menggunakan versi cache skrip pekerja layanan.
  • Pembaruan service worker diterapkan secara andal tanpa terblokir oleh cache HTTP.
  • Aplikasi PWA dapat memperbarui pekerja layanan mereka dengan lebih terprediksi.

Ini mengatasi masalah caching yang dapat mencegah pembaruan service worker diterapkan dengan benar, yang sangat penting bagi PWA yang mengandalkan service worker untuk fungsionalitas offline.

Kami merekomendasikan menggunakan opsi yang diatur ke none di semua PWAs, termasuk yang menargetkan .NET 9 atau versi sebelumnya.

Ekstensibilitas serialisasi untuk status komponen persisten

Terapkan serializer kustom dengan PersistentComponentStateSerializer<T>. Tanpa serializer kustom terdaftar, serialisasi kembali ke serialisasi JSON yang ada.

Serializer kustom terdaftar dalam file aplikasi Program . Dalam contoh berikut, CustomUserSerializer terdaftar untuk jenis :TUser

builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>, 
    CustomUserSerializer>();

Jenis secara otomatis dipertahankan dan dipulihkan dengan serializer kustom:

[PersistentState] 
public User? CurrentUser { get; set; } = new();

OwningComponentBase sekarang mengimplementasikan IAsyncDisposable

OwningComponentBase sekarang termasuk dukungan untuk pembuangan asinkron, meningkatkan manajemen sumber daya. Ada metode baru DisposeAsync dan DisposeAsyncCore dengan metode yang diperbarui Dispose untuk menangani pembuangan sinkron dan asinkron dari cakupan layanan.

Komponen baru InputHidden untuk menangani bidang input tersembunyi dalam formulir

Komponen baru InputHidden menyediakan bidang input tersembunyi untuk menyimpan nilai string.

Dalam contoh berikut, bidang input tersembunyi dibuat untuk properti formulir Parameter . Saat formulir dikirimkan, nilai bidang tersembunyi ditampilkan:

<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="InputHidden Example">
    <InputHidden id="hidden" @bind-Value="Parameter" />
    <button type="submit">Submit</button>
</EditForm>

@if (submitted)
{
    <p>Hello @Parameter!</p>
}

@code {
    private bool submitted;

    [SupplyParameterFromForm] 
    public string Parameter { get; set; } = "stranger";

    private void Submit() => submitted = true;
}

Dukungan status komponen persisten untuk navigasi yang ditingkatkan

Blazor sekarang mendukung penanganan status komponen persisten selama navigasi yang ditingkatkan. Status yang dipertahankan selama navigasi yang ditingkatkan dapat dibaca oleh komponen interaktif pada halaman.

Secara default, status komponen persisten hanya dimuat oleh komponen interaktif saat awalnya dimuat di halaman. Ini mencegah status penting, seperti data dalam formulir web yang diedit, agar tidak ditimpa jika peristiwa navigasi tambahan yang lebih canggih ke halaman yang sama terjadi setelah komponen dimuat.

Jika data bersifat baca-saja dan tidak sering berubah, pilih untuk mengizinkan pembaruan selama navigasi yang ditingkatkan dengan menetapkan AllowUpdates = true pada atribut [PersistentState]. Ini berguna untuk skenario seperti menampilkan data cache yang mahal untuk diambil tetapi tidak sering berubah. Contoh berikut menunjukkan penggunaan AllowUpdates data prakiraan cuaca:

[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }

protected override async Task OnInitializedAsync()
{
    Forecasts ??= await ForecastService.GetForecastAsync();
}

Untuk melewati pemulihan status pada saat pra-penyajian, atur RestoreBehavior ke SkipInitialValue:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }

Untuk melewati status pemulihan selama koneksi ulang, atur RestoreBehavior ke SkipLastSnapshot. Ini dapat berguna untuk memastikan data baru setelah koneksi ulang:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }

Panggil PersistentComponentState.RegisterOnRestoring untuk mendaftarkan panggilan balik untuk mengontrol secara imperatif bagaimana status dipulihkan, mirip dengan bagaimana PersistentComponentState.RegisterOnPersisting memberikan kontrol penuh tentang bagaimana status dipertahankan.

Blazor WebAssembly menghormati pengaturan budaya UI saat ini

Di .NET 9 atau yang lebih lama, aplikasi Blazor WebAssembly mandiri memuat sumber daya globalisasi UI berdasarkan CultureInfo.DefaultThreadCurrentCulture. Jika Anda ingin memuat data globalisasi tambahan untuk budaya pelokalan yang ditentukan oleh CultureInfo.DefaultThreadCurrentUICulture, tingkatkan aplikasi ke .NET 10 atau lebih baru.

Blazor Hybrid

Bagian ini menjelaskan fitur baru untuk Blazor Hybrid.

.NET MAUI Blazor Hybrid baru dengan artikel dan sampel Blazor Web App dan ASP.NET Core Identity

Artikel baru dan aplikasi sampel telah ditambahkan untuk .NET MAUIBlazor Hybrid dan Aplikasi Web menggunakan ASP.NET Core Identity.

Untuk informasi selengkapnya, lihat sumber daya berikut ini:

SignalR

Bagian ini menjelaskan fitur baru untuk SignalR.

Antarmuka Pemrograman Aplikasi Minimalis

Bagian ini menjelaskan fitur baru untuk API Minimal.

Memperlakukan string kosong dalam pengiriman formulir sebagai null untuk jenis nilai nullable.

Saat menggunakan [FromForm] atribut dengan objek kompleks di API Minimal, nilai string kosong dalam posting formulir sekarang dikonversi menjadi null bukan menyebabkan kegagalan penguraian. Perilaku ini cocok dengan logika pemrosesan untuk posting formulir yang tidak terkait dengan objek kompleks di API Minimal.

using Microsoft.AspNetCore.Http;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/todo", ([FromForm] Todo todo) => TypedResults.Ok(todo));

app.Run();

public class Todo
{
  public int Id { get; set; }
  public DateOnly? DueDate { get; set; } // Empty strings map to `null`
  public string Title { get; set; }
  public bool IsCompleted { get; set; }
}

Terima kasih kepada @nvmkpk untuk berkontribusi pada perubahan ini!

Dukungan validasi dalam API Minimal

Dukungan untuk validasi di API Minimal sekarang tersedia. Fitur ini memungkinkan Anda meminta validasi data yang dikirim ke titik akhir API Anda. Mengaktifkan validasi memungkinkan runtime ASP.NET Core melakukan validasi apa pun yang ditentukan pada:

  • Query
  • Header
  • Isi dari permintaan

Validasi didefinisikan menggunakan atribut di DataAnnotations namespace. Pengembang menyesuaikan perilaku sistem validasi dengan:

Jika validasi gagal, runtime mengembalikan respons 400 Permintaan Buruk dengan detail kesalahan validasi.

Mengaktifkan dukungan validasi bawaan untuk API Minimal

Aktifkan dukungan validasi bawaan untuk API Minimal dengan memanggil AddValidation metode ekstensi untuk mendaftarkan layanan yang diperlukan dalam kontainer layanan untuk aplikasi Anda:

builder.Services.AddValidation();

Implementasi secara otomatis menemukan tipe yang didefinisikan dalam handler API Minimal atau sebagai tipe dasar dari tipe yang ada pada handler API Minimal. Filter titik akhir melakukan validasi pada jenis ini dan ditambahkan untuk setiap titik akhir.

Validasi dapat dinonaktifkan untuk titik akhir tertentu dengan menggunakan DisableValidation metode ekstensi, seperti dalam contoh berikut:

app.MapPost("/products",
    ([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
        => TypedResults.Ok(productId))
    .DisableValidation();

Note

Beberapa peningkatan kecil dan perbaikan telah dilakukan pada generator validasi API Minimal yang diperkenalkan di ASP.NET Core untuk .NET 10. Untuk mendukung peningkatan di masa mendatang, API pemecah masalah validasi yang mendasar sekarang ditandai sebagai eksperimental. API tingkat AddValidation atas dan filter validasi bawaan tetap stabil dan tidak bereksperimen.

Validasi dengan tipe catatan

API minimal juga mendukung validasi dengan jenis catatan C#. Jenis rekaman dapat divalidasi menggunakan atribut dari System.ComponentModel.DataAnnotations namespace, mirip dengan kelas. Contohnya:

public record Product(
    [Required] string Name,
    [Range(1, 1000)] int Quantity);

Saat menggunakan jenis rekaman sebagai parameter di titik akhir API Minimal, atribut validasi secara otomatis diterapkan dengan cara yang sama seperti jenis kelas:

app.MapPost("/products", (Product product) =>
{
    // Endpoint logic here
    return TypedResults.Ok(product);
});

Integrasi Validasi API minimal dengan IProblemDetailsService

Respon kesalahan dari logika validasi untuk API Minimal sekarang dapat disesuaikan menggunakan sebuah implementasi IProblemDetailsService yang disediakan dalam koleksi layanan aplikasi (kontainer Injeksi Dependensi). Ini memungkinkan respons kesalahan yang lebih konsisten dan spesifik pengguna.

Dukungan untuk Acara Server-Sent (SSE)

ASP.NET Core sekarang mendukung pengembalian hasil ServerSentEvents menggunakan TypedResults.ServerSentEvents API. Fitur ini didukung di API Minimal dan aplikasi berbasis pengontrol.

Server-Sent Events adalah teknologi pendorongan server yang memungkinkan server mengirim aliran pesan peristiwa ke klien melalui satu koneksi HTTP. Dalam .NET pesan peristiwa diwakili sebagai objek SseItem<T>, yang mungkin berisi jenis peristiwa, ID, dan payload data jenis T.

Kelas TypedResults memiliki metode statis baru yang disebut ServerSentEvents yang dapat digunakan untuk mengembalikan hasil ServerSentEvents. Parameter pertama untuk metode ini adalah IAsyncEnumerable<SseItem<T>> yang mewakili aliran pesan peristiwa yang akan dikirim ke klien.

Contoh berikut mengilustrasikan cara menggunakan TypedResults.ServerSentEvents API untuk mengembalikan aliran peristiwa denyut jantung sebagai objek JSON kepada klien:

app.MapGet("/json-item", (CancellationToken cancellationToken) =>
{
    async IAsyncEnumerable<HeartRateRecord> GetHeartRate(
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var heartRate = Random.Shared.Next(60, 100);
            yield return HeartRateRecord.Create(heartRate);
            await Task.Delay(2000, cancellationToken);
        }
    }

    return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken),
                                                  eventType: "heartRate");
});

Untuk informasi selengkapnya, lihat:

  • Server-Sent Event pada MDN (Mozilla Developer Network).
  • Aplikasi sampel API minimal menggunakan TypedResults.ServerSentEvents API untuk mengembalikan aliran peristiwa denyut jantung sebagai string, ServerSentEvents, dan objek JSON ke klien.
  • Aplikasi sampel API pengontrol menggunakan TypedResults.ServerSentEvents API untuk mengembalikan aliran peristiwa denyut jantung sebagai string, ServerSentEvents, dan objek JSON ke klien.

API Validasi dipindahkan ke Microsoft. Extensions.Validation

API validasi telah dipindahkan ke paket namespace Microsoft.Extensions.Validation dan NuGet. Perubahan ini membuat API dapat digunakan di luar skenario HTTP ASP.NET Core. API dan perilaku publik tetap tidak berubah—hanya paket dan namespace yang berbeda. Proyek yang ada tidak memerlukan perubahan kode, karena referensi lama dialihkan ke implementasi baru.

Validasi yang disempurnakan untuk kelas dan rekaman

Atribut validasi sekarang dapat diterapkan ke kelas dan rekaman dengan pembuatan kode dan perilaku validasi yang konsisten. Peningkatan ini meningkatkan fleksibilitas saat merancang model menggunakan rekaman di aplikasi ASP.NET Core.

Kontribusi komunitas: Terima kasih kepada @marcominerva!

OpenAPI

Bagian ini menjelaskan fitur baru untuk OpenAPI.

Dukungan OpenAPI 3.1

ASP.NET Core telah menambahkan dukungan untuk menghasilkan dokumen OpenAPI versi 3.1 di .NET 10. Terlepas dari peningkatan versi minor, OpenAPI 3.1 merupakan pembaruan signifikan untuk spesifikasi OpenAPI, khususnya dengan dukungan penuh untuk draf Skema JSON 2020-12.

Beberapa perubahan yang akan Anda lihat dalam dokumen OpenAPI yang dihasilkan meliputi:

  • Jenis nullable tidak lagi memiliki properti nullable: true dalam skema.
  • Alih-alih properti nullable: true, mereka memiliki kata kunci type yang nilainya adalah array yang menyertakan null sebagai salah satu jenisnya.
  • Properti atau parameter yang didefinisikan sebagai C# int atau long sekarang muncul di dokumen OpenAPI yang dihasilkan tanpa type: integer bidang dan memiliki bidang yang pattern membatasi nilai ke digit. Ini terjadi ketika properti NumberHandling di JsonSerializerOptions diatur ke AllowReadingFromString, default untuk aplikasi Web ASP.NET Core. Untuk mengaktifkan C# int dan long agar dapat diwakili sebagai type: integer dalam dokumen OpenAPI, atur properti NumberHandling ke Strict.

Dengan fitur ini, versi OpenAPI default untuk dokumen yang dihasilkan3.1. Versi dapat diubah dengan secara eksplisit mengatur properti OpenApiVersion dari OpenApiOptions dalam configureOptions parameter delegasi AddOpenApi:

builder.Services.AddOpenApi(options =>
{
    options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1;
});

Saat membuat dokumen OpenAPI pada waktu build, versi OpenAPI dapat dipilih dengan mengatur --openapi-version di OpenApiGenerateDocumentsOptions item MSBuild:

<PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
    <!-- Configure build-time OpenAPI generation to produce an OpenAPI 3.1 document. -->
    <OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>

Dukungan OpenAPI 3.1 terutama ditambahkan dalam PR berikut.

Perubahan besar OpenAPI 3.1

Dukungan untuk OpenAPI 3.1 memerlukan pembaruan ke pustaka OpenAPI.NET yang mendasar ke versi utama baru, 2.0. Versi baru ini memiliki beberapa perubahan signifikan dari versi sebelumnya. Perubahan yang merusak dapat memengaruhi aplikasi yang memiliki transformator dokumen, operasi, atau skema. Perubahan besar dalam iterasi ini meliputi berikut ini:

  • Entitas dalam dokumen OpenAPI, seperti operasi dan parameter, ditik sebagai antarmuka. Implementasi konkret ada untuk varian yang sebaris dan yang direferensikan dari sebuah entitas. Misalnya, IOpenApiSchema dapat berupa OpenApiSchema sebaris atau OpenApiSchemaReference yang menunjuk ke skema yang ditentukan di tempat lain dalam dokumen.
  • Properti Nullable telah dihapus dari jenis OpenApiSchema. Untuk menentukan apakah jenis dapat di-null, evaluasi apakah properti OpenApiSchema.Type ditetapkan JsonSchemaType.Null.

Salah satu perubahan paling signifikan adalah bahwa kelas OpenApiAny telah dihilangkan demi menggunakan JsonNode secara langsung. Transformator yang menggunakan OpenApiAny perlu diperbarui untuk menggunakan JsonNode. Perbedaan berikut menunjukkan perubahan transformator skema dari .NET 9 ke .NET 10:

options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
    if (context.JsonTypeInfo.Type == typeof(WeatherForecast))
    {
-       schema.Example = new OpenApiObject
+       schema.Example = new JsonObject
        {
-           ["date"] = new OpenApiString(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")),
+           ["date"] = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"),
-           ["temperatureC"] = new OpenApiInteger(0),
+           ["temperatureC"] = 0,
-           ["temperatureF"] = new OpenApiInteger(32),
+           ["temperatureF"] = 32,
-           ["summary"] = new OpenApiString("Bracing"),
+           ["summary"] = "Bracing",
        };
    }
    return Task.CompletedTask;
});

Perhatikan bahwa perubahan ini diperlukan bahkan ketika hanya mengonfigurasi versi OpenAPI ke 3.0.

OpenAPI di YAML

ASP.NET sekarang mendukung penyajian dokumen OpenAPI yang dihasilkan dalam format YAML. YAML bisa lebih ringkas daripada JSON dengan menghilangkan kurung kurawal dan tanda kutip ketika bisa disimpulkan. YAML juga mendukung string multibaris, yang dapat berguna untuk deskripsi panjang.

Untuk mengonfigurasi aplikasi untuk melayani dokumen OpenAPI yang dihasilkan dalam format YAML, tentukan titik akhir dalam panggilan MapOpenApi dengan akhiran ".yaml" atau ".yml", seperti yang ditunjukkan dalam contoh berikut:

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi("/openapi/{documentName}.yaml");
}

Dukungan untuk:

  • YAML saat ini hanya tersedia untuk OpenAPI yang dilayani dari titik akhir OpenAPI.
  • Membuat dokumen OpenAPI dalam format YAML pada waktu build akan ditambahkan dalam pratinjau mendatang.

Lihat PR ini yang menambahkan dukungan untuk menyajikan dokumen OpenAPI yang dihasilkan dalam format YAML.

Deskripsi respons pada ProducesResponseType untuk pengendali API

ProducesAttribute, , ProducesResponseTypeAttributedan ProducesDefaultResponseTypeAttribute sekarang menerima parameter string opsional, Description, yang mengatur deskripsi respons:

[HttpGet(Name = "GetWeatherForecast")]
[ProducesResponseType<IEnumerable<WeatherForecast>>(StatusCodes.Status200OK,
    Description = "The weather forecast for the next 5 days.")]
public IEnumerable<WeatherForecast> Get()
{

Data OpenAPI yang dihasilkan:

"responses": {
  "200": {
    "description": "The weather forecast for the next 5 days.",
    "content": {

Fungsionalitas ini didukung di pengontrol API dan API Minimal. Untuk API Minimal, Description properti diatur dengan benar bahkan ketika jenis atribut dan jenis pengembalian yang disimpulkan tidak sama persis.

Kontribusi komunitas (dotnet/aspnetcore #58193) oleh Sander ten Brinke.

Isi komentar dokumen XML ke dalam dokumen OpenAPI

ASP.NET Core pembuatan dokumen OpenAPI sekarang akan menyertakan metadata dari komentar dokumen XML pada definisi metode, kelas, dan anggota dalam dokumen OpenAPI. Anda harus mengaktifkan komentar dokumen XML di file proyek Anda untuk menggunakan fitur ini. Anda dapat melakukan ini dengan menambahkan properti berikut ke file proyek Anda:

  <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

Pada build-time, paket OpenAPI akan memanfaatkan generator sumber untuk menemukan komentar XML di perakitan aplikasi saat ini dan referensi proyek apa pun dan memancarkan kode sumber untuk memasukkannya ke dalam dokumen melalui transformator dokumen OpenAPI.

Perhatikan bahwa proses build C# tidak menangkap komentar dokumentasi XML yang ditempatkan pada ekspresi lambda, jadi untuk menggunakan komentar dokumentasi XML guna menambahkan metadata ke titik akhir API Minimal, Anda harus menentukan handler titik akhir sebagai fungsi, menempatkan komentar dokumentasi XML pada fungsi tersebut, lalu mereferensikan fungsi tersebut dari metode MapXXX. Misalnya, untuk menggunakan komentar dokumen XML untuk menambahkan metadata ke titik akhir API Minimal yang awalnya didefinisikan sebagai ekspresi lambda:

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

Ubah panggilan MapGet untuk mereferensikan metode:

app.MapGet("/hello", Hello);

Tentukan metode Hello dengan komentar dokumen XML:

static partial class Program
{
    /// <summary>
    /// Sends a greeting.
    /// </summary>
    /// <remarks>
    /// Greeting a person by their name.
    /// </remarks>
    /// <param name="name">The name of the person to greet.</param>
    /// <returns>A greeting.</returns>
    public static string Hello(string name)
    {
        return $"Hello, {name}!";
    }
}

Dalam contoh sebelumnya, metode Hello ditambahkan ke kelas Program, tetapi Anda dapat menambahkannya ke kelas apa pun dalam proyek Anda.

Contoh sebelumnya mengilustrasikan komentar dokumen xml <summary>, <remarks>, dan <param> XML. Untuk informasi selengkapnya tentang komentar dokumen XML, termasuk semua tag yang didukung, lihat dokumentasi C#.

Karena fungsionalitas inti disediakan melalui generator sumber, fungsi ini dapat dinonaktifkan dengan menambahkan MSBuild berikut ke file proyek Anda.

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.2.*" GeneratePathProperty="true" />
</ItemGroup>

<Target Name="DisableCompileTimeOpenApiXmlGenerator" BeforeTargets="CoreCompile">
  <ItemGroup>
    <Analyzer Remove="$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll" />
  </ItemGroup>
</Target>

Proses generator sumber memproses file XML yang disertakan dalam properti AdditionalFiles. Untuk menambahkan (atau menghapus), sumber mengubah properti sebagai berikut:

<Target Name="AddXmlSources" BeforeTargets="CoreCompile">
  <ItemGroup>
    <AdditionalFiles Include="$(PkgSome_Package)/lib/net10.0/Some.Package.xml" />
  </ItemGroup>
</Target>

Microsoft. AspNetCore.OpenApi ditambahkan ke templat API web ASP.NET Core (Native AOT)

Templat proyek ASP.NET Core Web API (Native AOT) (nama pendek webapiaot) sekarang menyertakan dukungan untuk pembuatan dokumen OpenAPI menggunakan paket Microsoft.AspNetCore.OpenApi secara default. Dukungan ini dinonaktifkan dengan menggunakan --no-openapi bendera saat membuat proyek baru.

Kontribusi komunitas (dotnet/aspnetcore #60337) oleh Sander ten Brinke.

Dukungan untuk IOpenApiDocumentProvider dalam kontainer DI.

ASP.NET Core di .NET 10 mendukung IOpenApiDocumentProvider dalam kontainer injeksi dependensi (DI). Pengembang dapat menyuntikkan IOpenApiDocumentProvider ke aplikasi mereka dan menggunakannya untuk mengakses dokumen OpenAPI. Pendekatan ini berguna untuk mengakses dokumen OpenAPI di luar konteks permintaan HTTP, seperti di layanan latar belakang atau middleware kustom.

Sebelumnya, menjalankan logika startup aplikasi tanpa meluncurkan server HTTP dapat dilakukan dengan menggunakan HostFactoryResolver dengan implementasi no-op IServer . Fitur baru menyederhanakan proses ini dengan menyediakan API yang disederhanakan yang terinspirasi oleh AspireIDistributedApplicationPublisher, yang merupakan bagian Aspiredari kerangka kerja untuk hosting dan penerbitan aplikasi terdistribusi.

Untuk informasi selengkapnya, lihat dotnet/aspnetcore #61463.

Penyempurnaan pada generator komentar XML

Pembuatan komentar XML mengelola tipe yang lebih kompleks dalam .NET 10 dengan lebih baik daripada versi .NET sebelumnya.

  • Ini menghasilkan komentar XML yang akurat dan lengkap untuk berbagai jenis yang lebih luas.
  • Ini menangani skenario yang lebih kompleks.
  • Ini dengan mulus melewati pemrosesan untuk jenis kompleks yang menyebabkan kesalahan kompilasi dalam versi sebelumnya.

Peningkatan ini mengubah mode kegagalan untuk skenario tertentu dari kesalahan build menjadi metadata yang hilang.

Selain itu, pemrosesan komentar dokumen XML sekarang dapat dikonfigurasi untuk mengakses komentar XML di rakitan lain. Ini berguna untuk menghasilkan dokumentasi untuk jenis yang ditentukan di luar rakitan saat ini, seperti jenis ProblemDetails di namespace Microsoft.AspNetCore.Http.

Konfigurasi ini dilakukan dengan arahan dalam file build proyek. Contoh berikut menunjukkan cara mengonfigurasi generator komentar XML untuk mengakses komentar XML untuk jenis di rakitan Microsoft.AspNetCore.Http, yang mencakup kelas ProblemDetails.

<Target Name="AddOpenApiDependencies" AfterTargets="ResolveReferences">
  <ItemGroup>
  <!-- Include XML documentation from Microsoft.AspNetCore.Http.Abstractions
    to get metadata for ProblemDetails -->
    <AdditionalFiles
          Include="@(ReferencePath->'
            %(RootDir)%(Directory)%(Filename).xml')"
          Condition="'%(ReferencePath.Filename)' ==
           'Microsoft.AspNetCore.Http.Abstractions'"
          KeepMetadata="Identity;HintPath" />
  </ItemGroup>
</Target>

Kami berharap untuk menyertakan komentar XML dari sekumpulan rakitan yang dipilih dalam kerangka kerja bersama dalam pratinjau di masa mendatang untuk menghindari kebutuhan konfigurasi ini dalam banyak kasus.

Penanganan terpadu ID dokumentasi di generator komentar OPENAPI XML

Komentar dokumentasi XML dari rakitan yang direferensikan digabungkan dengan benar bahkan ketika ID dokumentasinya menyertakan akhiran jenis pengembalian. Akibatnya, semua komentar XML yang valid disertakan dengan andal dalam dokumentasi OpenAPI yang dihasilkan, meningkatkan akurasi dan kelengkapan dokumentasi untuk API menggunakan rakitan yang direferensikan.

Parameter data enum formulir menggunakan tipe enum aktual di OpenAPI

Parameter data formulir dalam tindakan pengontrol MVC sekarang menghasilkan metadata OpenAPI menggunakan jenis enum aktual alih-alih default ke string.

Kontribusi komunitas: Terima kasih kepada @ascott18!

Dukungan untuk menghasilkan OpenApiSchemas dalam transformator

Pengembang sekarang dapat menghasilkan skema untuk jenis C# menggunakan logika yang sama dengan ASP.NET Core pembuatan dokumen OpenAPI dan menambahkannya ke dokumen OpenAPI. Skema kemudian dapat direferensikan dari tempat lain dalam dokumen OpenAPI.

Konteks yang diteruskan ke transformator dokumen, operasi, dan skema mencakup metode baru GetOrCreateSchemaAsync yang dapat digunakan untuk menghasilkan skema untuk jenis. Metode ini juga memiliki parameter opsional ApiParameterDescription untuk menentukan metadata tambahan untuk skema yang dihasilkan.

Untuk mendukung penambahan skema ke dokumen OpenAPI, Document properti telah ditambahkan ke konteks transformator Operasi dan Skema. Ini memungkinkan transformator apa pun untuk menambahkan skema ke dokumen OpenAPI menggunakan metode dokumen AddComponent .

Example

Untuk menggunakan fitur ini dalam transformator dokumen, operasi, atau skema, buat skema menggunakan GetOrCreateSchemaAsync metode yang disediakan dalam konteks dan tambahkan ke dokumen OpenAPI menggunakan metode dokumen AddComponent .

builder.Services.AddOpenApi(options =>
{
    options.AddOperationTransformer(async (operation, context, cancellationToken) =>
    {
        // Generate schema for error responses
        var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
        context.Document?.AddComponent("Error", errorSchema);

        operation.Responses ??= new OpenApiResponses();
        // Add a "4XX" response to the operation with the newly created schema
        operation.Responses["4XX"] = new OpenApiResponse
        {
            Description = "Bad Request",
            Content = new Dictionary<string, OpenApiMediaType>
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchemaReference("Error", context.Document)
                }
            }
        };
    });
});

Transformer operasi OpenAPI yang spesifik untuk titik akhir

Transformator operasi khusus titik akhir memungkinkan penyesuaian dokumentasi OpenAPI yang terperinci untuk titik akhir rute individual. Fitur ini memungkinkan pengembang untuk menyesuaikan metadata Swagger/OpenAPI dan deskripsi berdasarkan per tindakan atau per rute, meningkatkan ekstensibilitas untuk skenario API tingkat lanjut.

Untuk detail implementasi dan sampel kode, lihat Menyesuaikan dokumen OpenAPI.

Tingkatkan Microsoft. OpenApi ke 2.0.0

Pustaka Microsoft.OpenApi yang digunakan untuk pembuatan dokumen OpenAPI di ASP.NET Core telah ditingkatkan ke versi 2.0.0 (GA).

Memutus perubahan dalam 2.0.0

Perubahan yang mengganggu berikut diperkenalkan dalam pratinjau rilis dan tetap ada dalam versi GA. Ini terutama memengaruhi pengguna yang menerapkan transformator dokumen, operasi, atau skema:

Dengan pembaruan ke versi GA, tidak ada perubahan yang dapat menyebabkan gangguan yang diharapkan dalam pembuatan dokumen OpenAPI.

Peningkatan Pembuatan Skema OpenAPI

Model jenis nullable menggunakan oneOf dalam skema OpenAPI

Generasi skema OpenAPI untuk tipe Nullable diperbaiki dengan menggunakan pola oneOf bukannya properti nullable untuk jenis kompleks dan koleksi. Implementasinya:

  • oneOf menggunakan oneOf dengan dan skema tipe sebenarnya untuk tipe kompleks yang dapat bernilai null dalam skema permintaan dan respons.
  • Mendeteksi kemampuan null untuk parameter, properti, dan tipe pengembalian menggunakan refleksi dan NullabilityInfoContext.
  • Menghapus jenis null dari skema komponen untuk menghindari duplikasi.

Perbaikan dan penyempurnaan resolusi referensi skema

Rilis ini meningkatkan penanganan skema JSON untuk pembuatan dokumen OpenAPI dengan menyelesaikan referensi skema JSON relatif ($ref) dengan benar dalam dokumen skema akar.

Sertakan deskripsi properti sebagai saudara kandung $ref dalam skema OpenAPI

Sebelum .NET 10, ASP.NET Core membuang deskripsi pada properti yang ditentukan dengan $ref dalam dokumen OpenAPI yang dihasilkan karena OpenAPI v3.0 tidak mengizinkan properti yang bersandingan dengan $ref dalam definisi skema. OpenAPI 3.1 sekarang memungkinkan Anda menyertakan deskripsi bersama $ref. RC1 menambahkan dukungan untuk menyertakan deskripsi properti sebagai saudara kandung $ref dalam skema OpenAPI yang dihasilkan.

Ini adalah kontribusi komunitas. Terima kasih @desjoerd!

Menambahkan metadata dari komentar XML pada [AsParameters] jenis ke skema OpenAPI

Pembuatan skema OpenAPI sekarang memproses komentar XML pada properti [AsParameters] kelas parameter untuk mengekstrak metadata untuk dokumentasi.

Mengecualikan metode HTTP yang tidak diketahui dari OpenAPI

Pembuatan skema OpenAPI sekarang mengecualikan metode HTTP yang tidak diketahui dari dokumen OpenAPI yang dihasilkan. Metode kueri, yang merupakan metode HTTP standar tetapi tidak dikenali oleh OpenAPI, sekarang dikecualikan dengan baik dari dokumen OpenAPI yang dihasilkan.

Ini adalah kontribusi komunitas. Terima kasih @martincostello!

Memperbaiki deskripsi untuk badan permintaan JSON Patch

Pembuatan skema OpenAPI untuk operasi JSON Patch sekarang menerapkan tipe media application/json-patch+json dengan benar untuk badan permintaan yang menggunakan JSON Patch. Ini memastikan bahwa dokumen OpenAPI yang dihasilkan secara akurat mencerminkan jenis media yang diharapkan untuk operasi Patch JSON. Selain itu, isi permintaan JSON Patch memiliki skema terperinci yang menjelaskan struktur dokumen JSON Patch, termasuk operasi yang dapat dilakukan.

Ini adalah kontribusi komunitas. Terima kasih @martincostello!

Menggunakan budaya invarian untuk pembuatan dokumen OpenAPI

Pembuatan dokumen OpenAPI sekarang menggunakan budaya yang invarian untuk memformat angka dan tanggal dalam dokumen OpenAPI yang dihasilkan. Ini memastikan bahwa dokumen yang dihasilkan konsisten dan tidak bervariasi berdasarkan pengaturan budaya server.

Ini adalah kontribusi komunitas. Terima kasih @martincostello!

Autentikasi dan otorisasi

Metrik autentikasi dan otorisasi

Metrik telah ditambahkan untuk peristiwa autentikasi dan otorisasi tertentu di ASP.NET Core. Dengan perubahan ini, Anda sekarang dapat memperoleh metrik untuk peristiwa berikut:

  • Authentication:
    • Durasi permintaan yang terautentikasi
    • Jumlah tantangan
    • Melarang penghitung
    • Jumlah masuk pengguna
    • Jumlah tanda keluar
  • Authorization:
    • Jumlah permintaan yang memerlukan otorisasi

Gambar di bawah ini menunjukkan contoh metrik durasi untuk permintaan yang terautentikasi di Aspire dashboard.

Durasi permintaan terautentikasi di Aspire dasbor

Untuk informasi selengkapnya, lihat metrik bawaan ASP.NET Core.

metrik ASP.NET Core Identity

Observabilitas ASP.NET Core Identity telah ditingkatkan pada .NET 10 dengan metrik. Metrik adalah penghitung, histogram, dan pengukur yang menyediakan pengukuran rangkaian waktu perilaku sistem atau aplikasi.

Misalnya, gunakan metrik ASP.NET Core Identity baru untuk mengamati:

  • Manajemen pengguna: Pembuatan pengguna baru, perubahan kata sandi, dan penetapan peran.
  • Penanganan login/sesi: Upaya masuk, masuk, keluar, dan pengguna menggunakan autentikasi dua faktor.

Metrik baru ada dalam meter Microsoft.AspNetCore.Identity:

  • aspnetcore.identity.user.create.duration
  • aspnetcore.identity.user.update.duration
  • aspnetcore.identity.user.delete.duration
  • aspnetcore.identity.user.check_password_attempts
  • aspnetcore.identity.user.generated_tokens
  • aspnetcore.identity.user.verify_token_attempts
  • aspnetcore.identity.sign_in.authenticate.duration
  • aspnetcore.identity.sign_in.check_password_attempts
  • aspnetcore.identity.sign_in.sign_ins
  • aspnetcore.identity.sign_in.sign_outs
  • aspnetcore.identity.sign_in.two_factor_clients_remembered
  • aspnetcore.identity.sign_in.two_factor_clients_forgotten

Untuk informasi selengkapnya tentang menggunakan metrik di ASP.NET Core, lihat metrik ASP.NET Core.

Secara default, permintaan yang tidak diautentikasi dan tidak sah yang dibuat ke titik akhir API yang diketahui dilindungi oleh cookie autentikasi sekarang menghasilkan respons 401 dan 403, alih-alih mengalihkan ke URI login atau akses ditolak.

Perubahan ini sangat diminta, karena mengalihkan permintaan yang tidak diautentikasi ke halaman login biasanya tidak masuk akal untuk titik akhir API yang biasanya mengandalkan kode status 401 dan 403 daripada pengalihan HTML untuk mengkomunikasikan kegagalan autentikasi.

Titik Akhir API yang diketahui diidentifikasi menggunakan antarmuka baruIApiEndpointMetadata, dan metadata yang mengimplementasikan antarmuka baru telah ditambahkan secara otomatis ke yang berikut:

  • [ApiController] Endpoint
  • Titik akhir API minimal yang membaca badan permintaan JSON atau menulis respons JSON
  • Titik akhir menggunakan TypedResults tipe pengembalian
  • SignalR Endpoint

Ketika IApiEndpointMetadata ada, cookie handler autentikasi sekarang mengembalikan kode status HTTP yang sesuai (401 untuk permintaan yang tidak diautentikasi, 403 untuk permintaan terlarang) alih-alih mengalihkan.

Jika Anda ingin mencegah perilaku baru ini, dan selalu mengalihkan ke URI untuk masuk dan akses ditolak untuk permintaan yang tidak diautentikasi atau tidak sah terlepas dari titik akhir target, Anda dapat melakukan override pada peristiwa RedirectToLogin dan RedirectToAccessDenied sebagai berikut:

builder.Services.AddAuthentication()
    .AddCookie(options =>
    {
        options.Events.OnRedirectToLogin = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };

        options.Events.OnRedirectToAccessDenied = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };
    });

Untuk informasi selengkapnya tentang perubahan signifikan ini, lihat pengumuman perubahan penting dari ASP.NET Core.

Miscellaneous

Bagian ini menjelaskan berbagai fitur baru dalam .NET 10.

Mengonfigurasi penghentian diagnostik penangan pengecualian

Opsi konfigurasi baru telah ditambahkan ke middleware penangan pengecualian ASP.NET Core guna mengendalikan output diagnostik: ExceptionHandlerOptions.SuppressDiagnosticsCallback. Panggilan balik ini diteruskan konteks tentang permintaan dan pengecualian, memungkinkan Anda menambahkan logika yang menentukan apakah middleware harus menulis log pengecualian dan telemetri lainnya.

Pengaturan ini berguna ketika Anda tahu pengecualian bersifat sementara atau telah ditangani oleh middleware penanganan pengecualian, dan Anda tidak ingin log kesalahan yang ditulis ke platform observabilitas Anda.

Perilaku default middleware juga telah berubah: ia tidak lagi menulis diagnostik pengecualian untuk pengecualian yang ditangani oleh IExceptionHandler. Berdasarkan umpan balik pengguna, pencatatan pengecualian yang ditangani pada level kesalahan sering kali tidak diinginkan saat IExceptionHandler.TryHandleAsync mengembalikan true.

Anda dapat kembali ke perilaku sebelumnya dengan mengonfigurasi SuppressDiagnosticsCallback:

app.UseExceptionHandler(new ExceptionHandlerOptions
{
    SuppressDiagnosticsCallback = context => false;
});

Untuk informasi lebih lanjut tentang perubahan besar ini, lihat https://github.com/aspnet/Announcements/issues/524.

Dukungan untuk Domain Top-Level .localhost

Domain .localhost tingkat atas (TLD) didefinisikan dalam RFC2606 dan RFC6761 sebagai dicadangkan untuk tujuan pengujian dan tersedia bagi pengguna untuk digunakan secara lokal seperti nama domain lainnya. Ini berarti menggunakan nama seperti myapp.localhost secara lokal yang mengarah ke alamat loopback IP diizinkan dan diharapkan sesuai dengan RFC-RFC ini. Selain itu, browser evergreen modern sudah secara otomatis menyelesaikan *.localhost nama apa pun ke alamat loopback IP (127.0.0.1/::1), secara efektif menjadikannya alias untuk layanan apa pun yang sudah dihosting di localhost pada komputer lokal, yaitu, layanan apa pun yang merespons http://localhost:6789 juga akan merespons http://anything-here.localhost:6789, dengan asumsi tidak ada verifikasi atau penegakan nama host spesifik lebih lanjut yang sedang dilakukan oleh layanan.

ASP.NET Core telah diperbarui di .NET 10 Pratinjau 7 untuk mendukung TLD .localhost dengan lebih baik, sehingga sekarang dapat dengan mudah digunakan saat membuat dan menjalankan aplikasi ASP.NET Core di lingkungan pengembangan lokal Anda. Memiliki aplikasi yang berbeda yang berjalan secara lokal dapat diselesaikan melalui nama yang berbeda memungkinkan pemisahan yang lebih baik dari beberapa aset situs web terkait nama domain, misalnya cookie, dan memudahkan untuk mengidentifikasi aplikasi mana yang Anda telusuri melalui nama yang ditampilkan di bilah alamat browser.

server HTTP bawaan ASP.NET Core, , sekarang akan memperlakukan dengan benar setiap nama yang ditetapkan melalui mekanisme konfigurasi titik akhir yang didukung sebagai alamat loopback lokal dan dengan demikian mengikat ke alamat tersebut daripada semua alamat eksternal (yaitu, mengikat ke daripada ). Ini termasuk "applicationUrl" properti dalam profil peluncuran yang dikonfigurasi dalam file launchSettings.json, dan ASPNETCORE_URLS variabel lingkungan. Ketika dikonfigurasi untuk mendengarkan di alamat .localhost, Kestrel akan mencatat pesan informasi untuk alamat .localhostdanlocalhost, untuk memperjelas bahwa kedua nama dapat digunakan.

Meskipun browser web secara otomatis mengubah *.localhost nama ke alamat loopback lokal, aplikasi lain mungkin memperlakukan *.localhost nama sebagai nama domain biasa dan mencoba menyelesaikannya melalui tumpukan DNS yang sesuai. Jika konfigurasi DNS Anda tidak dapat menyelesaikan nama *.localhost ke alamat, hal tersebut akan gagal terhubung. Anda dapat terus menggunakan nama reguler localhost untuk merujuk aplikasi Anda saat tidak berada di browser web.

Sertifikat pengembangan ASP.NET Core HTTPS (termasuk perintah dotnet dev-certs https) telah diperbarui untuk memastikan sertifikat valid untuk digunakan dengan nama domain *.dev.localhost. Setelah menginstal .NET 10 Pratinjau SDK 7, percayai sertifikat pengembang baru dengan menjalankan dotnet dev-certs https --trust di baris perintah untuk memastikan sistem Anda dikonfigurasi untuk mempercayai sertifikat baru.

Sertifikat mencantumkan nama *.dev.localhost sebagai Nama Alternatif Subjek (SAN) daripada *.localhost karena menggunakan sertifikat wildcard untuk nama domain tingkat atas tidak valid.

Templat proyek untuk ASP.NET Core Kosong (web) dan Blazor Web App (blazor) telah diperbarui dengan opsi baru yang ketika ditentukan mengonfigurasi proyek yang dibuat untuk menggunakan akhiran nama domain .dev.localhost, menggabungkannya dengan nama proyek untuk memungkinkan aplikasi dijelajahi di alamat seperti https://myapp.dev.localhost:5036:

$ dotnet new web -n MyApp --localhost-tld
The template "ASP.NET Core Empty" was created successfully.

Processing post-creation actions...
Restoring D:\src\MyApp\MyApp.csproj:
Restore succeeded.

$ cd .\MyApp\
$ dotnet run --launch-profile https
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://myapp.dev.localhost:7099
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7099/
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://myapp.dev.localhost:5036
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5036/
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\src\local\10.0.1xx\MyApp

Dukungan deserialisasi Json+PipeReader di API MVC dan Minimal

PR: https://github.com/dotnet/aspnetcore/pull/62895

Lihat https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview7/libraries.md#pipereader-support-for-json-serializer

MVC, API Minimal, dan HttpRequestJsonExtensions.ReadFromJsonAsync metode semuanya telah diperbarui untuk menggunakan dukungan Json+PipeReader baru tanpa memerlukan perubahan kode apa pun dari aplikasi.

Untuk sebagian besar aplikasi, penambahan dukungan ini tidak berpengaruh pada perilaku mereka. Namun, jika aplikasi menggunakan kustom JsonConverter, ada kemungkinan pengonversi tidak menangani Utf8JsonReader.HasValueSequence dengan benar. Ini dapat mengakibatkan data dan kesalahan yang hilang, seperti ArgumentOutOfRangeException, saat deserialisasi.

Solusi cepat (terutama jika Anda tidak memiliki JsonConverter kustom yang digunakan) adalah mengatur "Microsoft.AspNetCore.UseStreamBasedJsonParsing"AppContext beralih ke "true". Ini harus menjadi solusi sementara, dan JsonConverter harus diperbarui untuk mendukung HasValueSequence.

Untuk memperbaiki implementasi JsonConverter, ada solusi cepat yang mengalokasikan sebuah array menggunakan ReadOnlySequence dan akan tampak seperti contoh berikut:

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
    // previous code
}

Ada juga solusi yang lebih rumit (tetapi efisien), yang melibatkan jalur kode terpisah untuk penanganan ReadOnlySequence.

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    if (reader.HasValueSequence)
    {
        reader.ValueSequence;
        // ReadOnlySequence optimized path
    }
    else
    {
        reader.ValueSpan;
        // ReadOnlySpan optimized path
    }
}

Pengeluaran otomatis dari kumpulan memori

Kumpulan memori yang digunakan oleh Kestrel, IIS, dan HTTP.sys sekarang secara otomatis mengeluarkan blok memori ketika aplikasi diam atau di bawah beban yang lebih sedikit. Fitur ini berjalan secara otomatis dan tidak perlu diaktifkan atau dikonfigurasi secara manual.

Mengapa pengeluaran memori penting

Sebelumnya, memori yang dialokasikan oleh kumpulan akan tetap dicadangkan, bahkan ketika tidak digunakan. Fitur ini merilis memori kembali ke sistem saat aplikasi diam untuk jangka waktu tertentu. Pengeluaran ini mengurangi penggunaan memori secara keseluruhan dan membantu aplikasi tetap responsif di bawah berbagai beban kerja.

Menggunakan metrik pengusiran memori

Metrik telah ditambahkan ke kumpulan memori default yang digunakan oleh implementasi server kami. Metrik baru berada di bawah nama "Microsoft.AspNetCore.MemoryPool".

Untuk informasi tentang metrik dan cara menggunakannya, lihat metrik ASP.NET Core.

Mengelola kumpulan memori

Selain menggunakan kumpulan memori lebih efisien dengan mengusir blok memori yang tidak perlu, .NET 10 meningkatkan pengalaman membuat kumpulan memori. Ini dilakukan dengan menyediakan IMemoryPoolFactory dan implementasi MemoryPoolFactory. Ini membuat implementasi tersedia untuk aplikasi Anda melalui injeksi dependensi.

Contoh kode berikut menunjukkan layanan latar belakang sederhana yang menggunakan implementasi pabrik kumpulan memori bawaan untuk membuat kumpulan memori. Kumpulan ini mendapat manfaat dari fitur pengeluaran otomatis:

public class MyBackgroundService : BackgroundService
{
    private readonly MemoryPool<byte> _memoryPool;

    public MyBackgroundService(IMemoryPoolFactory<byte> factory)
    {
        _memoryPool = factory.Create();
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(20, stoppingToken);
                // do work that needs memory
                var rented = _memoryPool.Rent(100);
                rented.Dispose();
            }
            catch (OperationCanceledException)
            {
                return;
            }
        }
    }
}

Untuk menggunakan pabrik kumpulan memori Anda sendiri, buat kelas yang mengimplementasikan IMemoryPoolFactory dan mendaftarkannya dengan injeksi dependensi, seperti contoh berikut. Kumpulan memori yang dibuat dengan cara ini tidak mendapat manfaat dari fitur pengeluaran otomatis kecuali Anda menerapkan logika pengeluaran serupa di pabrik kustom Anda:

services.AddSingleton<IMemoryPoolFactory<byte>,
CustomMemoryPoolFactory>();

public class CustomMemoryPoolFactory : IMemoryPoolFactory<byte>
{
    public MemoryPool<byte> Create()
    {
        // Return a custom MemoryPool implementation
        // or the default, as is shown here.
        return MemoryPool<byte>.Shared;
    }
}

Deskriptor keamanan yang dapat disesuaikan untuk HTTP.sys

Anda sekarang dapat menentukan pendeskripsi keamanan kustom untuk antrean permintaan HTTP.sys. Properti RequestQueueSecurityDescriptor baru pada HttpSysOptions memungkinkan kontrol yang lebih terperinci atas hak akses untuk antrean permintaan. Kontrol terperinci ini memungkinkan Anda menyesuaikan keamanan dengan kebutuhan aplikasi Anda.

Apa yang dapat Anda lakukan dengan properti baru

Antrean permintaan di HTTP.sys adalah struktur tingkat kernel yang untuk sementara menyimpan permintaan HTTP masuk hingga aplikasi Anda siap memprosesnya. Dengan menyesuaikan deskriptor keamanan, Anda dapat mengizinkan atau menolak akses pengguna atau grup tertentu ke antrean permintaan. Ini berguna dalam skenario di mana Anda ingin membatasi atau mendelegasikan penanganan permintaan HTTP.sys di tingkat sistem operasi.

Cara menggunakan properti baru

Properti RequestQueueSecurityDescriptor hanya berlaku saat membuat antrean permintaan baru. Properti tidak akan memengaruhi daftar antrian permintaan yang sudah ada. Untuk menggunakan properti ini, atur ke GenericSecurityDescriptor instans saat mengonfigurasi server HTTP.sys Anda.

Misalnya, kode berikut memungkinkan semua pengguna yang diautentikasi tetapi menolak tamu:

using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.AspNetCore.Server.HttpSys;

// Create a new security descriptor
var securityDescriptor = new CommonSecurityDescriptor(isContainer: false, isDS: false, sddlForm: string.Empty);

// Create a discretionary access control list (DACL)
var dacl = new DiscretionaryAcl(isContainer: false, isDS: false, capacity: 2);
dacl.AddAccess(
    AccessControlType.Allow,
    new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
    -1,
    InheritanceFlags.None,
    PropagationFlags.None
);
dacl.AddAccess(
    AccessControlType.Deny,
    new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null),
    -1,
    InheritanceFlags.None,
    PropagationFlags.None
);

// Assign the DACL to the security descriptor
securityDescriptor.DiscretionaryAcl = dacl;

// Configure HTTP.sys options
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseHttpSys(options =>
{
    options.RequestQueueSecurityDescriptor = securityDescriptor;
});

Untuk informasi selengkapnya, lihat implementasi server web HTTP.sys di ASP.NET Core.

Dukungan yang lebih baik untuk menguji aplikasi dengan pernyataan tingkat atas

.NET 10 sekarang memiliki dukungan yang lebih baik untuk menguji aplikasi yang menggunakan pernyataan tingkat top. Sebelumnya pengembang harus menambahkan public partial class Program secara manual ke file Program.cs sehingga proyek pengujian dapat mereferensikan Program class. public partial class Program diperlukan karena fitur pernyataan tingkat atas di C# 9 menghasilkan Program class yang dinyatakan sebagai internal.

Dalam .NET 10, generator source digunakan untuk menghasilkan deklarasi public partial class Program jika programmer tidak menyatakannya secara eksplisit. Selain itu, penganalisis ditambahkan untuk mendeteksi kapan public partial class Program dinyatakan secara eksplisit dan menyarankan pengembang untuk menghapusnya.

Image

PR berikut berkontribusi pada fitur ini:

Implementasi JSON Patch baru dengan System.Text.Json

Patch JSON:

  • Adalah format standar untuk menjelaskan perubahan yang akan diterapkan ke dokumen JSON.
  • Didefinisikan dalam RFC 6902 dan banyak digunakan dalam API RESTful untuk melakukan pembaruan parsial pada sumber daya JSON.
  • Mewakili urutan operasi (misalnya, Tambahkan, Hapus, Ganti, Pindahkan, Salin, Uji) yang dapat diterapkan untuk mengubah dokumen JSON.

Di aplikasi web, JSON Patch umumnya digunakan dalam operasi PATCH untuk melakukan pembaruan parsial sumber daya. Daripada mengirim seluruh sumber daya untuk pembaruan, klien dapat mengirim dokumen Patch JSON yang hanya berisi perubahan. Patching mengurangi ukuran payload dan meningkatkan efisiensi.

Rilis ini memperkenalkan implementasi baru Microsoft.AspNetCore.JsonPatch berdasarkan serialisasi System.Text.Json. Fitur ini:

  • Selaras dengan praktik .NET modern dengan memanfaatkan pustaka System.Text.Json, yang dioptimalkan untuk .NET.
  • Memberikan peningkatan performa dan pengurangan penggunaan memori dibandingkan dengan implementasi berbasis warisan Newtonsoft.Json.

Tolok ukur berikut membandingkan performa implementasi baru System.Text.Json dengan implementasi warisan Newtonsoft.Json .

Scenario Implementation Mean Memori yang Dialokasikan
Tolok Ukur Aplikasi Newtonsoft.JsonPatch 271.924 μs 25 KB
System.Text.JsonPatch 1.584 μs 3 KB
Tolok Ukur Deserialisasi Newtonsoft.JsonPatch 19.261 μs 43 KB
System.Text.JsonPatch 7.917 μs 7 KB

Tolok ukur ini menyoroti perolehan performa yang signifikan dan mengurangi penggunaan memori dengan implementasi baru.

Notes:

  • Implementasi baru bukanlah pengganti yang dapat langsung digunakan untuk implementasi lama. Secara khusus, implementasi baru tidak mendukung jenis dinamis, misalnya, ExpandoObject.
  • Standar Patch JSON memiliki risiko keamanan yang melekat. Karena risiko ini melekat pada standar JSON Patch, implementasi baru tidak mencoba mengurangi risiko keamanan yang melekat. Ini adalah tanggung jawab pengembang untuk memastikan bahwa dokumen Patch JSON aman untuk diterapkan ke objek target. Untuk informasi selengkapnya, lihat bagian Mitigasi Risiko Keamanan .

Usage

Untuk mengaktifkan dukungan Patch JSON dengan System.Text.Json, instal paket NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson.

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease

Paket ini menyediakan JsonPatchDocument<T> kelas untuk mewakili dokumen Patch JSON untuk objek jenis T dan logika kustom untuk menserialisasikan dan mendeserialisasi dokumen JSON Patch menggunakan System.Text.Json. Metode kunci kelas JsonPatchDocument<T> adalah ApplyTo, yang menerapkan operasi patch ke objek target jenis T.

Contoh berikut menunjukkan cara menggunakan ApplyTo metode untuk menerapkan dokumen Patch JSON ke objek.

Contoh: Menerapkan JsonPatchDocument

Contoh berikut menunjukkan:

  1. Operasi add, replace, dan remove .
  2. Operasi pada properti berlapis.
  3. Menambahkan item baru ke array.
  4. Menggunakan Pengonversi Enum String JSON dalam dokumen JSON Patch.
// Original object
var person = new Person {
  FirstName = "John",
  LastName = "Doe",
  Email = "johndoe@gmail.com",
  PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
  Address = new Address
  {
    Street = "123 Main St",
    City = "Anytown",
    State = "TX"
  }
};

// Raw JSON Patch document
var jsonPatch = """
[
  { "op": "replace", "path": "/FirstName", "value": "Jane" },
  { "op": "remove", "path": "/Email"},
  { "op": "add", "path": "/Address/ZipCode", "value": "90210" },
  {
    "op": "add",
    "path": "/PhoneNumbers/-",
    "value": { "Number": "987-654-3210", "Type": "Work" }
  }
]
""";

// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

// Apply the JSON Patch document
patchDoc!.ApplyTo(person);

// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

// Output:
// {
//   "firstName": "Jane",
//   "lastName": "Doe",
//   "address": {
//     "street": "123 Main St",
//     "city": "Anytown",
//     "state": "TX",
//     "zipCode": "90210"
//   },
//   "phoneNumbers": [
//     {
//       "number": "123-456-7890",
//       "type": "Mobile"
//     },
//     {
//       "number": "987-654-3210",
//       "type": "Work"
//     }
//   ]
// }

Metode ApplyTo ini umumnya mengikuti konvensi dan opsi System.Text.Json untuk memproses JsonPatchDocument, termasuk perilaku yang dikontrol oleh opsi berikut:

  • NumberHandling: Apakah properti numerik ditafsirkan dari string.
  • PropertyNameCaseInsensitive: Apakah nama properti peka huruf besar/kecil.

Perbedaan utama antara System.Text.Json dan implementasi baru JsonPatchDocument<T> :

  • Jenis runtime objek target, bukan jenis yang dideklarasikan, menentukan patch properti ApplyTo mana.
  • System.Text.Json deserialisasi bergantung pada jenis yang dinyatakan untuk mengidentifikasi properti yang memenuhi syarat.

Contoh: Menerapkan JsonPatchDocument dengan penanganan kesalahan

Ada berbagai kesalahan yang dapat terjadi saat menerapkan dokumen Patch JSON. Misalnya, objek target mungkin tidak memiliki properti yang ditentukan, atau nilai yang ditentukan mungkin tidak kompatibel dengan jenis properti.

JSON Patch juga mendukung operasi test. Operasi test memeriksa apakah nilai yang ditentukan sama dengan properti target, dan jika tidak, mengembalikan kesalahan.

Contoh berikut menunjukkan cara menangani kesalahan ini dengan anggun.

Important

Objek yang diteruskan ke metode ApplyTo dimodifikasi langsung di tempatnya. Pemanggil bertanggung jawab untuk membuang perubahan ini jika ada operasi yang gagal.

// Original object
var person = new Person {
  FirstName = "John",
  LastName = "Doe",
  Email = "johndoe@gmail.com"
};

// Raw JSON Patch document
var jsonPatch = """
[
  { "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
  { "op": "test", "path": "/FirstName", "value": "Jane" },
  { "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";

// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

// Apply the JSON Patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
    {
        errors ??= new ();
        var key = jsonPatchError.AffectedObject.GetType().Name;
        if (!errors.ContainsKey(key))
        {
            errors.Add(key, new string[] { });
        }
        errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
    });
if (errors != null)
{
    // Print the errors
    foreach (var error in errors)
    {
        Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
    }
}

// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

// Output:
// Error in Person: The current value 'John' at path 'FirstName' is not equal 
// to the test value 'Jane'.
// {
//   "firstName": "John",
//   "lastName": "Smith",              <<< Modified!
//   "email": "janedoe@gmail.com",     <<< Modified!
//   "phoneNumbers": []
// }

Mengurangi risiko keamanan

Saat menggunakan paket Microsoft.AspNetCore.JsonPatch.SystemTextJson, sangat penting untuk memahami dan mengurangi potensi risiko keamanan. Bagian berikut menguraikan risiko keamanan yang diidentifikasi yang terkait dengan Patch JSON dan memberikan mitigasi yang direkomendasikan untuk memastikan penggunaan paket yang aman.

Important

Ini bukan daftar ancaman yang lengkap. Pengembang aplikasi harus melakukan tinjauan model ancaman mereka sendiri untuk menentukan daftar komprehensif khusus aplikasi dan membuat mitigasi yang sesuai kebutuhan. Misalnya, aplikasi yang mengekspos koleksi ke operasi patch harus mempertimbangkan potensi serangan kompleksitas algoritma jika operasi tersebut menyisipkan atau menghapus elemen di awal koleksi.

Dengan menjalankan model ancaman komprehensif untuk aplikasi mereka sendiri dan mengatasi ancaman yang diidentifikasi saat mengikuti mitigasi yang direkomendasikan di bawah ini, konsumen paket ini dapat mengintegrasikan fungsionalitas JSON Patch ke dalam aplikasi mereka sambil meminimalkan risiko keamanan.

Konsumen paket ini dapat mengintegrasikan fungsionalitas JSON Patch ke dalam aplikasi mereka sambil meminimalkan risiko keamanan, termasuk:

  • Jalankan model ancaman komprehensif untuk aplikasi mereka sendiri.
  • Mengatasi ancaman yang diidentifikasi.
  • Ikuti mitigasi yang direkomendasikan di bagian berikut.
Penolakan Layanan (DoS) melalui amplifikasi memori
  • Skenario: Klien berbahaya mengirimkan copy operasi yang menduplikasi grafik objek besar beberapa kali, yang mengarah ke konsumsi memori yang berlebihan.
  • Dampak: Potensi kondisi Out-Of-Memory (OOM), menyebabkan gangguan layanan.
  • Mitigation:
    • Validasi dokumen JSON Patch masuk untuk ukuran dan struktur sebelum memanggil ApplyTo.
    • Validasi harus spesifik untuk aplikasi, tetapi contoh validasi dapat terlihat mirip dengan yang berikut ini:
public void Validate(JsonPatchDocument<T> patch)
{
    // This is just an example. It's up to the developer to make sure that
    // this case is handled properly, based on the app's requirements.
    if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
        > MaxCopyOperationsCount)
    {
        throw new InvalidOperationException();
    }
}
Pengacauan Logika Bisnis
  • Skenario: Operasi patch dapat memanipulasi bidang dengan invarian implisit, (misalnya, bendera internal, ID, atau bidang komputasi), melanggar batasan bisnis.
  • Dampak: Masalah integritas data dan perilaku aplikasi yang tidak diinginkan.
  • Mitigation:
    • Gunakan objek POCO dengan properti yang ditentukan secara eksplisit yang aman untuk dimodifikasi.
    • Hindari mengekspos properti sensitif atau kritis keamanan di objek target.
    • Jika tidak ada objek POCO yang digunakan, validasi objek yang di-patch setelah menerapkan operasi untuk memastikan aturan bisnis dan invarian tidak dilanggar.
Autentikasi dan otorisasi
  • Skenario: Klien yang tidak diautentikasi atau tidak sah mengirim permintaan Patch JSON berbahaya.
  • Dampak: Akses tidak sah untuk memodifikasi data sensitif atau mengganggu perilaku aplikasi.
  • Mitigation:
    • Lindungi titik akhir yang menerima permintaan JSON Patch dengan mekanisme autentikasi dan otorisasi yang tepat.
    • Batasi akses ke klien atau pengguna tepercaya dengan izin yang sesuai.

Mendeteksi apakah URL bersifat lokal menggunakan RedirectHttpResult.IsLocalUrl

Gunakan metode pembantu RedirectHttpResult.IsLocalUrl(url) baru untuk mendeteksi apakah URL bersifat lokal. URL dianggap lokal jika berikut ini benar:

  • Ini tidak memiliki host atau bagian otoritas .
  • Jalur absolutnya adalah .

URL yang menggunakan jalur virtual "~/" juga lokal.

IsLocalUrl berguna untuk memvalidasi URL sebelum mengalihkannya untuk mencegah serangan pengalihan terbuka .

if (RedirectHttpResult.IsLocalUrl(url))
{
    return Results.LocalRedirect(url);
}

Terima kasih @martincostello atas kontribusi ini!

Perubahan mendasar

Gunakan artikel di Perubahan yang Mengganggu dalam .NET untuk menemukan perubahan yang mengganggu yang mungkin berlaku saat memperbarui aplikasi ke versi .NET yang lebih baru.