Bagikan melalui


Apa yang baru dalam ASP.NET Core 9.0

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

Artikel ini telah diperbarui untuk Pratinjau .NET 9 6.

Blazor

Bagian ini menjelaskan fitur baru untuk Blazor.

.NET MAUIBlazor Hybrid dan templat solusi Aplikasi Web

Templat solusi baru memudahkan untuk membuat .NET MAUI aplikasi klien asli dan Blazor web yang memiliki UI yang sama. Templat ini menunjukkan cara membuat aplikasi klien yang memaksimalkan penggunaan kembali kode dan target Android, iOS, Mac, Windows, dan Web.

Fitur utama templat ini meliputi:

  • Kemampuan untuk memilih Blazor mode render interaktif untuk aplikasi web.
  • Pembuatan otomatis proyek yang sesuai, termasuk Blazor Aplikasi Web (rendering Otomatis Interaktif global) dan aplikasi .NET MAUIBlazor Hybrid .
  • Proyek yang dibuat menggunakan pustaka kelas bersama Razor (RCL) untuk mempertahankan komponen UI Razor .
  • Kode sampel disertakan yang menunjukkan cara menggunakan injeksi dependensi untuk menyediakan implementasi antarmuka yang berbeda untuk Blazor Hybrid aplikasi dan Blazor Aplikasi Web.

Untuk memulai, instal .NET 9 SDK dan instal .NET MAUI beban kerja, yang berisi templat:

dotnet workload install maui

Buat solusi dari templat proyek di shell perintah menggunakan perintah berikut:

dotnet new maui-blazor-web

Templat juga tersedia di Visual Studio.

Catatan

