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 .NET 9 Release Candidate 2.
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 Web App (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 Web App.
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 Membangun .NET MAUIBlazor Hybrid aplikasi dengan .Blazor Web App
Pengoptimalan pengiriman aset statis
Konvensi titik akhir perutean Aset Statis Peta (MapStaticAssets) adalah fitur baru yang membantu mengoptimalkan pengiriman aset statis di aplikasi ASP.NET Core apa pun, termasuk Blazor aplikasi.
Untuk informasi selengkapnya, lihat sumber daya berikut:
- Bagian Optimalkan pengiriman aset web statis di artikel ini.
- ASP.NET File statis intiBlazor.
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 Blazor Web App. Saat Anda membuat baru Blazor Web App 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 dan sebelum runtime .NET WebAssembly 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 Web App templat proyek 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 Web App dari templat proyek, 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 aplikasi sisi Blazor server Secure ASP.NET Core:
Menambahkan halaman penyajian sisi server statis (SSR) ke interaktif global Blazor Web App
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 membaca/menulis cookie HTTP dan 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 @attribute
Razor :
@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 keInteractiveServer
mode render dengan interaktivitas global. Anda dapat menggantiInteractiveServer
denganInteractiveWebAssembly
atauInteractiveAuto
untuk menentukan mode render global default yang berbeda. - Halaman yang dianotasi dengan
[ExcludeFromInteractiveRouting]
mengadopsi SSR statis (PageRenderMode
ditetapkannull
).
<!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)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
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:
- panduan ASP.NET Core BlazorSignalR
- Panduan mitigasi ancaman untuk penyajian sisi server interaktif ASP.NET Core Blazor
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>
InputNumber
komponen mendukung type="range"
atribut
Komponen InputNumber<TValue> sekarang mendukung type="range"
atribut , yang membuat input rentang yang mendukung pengikatan model dan validasi formulir, biasanya dirender sebagai penggeser atau kontrol dial daripada kotak teks:
<EditForm Model="Model" OnSubmit="Submit" FormName="EngineForm">
<div>
<label>
Nacelle Count (2-6):
<InputNumber @bind-Value="Model!.NacelleCount" max="6" min="2"
step="1" type="range" />
</label>
</div>
<div>
<button type="submit">Submit</button>
</div>
</EditForm>
@code {
[SupplyParameterFromForm]
private EngineSpecifications? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private void Submit() {}
public class EngineSpecifications
{
[Required, Range(minimum: 2, maximum: 6)]
public int NacelleCount { get; set; }
}
}
Beberapa Blazor Web Apps per proyek server
Dukungan untuk beberapa Blazor Web Apps per proyek server akan dipertimbangkan untuk .NET 10 (November, 2025).
Untuk informasi selengkapnya, lihat Dukungan untuk beberapa Blazor aplikasi Web per proyek server (dotnet/aspnetcore
#52216).
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:
SignalR mendukung pemangkasan dan AOT Asli
Melanjutkan perjalanan AOT Asli yang dimulai di .NET 8, kami telah mengaktifkan pemangkasan dan dukungan kompilasi native ahead-of-time (AOT) untuk SignalR skenario klien dan server. Anda sekarang dapat memanfaatkan manfaat performa menggunakan AOT Asli dalam aplikasi yang digunakan SignalR untuk komunikasi web real time.
Memulai
Instal .NET 9 SDK terbaru.
Buat solusi dari webapiaot
templat di shell perintah menggunakan perintah berikut:
dotnet new webapiaot -o SignalRChatAOTExample
Ganti konten Program.cs
file dengan kode berikut SignalR :
using Microsoft.AspNetCore.SignalR;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddSignalR();
builder.Services.Configure<JsonHubProtocolOptions>(o =>
{
o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});
var app = builder.Build();
app.MapHub<ChatHub>("/chatHub");
app.MapGet("/", () => Results.Content("""
<!DOCTYPE html>
<html>
<head>
<title>SignalR Chat</title>
</head>
<body>
<input id="userInput" placeholder="Enter your name" />
<input id="messageInput" placeholder="Type a message" />
<button onclick="sendMessage()">Send</button>
<ul id="messages"></ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messages").appendChild(li);
});
async function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
await connection.invoke("SendMessage", user, message);
}
connection.start().catch(err => console.error(err));
</script>
</body>
</html>
""", "text/html"));
app.Run();
[JsonSerializable(typeof(string))]
internal partial class AppJsonSerializerContext : JsonSerializerContext { }
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Contoh sebelumnya menghasilkan eksekusi Windows asli 10 MB dan Linux yang dapat dieksekusi sebesar 10,9 MB.
Batasan
- Hanya protokol JSON yang saat ini didukung:
- Seperti yang ditunjukkan dalam kode sebelumnya, aplikasi yang menggunakan serialisasi JSON dan AOT Asli harus menggunakan
System.Text.Json
Generator Sumber. - Ini mengikuti pendekatan yang sama dengan API minimal.
- Seperti yang ditunjukkan dalam kode sebelumnya, aplikasi yang menggunakan serialisasi JSON dan AOT Asli harus menggunakan
- SignalR Di server, parameter metode hub jenis
IAsyncEnumerable<T>
danChannelReader<T>
di manaT
valueType (struct
) tidak didukung. Menggunakan jenis ini menghasilkan pengecualian runtime saat startup dalam pengembangan dan di aplikasi yang diterbitkan. Untuk informasi selengkapnya, lihat SignalR: Menggunakan IAsyncEnumerable<T> dan ChannelReader<T> dengan ValueTypes di AOT asli (dotnet/aspnetcore
#56179). - Hub yang sangat ditik tidak didukung dengan Native AOT (
PublishAot
). Menggunakan hub yang sangat diketik dengan AOT Asli akan menghasilkan peringatan selama build dan penerbitan, dan pengecualian runtime. Menggunakan hub yang sangat ditik dengan pemangkasan (PublishedTrimmed
) didukung. - Hanya
Task
,Task<T>
,ValueTask
, atauValueTask<T>
didukung untuk jenis pengembalian asinkron.
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();
Memanggil ProducesProblem
dan ProducesValidationProblem
pada grup rute
Metode ProducesProblem
ekstensi dan ProducesValidationProblem
telah diperbarui untuk mendukung penggunaannya pada grup rute. Metode ini menunjukkan bahwa semua titik akhir dalam grup rute dapat mengembalikan ProblemDetails
atau ValidationProblemDetails
merespons untuk tujuan metadata OpenAPI.
var app = WebApplication.Create();
var todos = app.MapGroup("/todos")
.ProducesProblem();
todos.MapGet("/", () => new Todo(1, "Create sample app", false));
todos.MapPost("/", (Todo todo) => Results.Ok(todo));
app.Run();
record Todo(int Id, string Title, boolean IsCompleted);
Problem
dan ValidationProblem
jenis hasil mendukung konstruksi dengan IEnumerable<KeyValuePair<string, object?>>
nilai
Sebelum .NET 9, membuat jenis hasil Masalah dan ValidasiProblem dalam API minimal yang errors
mengharuskan properti dan extensions
diinisialisasi dengan implementasi IDictionary<string, object?>
. Dalam rilis ini, API konstruksi ini mendukung kelebihan beban yang mengonsumsi IEnumerable<KeyValuePair<string, object?>>
.
var app = WebApplication.Create();
app.MapGet("/", () =>
{
var extensions = new List<KeyValuePair<string, object?>> { new("test", "value") };
return TypedResults.Problem("This is an error with extensions",
extensions: extensions);
});
Terima kasih kepada pengguna GitHub joegoldman2 untuk kontribusi ini!
OpenAPI
Bagian ini menjelaskan fitur baru untuk 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. Di .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 juga dapat dihasilkan pada waktu build dengan menambahkan Microsoft.Extensions.ApiDescription.Server
paket:
dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease
Untuk memodifikasi lokasi dokumen OpenAPI yang dipancarkan, atur jalur target di properti OpenApiDocumentsDirectory dalam file proyek aplikasi:
<PropertyGroup>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)</OpenApiDocumentsDirectory>
</PropertyGroup>
Jalankan dotnet build
dan periksa file JSON yang dihasilkan di direktori proyek.
ASP.NET pembuatan dokumen OpenAPI bawaan Core menyediakan dukungan untuk berbagai penyesuaian dan opsi. Ini menyediakan transformator dokumen, operasi, dan skema 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.
Microsoft.AspNetCore.OpenApi mendukung pemangkasan dan AOT Asli
Dukungan OpenAPI bawaan baru di ASP.NET Core sekarang juga mendukung pemangkasan dan AOT Asli.
Memulai
Buat proyek ASP.NET Core Web API (Native AOT) baru.
dotnet new webapiaot
Tambahkan paket Microsoft.AspNetCore.OpenAPI.
dotnet add package Microsoft.AspNetCore.OpenApi --prerelease
Untuk pratinjau ini, Anda juga perlu menambahkan paket Microsoft.OpenAPI terbaru untuk menghindari peringatan pemangkasan.
dotnet add package Microsoft.OpenApi
Perbarui Program.cs untuk mengaktifkan pembuatan dokumen OpenAPI.
+ builder.Services.AddOpenApi();
var app = builder.Build();
+ app.MapOpenApi();
Memublikasikan aplikasi.
dotnet publish
Aplikasi ini menerbitkan menggunakan Native AOT tanpa peringatan.
Autentikasi dan otorisasi
Bagian ini menjelaskan fitur baru untuk autentikasi dan otorisasi.
OpenIdConnectHandler menambahkan dukungan untuk Permintaan Otorisasi Yang Didorong (PAR)
Kami ingin mengucapkan terima kasih kepada Joe DeCock dari Duende Software karena telah menambahkan Permintaan Otorisasi Yang Didorong (PAR) ke OpenIdConnectHandler ASP.NET Core. Joe menjelaskan latar belakang dan motivasi untuk mengaktifkan PAR dalam proposal API-nya sebagai berikut:
Permintaan Otorisasi yang Didorong (PAR) adalah standar OAuth yang relatif baru yang meningkatkan keamanan alur OAuth dan OIDC dengan memindahkan parameter otorisasi dari saluran depan ke saluran belakang. Artinya, memindahkan parameter otorisasi dari URL pengalihan di browser ke komputer langsung ke panggilan http komputer di ujung belakang.
Ini mencegah serangan cyber di browser dari:
- Melihat parameter otorisasi, yang dapat membocorkan PII.
- Merusak parameter tersebut. Misalnya, cyberattacker dapat mengubah cakupan akses yang diminta.
Mendorong parameter otorisasi juga membuat URL permintaan tetap pendek. Parameter otorisasi bisa menjadi sangat panjang saat menggunakan fitur OAuth dan OIDC yang lebih kompleks seperti Permintaan Otorisasi Kaya. URL yang merupakan masalah penyebab panjang di banyak browser dan infrastruktur jaringan.
Penggunaan PAR didorong oleh kelompok kerja FAPI dalam OpenID Foundation. Misalnya, Profil Keamanan FAPI2.0 memerlukan penggunaan PAR. Profil keamanan ini digunakan oleh banyak kelompok yang bekerja pada perbankan terbuka (terutama di Eropa), dalam perawatan kesehatan, dan di industri lain dengan persyaratan keamanan yang tinggi.
PAR didukung oleh sejumlah identity penyedia, termasuk
Untuk .NET 9, kami telah memutuskan untuk mengaktifkan PAR secara default jika identity dokumen penemuan penyedia mengiklankan dukungan untuk PAR, karena harus memberikan keamanan yang ditingkatkan untuk penyedia yang mendukungnya. Dokumen identity penemuan penyedia biasanya ditemukan di .well-known/openid-configuration
. Jika ini menyebabkan masalah, Anda dapat menonaktifkan PAR melalui OpenIdConnectOptions.PushedAuthorizationBehavior sebagai berikut:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("oidc", oidcOptions =>
{
// Other provider-specific configuration goes here.
// The default value is PushedAuthorizationBehavior.UseIfAvailable.
// 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
// and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
// of type 'OpenIdConnectOptions' could be found
oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
});
Untuk memastikan bahwa autentikasi hanya berhasil jika PAR digunakan, gunakan PushedAuthorizationBehavior.Require sebagai gantinya. Perubahan ini juga memperkenalkan peristiwa OnPushAuthorization baru ke OpenIdConnectEvents yang dapat digunakan menyesuaikan permintaan otorisasi yang didorong atau menanganinya secara manual. Lihat proposal API untuk detail selengkapnya.
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
Penting
HybridCache
saat ini masih dalam pratinjau tetapi akan sepenuhnya dirilis setelah .NET 9.0 dalam rilis minor ekstensi .NET di masa mendatang.
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.Hybrid
baru :
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="9.0.0" />
Daftarkan HybridCache
layanan, seperti anda akan mendaftarkan implementasi IDistributedCache
:
builder.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
. Katasealed
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:
Saat menguji halaman pengecualian pengembang, peningkatan kualitas hidup yang kecil diidentifikasi. Mereka dikirim dalam Pratinjau 4:
- Pembungkusan teks yang lebih baik. Cookie panjang, 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:
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:
Setelah:
ASP.NET Core memiliki banyak koleksi kunci-nilai. Pengalaman penelusuran kesalahan yang ditingkatkan ini berlaku untuk:
- Header HTTP
- String kueri
- Formulir
- Cookie
- 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 fitur baru yang 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 dangzip + brotli
selama penerbitan.- Semua aset dikompresi dengan tujuan mengurangi ukuran aset menjadi minimum.
ETags
Berbasis 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 |
---|---|---|---|
fasih.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 MudBlazorBlazor :
File | Asli | Dikompresi | Pengurangan |
---|---|---|---|
MudBlazor.min.css | 541 | 37,5 | 93.07% |
MudBlazor.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 MudBlazor 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;
}
Metrik koneksi yang disempurnakan Kestrel
Kami telah melakukan peningkatan signifikan pada Kestrelmetrik koneksi dengan menyertakan metadata tentang mengapa koneksi gagal. kestrel.connection.duration
Metrik sekarang menyertakan alasan dekat koneksi dalam error.type
atribut .
Berikut adalah sampel kecil nilai error.type
:
tls_handshake_failed
- Koneksi memerlukan TLS, dan jabat tangan TLS gagal.connection_reset
- Koneksi secara tak terduga ditutup oleh klien saat permintaan sedang berlangsung.request_headers_timeout
- Kestrel menutup koneksi karena tidak menerima header permintaan tepat waktu.max_request_body_size_exceeded
- Kestrel menutup koneksi karena data yang diunggah melebihi ukuran maksimum.
Sebelumnya, mendiagnosis Kestrel masalah koneksi memerlukan server untuk merekam pengelogan tingkat rendah yang terperinci. Namun, log bisa mahal untuk dihasilkan dan disimpan, dan mungkin sulit untuk menemukan informasi yang tepat di antara kebisingan.
Metrik adalah alternatif yang jauh lebih murah yang dapat dibiarkan di lingkungan produksi dengan dampak minimal. Metrik yang dikumpulkan dapat mendorong dasbor dan pemberitahuan. Setelah masalah diidentifikasi pada tingkat tinggi dengan metrik, penyelidikan lebih lanjut menggunakan pengelogan dan alat lainnya dapat dimulai.
Kami berharap metrik koneksi yang ditingkatkan berguna dalam banyak skenario:
- Menyelidiki masalah performa yang disebabkan oleh masa pakai koneksi yang singkat.
- Mengamati serangan eksternal yang sedang berlangsung yang berdampak pada Kestrel performa dan stabilitas.
- Perekaman mencoba serangan eksternal pada Kestrel penguatan keamanan bawaan yang Kestreldicegah.
Untuk informasi selengkapnya, lihat metrik ASP.NET Core.
Menyesuaikan Kestrel titik akhir pipa bernama
KestrelDukungan pipa bernama telah ditingkatkan dengan opsi kustomisasi tingkat lanjut. Metode baru CreateNamedPipeServerStream
pada opsi pipa bernama memungkinkan pipa disesuaikan per titik akhir.
Contoh di mana ini berguna adalah Kestrel aplikasi yang memerlukan dua titik akhir pipa dengan keamanan akses yang berbeda. Opsi CreateNamedPipeServerStream
dapat digunakan untuk membuat pipa dengan pengaturan keamanan kustom, tergantung pada nama pipa.
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenNamedPipe("pipe1");
options.ListenNamedPipe("pipe2");
});
builder.WebHost.UseNamedPipes(options =>
{
options.CreateNamedPipeServerStream = (context) =>
{
var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);
return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
};
});
ExceptionHandlerMiddleware
opsi untuk memilih kode status berdasarkan jenis pengecualian
Opsi baru saat mengonfigurasi ExceptionHandlerMiddleware
memungkinkan pengembang aplikasi memilih kode status apa yang akan dikembalikan saat pengecualian terjadi selama penanganan permintaan. Opsi baru mengubah kode status yang ProblemDetails
diatur dalam respons dari ExceptionHandlerMiddleware
.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError,
});
Menolak metrik HTTP pada titik akhir dan permintaan tertentu
.NET 9 memperkenalkan kemampuan untuk menolak metrik HTTP untuk titik akhir dan permintaan tertentu. Memilih keluar dari metrik perekaman bermanfaat untuk titik akhir yang sering dipanggil oleh sistem otomatis, seperti pemeriksaan kesehatan. Merekam metrik untuk permintaan ini umumnya tidak perlu.
Permintaan HTTP ke titik akhir dapat dikecualikan dari metrik dengan menambahkan metadata. Yaitu:
[DisableHttpMetrics]
Tambahkan atribut ke pengontrol API Web, hub, SignalR atau layanan gRPC.- Panggil DisableHttpMetrics saat memetakan titik akhir di startup aplikasi:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
Properti MetricsDisabled
telah ditambahkan ke IHttpMetricsTagsFeature
untuk:
- Skenario tingkat lanjut di mana permintaan tidak dipetakan ke titik akhir.
- Menonaktifkan pengumpulan metrik secara dinamis untuk permintaan HTTP tertentu.
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null &&
context.Request.Headers.ContainsKey("x-disable-metrics"))
{
metricsFeature.MetricsDisabled = true;
}
await next(context);
});
Dukungan Perlindungan Data untuk menghapus kunci
Sebelum .NET 9, kunci perlindungan data tidak dapat dilepas berdasarkan desain, untuk mencegah kehilangan data. Menghapus kunci membuat data yang dilindungi tidak dapat diambil. Mengingat ukurannya yang kecil, akumulasi kunci ini umumnya menimbulkan dampak minimal. Namun, untuk mengakomodasi layanan yang sangat berjalan lama, kami telah memperkenalkan opsi untuk menghapus kunci. Umumnya, hanya kunci lama yang harus dihapus. Hanya hapus kunci saat Anda dapat menerima risiko kehilangan data dengan imbalan penghematan penyimpanan. Sebaiknya kunci perlindungan data tidak boleh dihapus.
using Microsoft.AspNetCore.DataProtection.KeyManagement;
var services = new ServiceCollection();
services.AddDataProtection();
var serviceProvider = services.BuildServiceProvider();
var keyManager = serviceProvider.GetService<IKeyManager>();
if (keyManager is IDeletableKeyManager deletableKeyManager)
{
var utcNow = DateTimeOffset.UtcNow;
var yearAgo = utcNow.AddYears(-1);
if (!deletableKeyManager.DeleteKeys(key => key.ExpirationDate < yearAgo))
{
Console.WriteLine("Failed to delete keys.");
}
else
{
Console.WriteLine("Old keys deleted successfully.");
}
}
else
{
Console.WriteLine("Key manager does not support deletion.");
}
Middleware mendukung Keyed DI
Middleware sekarang mendukung Keyed DI di konstruktor dan Invoke
/InvokeAsync
metode :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<MySingletonClass>("test");
builder.Services.AddKeyedScoped<MyScopedClass>("test2");
var app = builder.Build();
app.UseMiddleware<MyMiddleware>();
app.Run();
internal class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next,
[FromKeyedServices("test")] MySingletonClass service)
{
_next = next;
}
public Task Invoke(HttpContext context,
[FromKeyedServices("test2")]
MyScopedClass scopedService) => _next(context);
}
Percayai sertifikat pengembangan HTTPS inti ASP.NET di Linux
Pada distro Linux berbasis Ubuntu dan Fedora, dotnet dev-certs https --trust
sekarang mengonfigurasi sertifikat pengembangan ASP.NET Core HTTPS sebagai sertifikat tepercaya untuk:
- Browser Chromium, misalnya, Google Chrome, Microsoft Edge, dan Chromium.
- Mozilla Firefox dan Mozilla memperoleh browser.
- API .NET, misalnya, HttpClient
Sebelumnya, --trust
hanya bekerja pada Windows dan macOS. Kepercayaan sertifikat diterapkan per pengguna.
Untuk membangun kepercayaan pada OpenSSL, alat ini dev-certs
:
- Memasukkan sertifikat
~/.aspnet/dev-certs/trust
- Menjalankan versi alat c_rehash OpenSSL yang disederhanakan pada direktori.
- Meminta pengguna untuk memperbarui
SSL_CERT_DIR
variabel lingkungan.
Untuk membangun kepercayaan pada dotnet, alat ini menempatkan sertifikat di penyimpanan My/Root
sertifikat.
Untuk membangun kepercayaan dalam database NSS, jika ada, alat ini mencari home direktori untuk profil Firefox, ~/.pki/nssdb
, dan ~/snap/chromium/current/.pki/nssdb
. Untuk setiap direktori yang ditemukan, alat menambahkan entri ke nssdb
.
Templat yang diperbarui ke versi Validasi Bootstrap, jQuery, dan jQuery terbaru
Templat dan pustaka proyek ASP.NET Core telah diperbarui untuk menggunakan versi terbaru Bootstrap, jQuery, dan Validasi jQuery, khususnya:
- Bootstrap 5.3.3
- jQuery 3.7.1
- Validasi jQuery 1.21.0
ASP.NET Core