Saat ini, pengecualian terjadi jika Blazor mode penyajian ditentukan pada tingkat per halaman/komponen. Untuk informasi selengkapnya, lihat BlazorWebView memerlukan cara untuk mengaktifkan penggantian ResolveComponentForRenderMode (dotnet/aspnetcore #51235).

Untuk informasi selengkapnya, lihat Membuat .NET MAUIBlazor Hybrid aplikasi dengan Blazor Aplikasi Web.

Pengoptimalan pengiriman aset statis

MapStaticAssets adalah middleware baru yang membantu mengoptimalkan pengiriman aset statis di aplikasi ASP.NET Core apa pun, termasuk Blazor aplikasi.

Untuk informasi selengkapnya, lihat salah satu sumber daya berikut ini:

Mendeteksi lokasi penyajian, interaktivitas, dan mode render yang ditetapkan saat runtime

Kami telah memperkenalkan API baru yang dirancang untuk menyederhanakan proses kueri status komponen saat runtime. API ini menyediakan kemampuan berikut:

  • Tentukan lokasi eksekusi komponen saat ini: Ini dapat sangat berguna untuk penelusuran kesalahan dan mengoptimalkan performa komponen.
  • Periksa apakah komponen berjalan di lingkungan interaktif: Ini dapat membantu komponen yang memiliki perilaku yang berbeda berdasarkan interaktivitas lingkungan mereka.
  • Ambil mode render yang ditetapkan untuk komponen: Memahami mode render dapat membantu mengoptimalkan proses penyajian dan meningkatkan performa keseluruhan komponen.

Untuk informasi selengkapnya, lihat mode render ASP.NET CoreBlazor.

Pengalaman koneksi ulang sisi server yang disempurnakan:

Penyempurnaan berikut telah dilakukan pada pengalaman koneksi ulang sisi server default:

  • Saat pengguna menavigasi kembali ke aplikasi dengan sirkuit yang terputus, koneksi ulang dicoba segera daripada menunggu durasi interval koneksi ulang berikutnya. Ini meningkatkan pengalaman pengguna saat menavigasi ke aplikasi di tab browser yang telah tidur.

  • Ketika upaya koneksi ulang mencapai server tetapi server telah merilis sirkuit, refresh halaman terjadi secara otomatis. Ini mencegah pengguna harus menyegarkan halaman secara manual jika kemungkinan akan menghasilkan koneksi ulang yang berhasil.

  • Waktu koneksi ulang menggunakan strategi backoff komputasi. Secara default, beberapa upaya koneksi ulang pertama terjadi berturut-turut dengan cepat tanpa interval coba lagi sebelum penundaan komputasi diperkenalkan di antara upaya. Anda dapat menyesuaikan perilaku interval coba lagi dengan menentukan fungsi untuk menghitung interval coba lagi, seperti yang ditunjukkan contoh backoff eksponensial berikut:

    Blazor.start({
      circuit: {
        reconnectionOptions: {
          retryIntervalMilliseconds: (previousAttempts, maxRetries) => 
            previousAttempts >= maxRetries ? null : previousAttempts * 1000
        },
      },
    });
    
  • Gaya antarmuka pengguna koneksi ulang default telah dimodernisasi.

Untuk informasi selengkapnya, lihat panduan ASP.NET CoreBlazorSignalR.

Serialisasi status autentikasi yang disederhanakan untuk Blazor Web Apps

API baru memudahkan untuk menambahkan autentikasi ke Aplikasi Web yang sudah ada Blazor . Saat Anda membuat Aplikasi Web baru Blazor dengan autentikasi menggunakan Akun Individual dan Anda mengaktifkan interaktivitas berbasis WebAssembly, proyek menyertakan kustom AuthenticationStateProvider di proyek server dan klien.

Penyedia ini mengalirkan status autentikasi pengguna ke browser. Mengautentikasi di server daripada klien memungkinkan aplikasi mengakses status autentikasi selama pra-penyajian Blazor WebAssembly dan sebelum runtime diinisialisasi.

Implementasi kustom menggunakan layanan Status Komponen Persisten (PersistentComponentState) untuk membuat serialisasi status autentikasi ke dalam komentar HTML dan membacanya kembali dari WebAssembly untuk membuat instans baruAuthenticationState. AuthenticationStateProvider

Ini berfungsi dengan baik jika Anda telah memulai dari Blazor templat proyek Aplikasi Web dan memilih opsi Akun Individual, tetapi banyak kode untuk mengimplementasikan diri Anda atau menyalin jika Anda mencoba menambahkan autentikasi ke proyek yang ada. Sekarang ada API, yang sekarang menjadi bagian Blazor dari templat proyek Aplikasi Web, yang dapat dipanggil di server dan proyek klien untuk menambahkan fungsionalitas ini:

  • AddAuthenticationStateSerialization: Menambahkan layanan yang diperlukan untuk membuat serialisasi status autentikasi di server.
  • AddAuthenticationStateDeserialization: Menambahkan layanan yang diperlukan untuk mendeserialisasi status autentikasi di browser.

Secara default, API hanya menserialisasikan nama sisi server dan klaim peran untuk akses di browser. Opsi dapat diteruskan ke AddAuthenticationStateSerialization untuk menyertakan semua klaim.

Untuk informasi selengkapnya, lihat bagian berikut dari artikel **:

Menambahkan halaman penyajian sisi server statis (SSR) ke Aplikasi Web interaktif Blazor global

Dengan rilis .NET 9, sekarang lebih sederhana untuk menambahkan halaman SSR statis ke aplikasi yang mengadopsi interaktivitas global.

Pendekatan ini hanya berguna ketika aplikasi memiliki halaman tertentu yang tidak dapat bekerja dengan Server interaktif atau penyajian WebAssembly. Misalnya, adopsi pendekatan ini untuk halaman yang bergantung pada pembacaan/penulisan HTTP cookiedan hanya dapat berfungsi dalam siklus permintaan/respons alih-alih penyajian interaktif. Untuk halaman yang berfungsi dengan penyajian interaktif, Anda tidak boleh memaksanya untuk menggunakan penyajian SSR statis, karena kurang efisien dan kurang responsif bagi pengguna akhir.

Tandai halaman komponen apa pun Razor dengan atribut baru [ExcludeFromInteractiveRouting] yang ditetapkan dengan direktif @attributeRazor :

@attribute [ExcludeFromInteractiveRouting]

Menerapkan atribut menyebabkan navigasi ke halaman keluar dari perutean interaktif. Navigasi masuk dipaksa untuk melakukan pemuatan ulang halaman penuh sebagai gantinya menyelesaikan halaman melalui perutean interaktif. Pemuatan ulang halaman penuh memaksa komponen akar tingkat atas, biasanya App komponen (App.razor), untuk merender dari server, memungkinkan aplikasi beralih ke mode render tingkat atas yang berbeda.

Metode HttpContext.AcceptsInteractiveRouting ekstensi memungkinkan komponen mendeteksi apakah [ExcludeFromInteractiveRouting] diterapkan ke halaman saat ini.

App Dalam komponen, gunakan pola dalam contoh berikut:

  • Halaman yang tidak dianotasi dengan [ExcludeFromInteractiveRouting] default ke InteractiveServer mode render dengan interaktivitas global. Anda dapat mengganti InteractiveServer dengan InteractiveWebAssembly atau InteractiveAuto untuk menentukan mode render global default yang berbeda.
  • Halaman yang dianotasi dengan [ExcludeFromInteractiveRouting] mengadopsi SSR statis (PageRenderMode ditetapkan null).
<!DOCTYPE html>
<html>
<head>
    ...
    <HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
    <Routes @rendermode="@PageRenderMode" />
    ...
</body>
</html>

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

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

Alternatif untuk menggunakan HttpContext.AcceptsInteractiveRouting metode ekstensi adalah membaca metadata titik akhir secara manual menggunakan HttpContext.GetEndpoint()?.Metadata.

Fitur ini dicakup oleh dokumentasi referensi dalam mode render ASP.NET CoreBlazor.

Injeksi konstruktor

Razor komponen mendukung injeksi konstruktor.

Dalam contoh berikut, kelas parsial (code-behind) menyuntikkan NavigationManager layanan menggunakan konstruktor utama:

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

Untuk informasi selengkapnya, lihat Injeksi dependensi ASP.NET Core Blazor.

Kompresi Websocket untuk komponen Server Interaktif

Secara default, komponen Server Interaktif memungkinkan pemadatan untuk koneksi WebSocket dan mengatur frame-ancestors direktif Kebijakan Keamanan Konten (CSP) yang diatur ke 'self', yang hanya mengizinkan penyematan aplikasi di <iframe> asal tempat aplikasi dilayani saat pemadatan diaktifkan atau saat konfigurasi untuk konteks WebSocket disediakan.

Pemadatan dapat dinonaktifkan dengan mengatur ConfigureWebSocketOptions ke null, yang mengurangi kerentanan aplikasi untuk menyerang tetapi dapat mengakibatkan penurunan performa:

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

Konfigurasikan CSP yang lebih frame-ancestors ketat dengan nilai 'none' (tanda kutip tunggal diperlukan), yang memungkinkan kompresi WebSocket tetapi mencegah browser menyematkan aplikasi ke dalam :<iframe>

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

Untuk informasi selengkapnya, lihat sumber daya berikut:

Menangani peristiwa komposisi keyboard di Blazor

Properti baru KeyboardEventArgs.IsComposing menunjukkan apakah peristiwa keyboard adalah bagian dari sesi komposisi. Melacak status komposisi peristiwa keyboard sangat penting untuk menangani metode input karakter internasional.

Menambahkan parameter OverscanCount ke QuickGrid

Komponen QuickGrid sekarang mengekspos OverscanCount properti yang menentukan berapa banyak baris tambahan yang dirender sebelum dan sesudah wilayah yang terlihat saat virtualisasi diaktifkan.

OverscanCount Defaultnya adalah 3. Contoh berikut meningkatkan menjadi OverscanCount 4:

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

SignalR

Bagian ini menjelaskan fitur baru untuk SignalR.

Dukungan jenis polimorfik di SignalR Hub

Metode hub sekarang dapat menerima kelas dasar alih-alih kelas turunan untuk mengaktifkan skenario polimorfik. Jenis dasar perlu diannotasi untuk memungkinkan polimorfisme.

public class MyHub : Hub
{
    public void Method(JsonPerson person)
    {
        if (person is JsonPersonExtended)
        {
        }
        else if (person is JsonPersonExtended2)
        {
        }
        else
        {
        }
    }
}

[JsonPolymorphic]
[JsonDerivedType(typeof(JsonPersonExtended), nameof(JsonPersonExtended))]
[JsonDerivedType(typeof(JsonPersonExtended2), nameof(JsonPersonExtended2))]
private class JsonPerson
{
    public string Name { get; set; }
    public Person Child { get; set; }
    public Person Parent { get; set; }
}

private class JsonPersonExtended : JsonPerson
{
    public int Age { get; set; }
}

private class JsonPersonExtended2 : JsonPerson
{
    public string Location { get; set; }
}

Aktivitas yang Ditingkatkan untuk SignalR

SignalR sekarang memiliki ActivitySource bernama Microsoft.AspNetCore.SignalR.Server yang memancarkan peristiwa untuk panggilan metode hub:

  • Setiap metode adalah aktivitasnya sendiri, sehingga apa pun yang memancarkan aktivitas selama panggilan metode hub berada di bawah aktivitas metode hub.
  • Aktivitas metode hub tidak memiliki induk. Ini berarti mereka tidak dibundel di bawah koneksi yang berjalan SignalR lama.

Contoh berikut menggunakan dasbor .NET Aspire dan paket OpenTelemetry:

<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />

Tambahkan kode startup berikut ke Program.cs file:

// Set OTEL_EXPORTER_OTLP_ENDPOINT environment variable depending on where your OTEL endpoint is
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        if (builder.Environment.IsDevelopment())
        {
            // We want to view all traces in development
            tracing.SetSampler(new AlwaysOnSampler());
        }

        tracing.AddAspNetCoreInstrumentation();
        tracing.AddSource("Microsoft.AspNetCore.SignalR.Server");
    });

builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());

Berikut ini adalah contoh output dari Dasbor Aspire:

Daftar aktivitas untuk SignalR peristiwa panggilan metode Hub

Minimal API

Bagian ini menjelaskan fitur baru untuk API minimal.

Menambahkan InternalServerError dan InternalServerError<TValue> ke TypedResults

Kelas TypedResults ini adalah kendaraan bermanfaat untuk mengembalikan respons berbasis kode status HTTP yang sangat ditik dari API minimal. TypedResults sekarang termasuk metode dan jenis pabrik untuk mengembalikan respons "500 Kesalahan Server Internal" dari titik akhir. Berikut adalah contoh yang mengembalikan respons 500:

var app = WebApplication.Create();

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

app.Run();

OpenAPI

Dukungan bawaan untuk pembuatan dokumen OpenAPI

Spesifikasi OpenAPI adalah standar untuk menjelaskan API HTTP. Standar ini memungkinkan pengembang untuk menentukan bentuk API yang dapat dicolokkan ke generator klien, generator server, alat pengujian, dokumentasi, dan banyak lagi. Dalam Pratinjau .NET 9, ASP.NET Core menyediakan dukungan bawaan untuk menghasilkan dokumen OpenAPI yang mewakili API berbasis pengontrol atau minimal melalui paket Microsoft.AspNetCore.OpenApi .

Panggilan kode yang disorot berikut:

  • AddOpenApi untuk mendaftarkan dependensi yang diperlukan ke dalam kontainer DI aplikasi.
  • MapOpenApi untuk mendaftarkan titik akhir OpenAPI yang diperlukan dalam rute aplikasi.
var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

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

app.Run();

Microsoft.AspNetCore.OpenApi Instal paket dalam proyek menggunakan perintah berikut:

dotnet add package Microsoft.AspNetCore.OpenApi --prerelease

Jalankan aplikasi dan navigasikan ke untuk openapi/v1.json melihat dokumen OpenAPI yang dihasilkan:

Dokumen OpenAPI

Dokumen OpenAPI juga dapat dihasilkan pada waktu build dengan menambahkan Microsoft.Extensions.ApiDescription.Server paket:

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

Dalam file proyek aplikasi, tambahkan yang berikut ini:

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

Jalankan dotnet build dan periksa file ON yang dihasilkan JSdi direktori proyek.

Pembuatan dokumen OpenAPI saat build-time

ASP.NET pembuatan dokumen OpenAPI bawaan Core menyediakan dukungan untuk berbagai penyesuaian dan opsi. Ini menyediakan transformator dokumen dan operasi dan memiliki kemampuan untuk mengelola beberapa dokumen OpenAPI untuk aplikasi yang sama.

Untuk mempelajari selengkapnya tentang kemampuan dokumen OpenAPI baru ASP.NET Core, lihat dokumen Microsoft.AspNetCore.OpenApi baru.

Peningkatan penyelesaian Intellisense untuk paket OpenAPI

ASP.NET dukungan OpenAPI Core sekarang lebih mudah diakses dan ramah pengguna. API OpenAPI dikirim sebagai paket independen, terpisah dari kerangka kerja bersama. Hingga saat ini, ini berarti bahwa pengembang tidak memiliki kenyamanan fitur penyelesaian kode seperti Intellisense untuk API OpenAPI.

Mengenali kesenjangan ini, kami telah memperkenalkan penyedia penyelesaian baru dan perbaikan kode. Alat-alat ini dirancang untuk memfasilitasi penemuan dan penggunaan API OpenAPI, sehingga memudahkan pengembang untuk mengintegrasikan OpenAPI ke dalam proyek mereka. Penyedia penyelesaian menawarkan saran kode real-time, sementara perbaikan kode membantu memperbaiki kesalahan umum dan meningkatkan penggunaan API. Peningkatan ini adalah bagian dari komitmen berkelanjutan kami untuk meningkatkan pengalaman pengembang dan menyederhanakan alur kerja terkait API.

Saat pengguna mengetik pernyataan di mana API terkait OpenAPI tersedia, penyedia penyelesaian menampilkan rekomendasi untuk API. Misalnya, dalam cuplikan layar berikut, penyelesaian untuk AddOpenApi dan MapOpenApi disediakan saat pengguna memasukkan pernyataan pemanggilan pada jenis yang didukung, seperti IEndpointConventionBuilder:

Penyelesaian OpenAPI

Ketika penyelesaian diterima dan Microsoft.AspNetCore.OpenApi paket tidak diinstal, codefixer menyediakan pintasan untuk menginstal dependensi secara otomatis dalam proyek.

Penginstalan paket otomatis

Dukungan untuk [Required] atribut dan [DefaultValue] pada parameter dan properti

Ketika [Required] atribut dan [DefaultValue] diterapkan pada parameter atau properti dalam jenis kompleks, implementasi OpenAPI memetakan ini ke required properti dan default dalam dokumen OpenAPI yang terkait dengan parameter atau skema jenis.

Misalnya, API berikut menghasilkan skema yang menyertainya untuk jenis tersebut Todo .

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}
{
	"required": [
	  "title",
	  "description",
	  "createdOn"
	],
	"type": "object",
	"properties": {
	  "id": {
	    "type": "integer",
	    "format": "int32"
	  },
	  "title": {
	    "type": "string"
	  },
	  "description": {
	    "type": "string",
	    "default": "A new todo"
	  },
	  "createdOn": {
	    "type": "string",
	    "format": "date-time"
	  }
	}
}

Dukungan untuk transformator skema pada dokumen OpenAPI

Dukungan OpenAPI bawaan sekarang dikirim dengan dukungan untuk transformator skema yang dapat digunakan untuk memodifikasi skema yang dihasilkan oleh System.Text.Json dan implementasi OpenAPI. Seperti transformator dokumen dan operasi, transformator skema dapat didaftarkan pada objek OpenApiOptions . Misalnya, sampel kode berikut menunjukkan menggunakan transformator skema untuk menambahkan contoh ke skema jenis.

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options =>
{
    options.UseSchemaTransformer((schema, context, cancellationToken) =>
    {
        if (context.Type == typeof(Todo))
        {
            schema.Example = new OpenApiObject
            {
                ["id"] = new OpenApiInteger(1),
                ["title"] = new OpenApiString("A short title"),
                ["description"] = new OpenApiString("A long description"),
                ["createdOn"] = new OpenApiDateTime(DateTime.Now)
            };
        }
        return Task.CompletedTask;
    });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.MapPost("/todos", (Todo todo) => { });

app.Run();

class Todo
{
	public int Id { get; init; }
	public required string Title { get; init; }
	[DefaultValue("A new todo")]
	public required string Description { get; init; }
	[Required]
	public DateTime CreatedOn { get; init; }
}

Autentikasi dan otorisasi

Bagian ini menjelaskan fitur baru untuk autentikasi dan otorisasi.

Kustomisasi Parameter OIDC dan OAuth

Handler autentikasi OAuth dan OIDC sekarang memiliki AdditionalAuthorizationParameters opsi untuk mempermudah kustomisasi parameter pesan otorisasi yang biasanya disertakan sebagai bagian dari string kueri pengalihan. Di .NET 8 dan yang lebih lama, ini memerlukan metode panggilan balik kustom OnRedirectToIdentityProvider atau ditimpa BuildChallengeUrl dalam handler kustom. Berikut adalah contoh kode .NET 8:

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
{
    options.Events.OnRedirectToIdentityProvider = context =>
    {
        context.ProtocolMessage.SetParameter("prompt", "login");
        context.ProtocolMessage.SetParameter("audience", "https://api.example.com");
        return Task.CompletedTask;
    };
});

Contoh sebelumnya sekarang dapat disederhanakan ke kode berikut:

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

Mengonfigurasi HTTP.sys bendera autentikasi yang diperluas

Anda sekarang dapat mengonfigurasi HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING bendera dan HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL HTTP.sys dengan menggunakan properti baru EnableKerberosCredentialCaching dan CaptureCredentials pada HTTP.sys AuthenticationManager untuk mengoptimalkan cara autentikasi Windows ditangani. Contohnya:

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

Lain-lain

Bagian berikut menjelaskan fitur baru lainnya.

Pustaka baru HybridCache

HybridCache API menjenjang beberapa celah di API dan IMemoryCache yang adaIDistributedCache. Ini juga menambahkan kemampuan baru, seperti:

  • Perlindungan "Stampede" untuk mencegah pengambilan paralel dari pekerjaan yang sama.
  • Serialisasi yang dapat dikonfigurasi.

HybridCache dirancang untuk menjadi pengganti drop-in untuk yang ada IDistributedCache dan IMemoryCache penggunaan, dan menyediakan API sederhana untuk menambahkan kode penembolokan baru. Ini menyediakan API terpadu untuk penembolokan dalam proses dan di luar proses.

Untuk melihat bagaimana API disederhanakan HybridCache IDistributedCache, bandingkan dengan kode yang menggunakan . Berikut adalah contoh tampilan penggunaan IDistributedCache :

public class SomeService(IDistributedCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
        var bytes = await cache.GetAsync(key, token); // Try to get from cache.
        SomeInformation info;
        if (bytes is null)
        {
            // Cache miss; get the data from the real source.
            info = await SomeExpensiveOperationAsync(name, id, token);

            // Serialize and cache it.
            bytes = SomeSerializer.Serialize(info);
            await cache.SetAsync(key, bytes, token);
        }
        else
        {
            // Cache hit; deserialize it.
            info = SomeSerializer.Deserialize<SomeInformation>(bytes);
        }
        return info;
    }

    // This is the work we're trying to cache.
    private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
        CancellationToken token = default)
    { /* ... */ }
}

Itu banyak pekerjaan yang harus disempurnakan setiap kali, termasuk hal-hal seperti serialisasi. Dan dalam skenario cache miss, Anda bisa berakhir dengan beberapa utas bersamaan, semua mendapatkan cache miss, semua mengambil data yang mendasarinya, semua menserialisasikannya, dan semua mengirim data tersebut ke cache.

Untuk menyederhanakan dan meningkatkan kode ini dengan HybridCache, pertama-tama kita perlu menambahkan pustaka Microsoft.Extensions.Caching.Hybridbaru :

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

Daftarkan HybridCache layanan, seperti anda akan mendaftarkan implementasi IDistributedCache :

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

Sekarang sebagian besar masalah penembolokan dapat dilepaskan ke HybridCache:

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

Kami menyediakan implementasi konkret dari HybridCache kelas abstrak melalui injeksi dependensi, tetapi dimaksudkan agar pengembang dapat memberikan implementasi kustom API. Implementasi HybridCache ini berkaitan dengan segala sesuatu yang terkait dengan penembolokan, termasuk penanganan operasi bersamaan. Token cancel di sini mewakili pembatalan gabungan semua pemanggil bersamaan—bukan hanya pembatalan pemanggil yang dapat kita lihat (yaitu, token).

Skenario throughput tinggi dapat dioptimalkan lebih lanjut dengan menggunakan TState pola, untuk menghindari beberapa overhead dari variabel yang ditangkap dan panggilan balik per instans:

public class SomeService(HybridCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
    {
        return await cache.GetOrCreateAsync(
            $"someinfo:{name}:{id}", // unique key for this combination
            (name, id), // all of the state we need for the final call, if needed
            static async (state, token) =>
                await SomeExpensiveOperationAsync(state.name, state.id, token),
            token: token
        );
    }
}

HybridCache menggunakan implementasi yang dikonfigurasi IDistributedCache , jika ada, untuk penembolokan di luar proses sekunder, misalnya, menggunakan Redis. Tetapi bahkan tanpa IDistributedCache, HybridCache layanan masih akan memberikan perlindungan penembolokan dalam proses dan "stempel".

Catatan tentang penggunaan kembali objek

Dalam kode umum yang ada yang menggunakan IDistributedCache, setiap pengambilan objek dari cache menghasilkan deserialisasi. Perilaku ini berarti bahwa setiap pemanggil bersamaan mendapatkan instans terpisah dari objek, yang tidak dapat berinteraksi dengan instans lain. Hasilnya adalah keamanan utas, karena tidak ada risiko modifikasi bersamaan pada instans objek yang sama.

Karena banyak HybridCache penggunaan akan diadaptasi dari kode yang ada IDistributedCache , HybridCache mempertahankan perilaku ini secara default untuk menghindari memperkenalkan bug konkurensi. Namun, kasus penggunaan tertentu secara inheren aman utas:

  • Jika jenis yang di-cache tidak dapat diubah.
  • Jika kode tidak mengubahnya.

Dalam kasus seperti itu, informasikan HybridCache bahwa aman untuk menggunakan kembali instans dengan:

  • Menandai jenis sebagai sealed. Kata sealed kunci dalam C# berarti bahwa kelas tidak dapat diwariskan.
  • Menerapkan atribut ke [ImmutableObject(true)] atribut tersebut. Atribut [ImmutableObject(true)] menunjukkan bahwa status objek tidak dapat diubah setelah dibuat.

Dengan menggunakan kembali instans, HybridCache dapat mengurangi overhead CPU dan alokasi objek yang terkait dengan deserialisasi per panggilan. Hal ini dapat menyebabkan peningkatan performa dalam skenario di mana objek yang di-cache besar atau sering diakses.

Fitur lain HybridCache

Seperti IDistributedCache, HybridCache mendukung penghapusan dengan kunci dengan RemoveKeyAsync metode .

HybridCache juga menyediakan API opsional untuk IDistributedCache implementasi, untuk menghindari byte[] alokasi. Fitur ini diimplementasikan oleh versi pratinjau paket Microsoft.Extensions.Caching.StackExchangeRedis dan Microsoft.Extensions.Caching.SqlServer .

Serialisasi dikonfigurasi sebagai bagian dari mendaftarkan layanan, dengan dukungan untuk serializer khusus jenis dan umum melalui WithSerializer metode dan .WithSerializerFactory , ditautkan dari AddHybridCache panggilan. Secara default, pustaka menangani string dan byte[] secara internal, dan menggunakan System.Text.Json untuk yang lain, tetapi Anda dapat menggunakan protobuf, xml, atau apa pun.

HybridCache mendukung runtime .NET yang lebih lama, hingga .NET Framework 4.7.2 dan .NET Standard 2.0.

Untuk informasi selengkapnya tentang HybridCache, lihat Pustaka HybridCache di ASP.NET Core

Penyempurnaan halaman pengecualian pengembang

Halaman pengecualian pengembang ASP.NET Core ditampilkan saat aplikasi melemparkan pengecualian yang tidak tertangani selama pengembangan. Halaman pengecualian pengembang menyediakan informasi terperinci tentang pengecualian dan permintaan.

Pratinjau 3 menambahkan metadata titik akhir ke halaman pengecualian pengembang. ASP.NET Core menggunakan metadata titik akhir untuk mengontrol perilaku titik akhir, seperti perutean, penembolokan respons, pembatasan laju, pembuatan OpenAPI, dan banyak lagi. Gambar berikut menunjukkan informasi metadata baru di bagian Routing halaman pengecualian pengembang:

Informasi metadata baru di halaman pengecualian pengembang

Saat menguji halaman pengecualian pengembang, peningkatan kualitas hidup yang kecil diidentifikasi. Mereka dikirim dalam Pratinjau 4:

  • Pembungkusan teks yang lebih baik. Panjang cookie, nilai string kueri, dan nama metode tidak lagi menambahkan bilah gulir browser horizontal.
  • Teks yang lebih besar yang ditemukan dalam desain modern.
  • Ukuran tabel yang lebih konsisten.

Gambar animasi berikut menunjukkan halaman pengecualian pengembang baru:

Halaman pengecualian pengembang baru

Peningkatan penelusuran kesalahan kamus

Tampilan debugging kamus dan koleksi nilai kunci lainnya memiliki tata letak yang ditingkatkan. Kunci ditampilkan di kolom kunci debugger alih-alih digabungkan dengan nilai . Gambar berikut menunjukkan tampilan lama dan baru kamus di debugger.

Sebelum:

Pengalaman debugger sebelumnya

Setelah:

Pengalaman debugger baru

ASP.NET Core memiliki banyak koleksi kunci-nilai. Pengalaman penelusuran kesalahan yang ditingkatkan ini berlaku untuk:

  • Header HTTP
  • String kueri
  • Formulir
  • Cookies
  • Menampilkan data
  • Merutekan data
  • Fitur

Perbaikan untuk 503 selama daur ulang aplikasi di IIS

Secara default sekarang ada penundaan 1 detik antara ketika IIS diberi tahu tentang daur ulang atau matikan dan ketika ANCM memberi tahu server terkelola untuk mulai dimatikan. Penundaan dapat dikonfigurasi melalui ANCM_shutdownDelay variabel lingkungan atau dengan mengatur shutdownDelay pengaturan handler. Kedua nilai berada dalam milidetik. Penundaan ini terutama untuk mengurangi kemungkinan perlombaan di mana:

  • IIS belum mulai mengantre permintaan untuk masuk ke aplikasi baru.
  • ANCM mulai menolak permintaan baru yang masuk ke aplikasi lama.

Komputer atau mesin yang lebih lambat dengan penggunaan CPU yang lebih berat mungkin ingin menyesuaikan nilai ini untuk mengurangi kemungkinan 503.

Contoh pengaturan shutdownDelay:

<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
  <handlerSettings>
    <!-- Milliseconds to delay shutdown by.
    this doesn't mean incoming requests will be delayed by this amount,
    but the old app instance will start shutting down after this timeout occurs -->
    <handlerSetting name="shutdownDelay" value="5000" />
  </handlerSettings>
</aspNetCore>

Perbaikannya ada dalam modul ANCM yang diinstal secara global yang berasal dari bundel hosting.

Mengoptimalkan pengiriman aset web statis

Mengikuti praktik terbaik produksi untuk melayani aset statis membutuhkan sejumlah besar keahlian kerja dan teknis. Tanpa pengoptimalan seperti kompresi, penembolokan, dan sidik jari:

  • Browser harus membuat permintaan tambahan pada setiap pemuatan halaman.
  • Lebih banyak byte daripada yang diperlukan ditransfer melalui jaringan.
  • Terkadang versi file kedaluarsa disajikan kepada klien.

Membuat aplikasi web berkinerja memerlukan pengoptimalan pengiriman aset ke browser. Kemungkinan pengoptimalan meliputi:

  • Layani aset tertentu sekali sampai file berubah atau browser menghapus cache-nya. Atur header ETag .
  • Cegah browser menggunakan aset lama atau kedaluarsa setelah aplikasi diperbarui. Atur header Terakhir Diubah .
  • Siapkan header penembolokan yang tepat.
  • Gunakan middleware penembolokan.
  • Menyajikan versi aset yang dikompresi jika memungkinkan.
  • Gunakan CDN untuk melayani aset yang lebih dekat dengan pengguna.
  • Minimalkan ukuran aset yang disajikan ke browser. Pengoptimalan ini tidak termasuk minifikasi.

MapStaticAssets adalah middleware baru yang membantu mengoptimalkan pengiriman aset statis dalam aplikasi. Ini dirancang untuk bekerja dengan semua kerangka kerja UI, termasuk Blazor, Razor Pages, dan MVC. Ini biasanya merupakan pengganti drop-in untuk UseStaticFiles:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

+app.MapStaticAssets();
-app.UseStaticFiles();
app.MapRazorPages();

app.Run();

MapStaticAssets beroperasi dengan menggabungkan proses build dan publish-time untuk mengumpulkan informasi tentang semua sumber daya statis dalam aplikasi. Informasi ini kemudian digunakan oleh pustaka runtime untuk melayani file-file ini secara efisien ke browser.

MapStaticAssets dapat menggantikan UseStaticFiles dalam sebagian besar situasi, namun, hal ini dioptimalkan untuk melayani aset yang memiliki pengetahuan tentang aplikasi saat membangun dan menerbitkan waktu. Jika aplikasi melayani aset dari lokasi lain, seperti disk atau sumber daya yang disematkan, UseStaticFiles harus digunakan.

MapStaticAssets memberikan manfaat berikut yang tidak ditemukan dengan UseStaticFiles:

  • Kompresi waktu build untuk semua aset di aplikasi:
    • gzip selama pengembangan dan gzip + brotli selama penerbitan.
    • Semua aset dikompresi dengan tujuan mengurangi ukuran aset menjadi minimum.
  • ETagsBerbasis konten : Etags untuk setiap sumber daya adalah string yang dikodekan Base64 dari hash SHA-256 konten. Ini memastikan bahwa browser hanya mengunduh ulang file jika kontennya telah berubah.

Tabel berikut ini memperlihatkan ukuran asli dan terkompresi CSS dan JS file dalam templat Halaman default Razor :

File Asli Dikompresi % Pengurangan
bootstrap.min.css 163 17.5 89.26%
jquery.js 89.6 28 68.75%
bootstrap.min.js 78.5 20 74.52%
Total 331.1 65.5 80.20%

Tabel berikut ini memperlihatkan ukuran asli dan terkompresi menggunakan pustaka komponen UI Blazor Fasih:

File Asli Dikompresi % Pengurangan
fluent.js 384 73 80.99%
fluent.css 94 11 88.30%
Total 478 84 82.43%

Untuk total 478 KB yang tidak dikompresi menjadi 84 KB dikompresi.

Tabel berikut ini memperlihatkan ukuran asli dan terkompresi menggunakan pustaka komponen LumpurBlazorBlazor:

File Asli Dikompresi Pengurangan
LumpurBlazor.min.css 541 37,5 93.07%
BlazorLumpur.min.js 47.4 9.2 80.59%
Total 588.4 46.7 92.07%

Pengoptimalan terjadi secara otomatis saat menggunakan MapStaticAssets. Saat pustaka ditambahkan atau diperbarui, misalnya dengan JavaScript atau CSS baru, aset dioptimalkan sebagai bagian dari build. Pengoptimalan sangat bermanfaat bagi lingkungan seluler yang dapat memiliki bandwidth yang lebih rendah atau koneksi yang tidak dapat diandalkan.

Untuk informasi selengkapnya tentang fitur pengiriman file baru, lihat sumber daya berikut ini:

Mengaktifkan kompresi dinamis di server vs menggunakan MapStaticAssets

MapStaticAssets memiliki keuntungan berikut daripada kompresi dinamis di server:

  • Lebih sederhana karena tidak ada konfigurasi spesifik server.
  • Lebih berkinerja karena aset dikompresi pada waktu build.
  • Memungkinkan pengembang untuk menghabiskan waktu tambahan selama proses build untuk memastikan bahwa aset adalah ukuran minimum.

Pertimbangkan tabel berikut membandingkan kompresi LumpurBlazor dengan kompresi dinamis IIS dan MapStaticAssets:

IIS gzip MapStaticAssets Pengurangan MapStaticAssets
≅ 90 37,5 59%

ASP0026: Penganalisis untuk memperingatkan ketika [Otorisasi] ditimpa oleh [AllowAnonymous] dari "lebih jauh"

Tampaknya intuitif bahwa atribut ditempatkan [Authorize] "lebih dekat" ke tindakan MVC daripada [AllowAnonymous] atribut akan mengambil alih [AllowAnonymous] atribut dan memaksa otorisasi. Namun, ini belum tentu terjadi. Yang penting adalah urutan relatif atribut.

Kode berikut menunjukkan contoh di mana atribut yang lebih dekat [Authorize] ditimpa oleh [AllowAnonymous] atribut yang lebih jauh.

[AllowAnonymous]
public class MyController
{
    [Authorize] // Overridden by the [AllowAnonymous] attribute on the class
    public IActionResult Private() => null;
}
[AllowAnonymous]
public class MyControllerAnon : ControllerBase
{
}

[Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
public class MyControllerInherited : MyControllerAnon
{
}

public class MyControllerInherited2 : MyControllerAnon
{
    [Authorize] // Overridden by the [AllowAnonymous] attribute on MyControllerAnon
    public IActionResult Private() => null;
}
[AllowAnonymous]
[Authorize] // Overridden by the preceding [AllowAnonymous]
public class MyControllerMultiple : ControllerBase
{
}

Di .NET 9 Pratinjau 6, kami telah memperkenalkan penganalisis yang akan menyoroti instans seperti ini di mana atribut yang lebih dekat [Authorize] ditimpa oleh [AllowAnonymous] atribut yang lebih jauh dari tindakan MVC. Peringatan menunjuk ke atribut yang ditimpa [Authorize] dengan pesan berikut:

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

Tindakan yang benar untuk diambil jika Anda melihat peringatan ini tergantung pada niat di balik atribut. Atribut yang lebih [AllowAnonymous] jauh harus dihapus jika tidak sengaja mengekspos titik akhir ke pengguna anonim. [AllowAnonymous] Jika atribut dimaksudkan untuk mengambil alih atribut yang lebih dekat[Authorize], Anda dapat mengulangi [AllowAnonymous] atribut setelah [Authorize] atribut untuk mengklarifikasi niat.

[AllowAnonymous]
public class MyController
{
    // This produces no warning because the second, "closer" [AllowAnonymous]
    // clarifies that [Authorize] is intentionally overridden.
    // Specifying AuthenticationSchemes can still be useful
    // for endpoints that allow but don't require authenticated users.
    [Authorize(AuthenticationSchemes = "Cookies")]
    [AllowAnonymous]
    public IActionResult Privacy() => null;
}