Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Artikel ini menjelaskan cara mengonfigurasi dan mengelola SignalR koneksi di Blazor aplikasi.
Untuk panduan umum tentang konfigurasi ASP.NET CoreSignalR, lihat topik di bagian Ikhtisar ASP.NET CoreSignalR dalam dokumentasi, terutama konfigurasi ASP.NET CoreSignalR.
Aplikasi sisi server menggunakan ASP.NET Core SignalR untuk berkomunikasi dengan browser. SignalRKondisi berlaku untuk hosting dan penskalakan pada aplikasi sisi server.
Blazor berfungsi paling baik saat menggunakan WebSocket sebagai SignalR transportasi karena latensi, keandalan, dan keamanan yang lebih rendah. Polling Panjang diimplementasikan oleh SignalR ketika WebSockets tidak tersedia atau aplikasi dikonfigurasi secara eksplisit untuk menggunakan Polling Panjang.
Layanan Azure SignalR dengan koneksi ulang berstatus pemulihan
Azure SignalR Service dengan SDK v1.26.1 atau yang lebih baru mendukung SignalR (WithStatefulReconnect).
Kompresi WebSocket untuk komponen Server Interaktif
Secara default, komponen Server Interaktif:
Aktifkan pemadatan untuk koneksi WebSocket. DisableWebSocketCompression (default:
false) mengontrol kompresi WebSocket.frame-ancestorsMengadopsi direktif Kebijakan Keamanan Konten (CSP) yang diatur ke'self', yang adalah pengaturan bawaan dan hanya mengizinkan penyematan aplikasi di<iframe>dari asal tempat aplikasi disajikan ketika pemadatan diaktifkan atau ketika konfigurasi untuk konteks WebSocket disediakan.
CSP default frame-ancestors dapat diubah dengan mengatur nilai ContentSecurityFrameAncestorsPolicy ke null jika Anda ingin mengonfigurasi CSP dengan cara terpusat atau 'none' untuk kebijakan yang lebih ketat.
frame-ancestors Saat CSP dikelola dengan cara terpusat, perhatian harus diberikan untuk menerapkan kebijakan setiap kali dokumen pertama dirender. Kami tidak menyarankan untuk menghapus kebijakan sepenuhnya, karena akan membuat aplikasi rentan terhadap serangan. Untuk informasi selengkapnya, lihat Menerapkan Kebijakan Keamanan Konten untuk ASP.NET Core Blazor dan Panduan CSP MDN.
Gunakan ConfigureWebSocketAcceptContext untuk mengonfigurasi WebSocketAcceptContext untuk koneksi WebSocket yang digunakan oleh komponen server. Secara default, kebijakan yang memungkinkan kompresi dan mengatur CSP untuk leluhur bingkai yang ditentukan dalam ContentSecurityFrameAncestorsPolicy diterapkan.
Contoh penggunaan:
Nonaktifkan kompresi dengan mengatur DisableWebSocketCompression ke true, yang mengurangi kerentanan aplikasi untuk menyerang tetapi dapat mengakibatkan penurunan performa:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.DisableWebSocketCompression = true)
Saat kompresi diaktifkan, 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>:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")
Saat pemadatan diaktifkan, hapus frame-ancestors CSP dengan mengatur ContentSecurityFrameAncestorsPolicy ke null. Skenario ini hanya direkomendasikan untuk aplikasi yang mengatur CSP dengan cara terpusat:
builder.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = null)
Penting
Browser menerapkan arahan CSP dari beberapa header CSP menggunakan nilai arahan kebijakan paling ketat. Oleh karena itu, pengembang tidak dapat secara sengaja maupun tidak sengaja menambahkan kebijakan yang lebih lemah dari frame-ancestors daripada 'self'.
Tanda kutip tunggal diperlukan pada nilai string yang diteruskan ke ContentSecurityFrameAncestorsPolicy:
Nilai yang tidak didukung:none, self
Nilai yang didukung:'none', 'self'
Opsi tambahan termasuk menentukan satu atau beberapa sumber host dan sumber skema.
Untuk implikasi keamanan, lihat pedoman mitigasi ancaman untuk ASP.NET Core Blazor rendering sisi server interaktif. Untuk informasi selengkapnya, lihat Menerapkan Kebijakan Keamanan Konten untuk ASP.NET Core Blazor dan CSP: frame-ancestors (dokumentasi MDN).
SignalR
ConfigureConnection untuk komponen Server Interaktif
Blazor menyediakan akses untuk mengonfigurasi opsi koneksi SignalR yang mendasari saat menggunakan komponen Interactive Server melalui properti ConfigureConnection pada ServerComponentsEndpointOptions. Ini memungkinkan konfigurasi properti HttpConnectionDispatcherOptions.
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection = dispatcherOptions =>
{
dispatcherOptions.CloseOnAuthenticationExpiration = true;
dispatcherOptions.AllowStatefulReconnects = true;
dispatcherOptions.ApplicationMaxBufferSize = 1024 * 1024;
};
});
Ini menyediakan API yang rapi dan aman dari segi tipe untuk mengonfigurasi pengaturan koneksi SignalR tanpa perlu memeriksa metadata endpoint.
Menonaktifkan kompresi respons untuk Hot Reload
Saat menggunakan Hot Reload, nonaktifkan Middleware Kompresi Respons di Development lingkungan. Apakah kode default dari templat proyek digunakan atau tidak, selalu panggil UseResponseCompression terlebih dahulu dalam alur pemrosesan permintaan.
Dalam file Program:
if (!app.Environment.IsDevelopment())
{
app.UseResponseCompression();
}
Negosiasi lintas asal SignalR sisi klien untuk autentikasi
Bagian ini menjelaskan cara mengonfigurasi klien dasar SignalR untuk mengirimkan kredensial, seperti cookie atau header otentikasi HTTP.
Gunakan SetBrowserRequestCredentials untuk mengatur Include permintaan asal lintas fetch.
IncludeRequestCredentialsMessageHandler.cs:
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Http;
public class IncludeRequestCredentialsMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
return base.SendAsync(request, cancellationToken);
}
}
Saat koneksi hub dibangun, tetapkan HttpMessageHandler ke opsi HttpMessageHandlerFactory.
private HubConnectionBuilder? hubConnection;
...
hubConnection = new HubConnectionBuilder()
.WithUrl(new Uri(Navigation.ToAbsoluteUri("/chathub")), options =>
{
options.HttpMessageHandlerFactory = innerHandler =>
new IncludeRequestCredentialsMessageHandler { InnerHandler = innerHandler };
}).Build();
Contoh sebelumnya mengonfigurasi URL koneksi hub ke alamat URI absolut di /chathub. URI juga dapat diatur melalui string, misalnya https://signalr.example.com, atau melalui konfigurasi.
Untuk informasi selengkapnya, lihat konfigurasi SignalR ASP.NET Core.
Penyajian sisi klien
Jika pra-penyajian dikonfigurasi, pra-penyajian terjadi sebelum koneksi klien ke server dibuat. Untuk informasi selengkapnya, lihat ASP.NET Blazor Persistensi status core yang telah dirender sebelumnya.
Jika pra-penyajian dikonfigurasi, pra-penyajian terjadi sebelum koneksi klien ke server dibuat. Untuk informasi lebih lanjut, baca artikel berikut:
Ukuran status dan SignalR batas ukuran pesan yang telah dirender sebelumnya
Ukuran status besar yang telah dirender mungkin melebihi batas ukuran pesan sirkuit Blazor, SignalR yang mengakibatkan hal berikut:
- SignalR Sirkuit gagal diinisialisasi dengan kesalahan pada klien:Circuit host not initialized.
- Antarmuka pengguna pemulihan koneksi pada klien muncul ketika sirkuit gagal. Pemulihan tidak dimungkinkan.
Untuk mengatasi masalah tersebut, gunakan salah satu pendekatan berikut:
- Kurangi jumlah data yang Anda masukkan ke dalam status yang telah dirender sebelumnya.
- Tingkatkan SignalR batas ukuran pesan. PERINGATAN: Meningkatkan batas dapat meningkatkan risiko serangan Denial of Service (DoS).
Sumber daya sisi klien tambahan
- Mengamankan SignalR hub
- Gambaran umum ASP.NET Core SignalR
- Konfigurasi ASP.NET Core SignalR
-
Blazor (
dotnet/blazor-samples) (cara mengunduh)
Gunakan afinitas sesi (sesi lekat) untuk hosting farm web sisi server
Ketika lebih dari satu server backend sedang digunakan, aplikasi harus menerapkan afinitas sesi, yang juga disebut sticky sessions. Afinitas sesi memastikan bahwa sirkuit klien terhubung kembali ke server yang sama jika koneksi terputus, yang penting karena status klien hanya disimpan dalam memori server yang pertama kali menetapkan sirkuit klien.
Kesalahan berikut terjadi pada aplikasi yang belum mengaktifkan afinitas sesi di lingkungan web.
Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed.
Untuk informasi selengkapnya tentang afinitas sesi dengan hosting Azure App Service, lihat Host dan deploy aplikasi sisi server Blazor ASP.NET Core.
Layanan Azure SignalR
Layanan Azure SignalR opsional berfungsi bersama dengan hub aplikasi SignalR untuk meningkatkan skala aplikasi sisi server ke sejumlah besar koneksi bersamaan. Selain itu, jangkauan global layanan dan pusat data berkinerja tinggi secara signifikan membantu mengurangi latensi karena geografi.
Layanan ini tidak diperlukan untuk Blazor aplikasi yang dihosting di Azure App Service atau Azure Container Apps tetapi dapat membantu di lingkungan hosting lainnya:
- Untuk memfasilitasi peluasan skala koneksi.
- Menangani distribusi global.
Untuk informasi selengkapnya, lihat Menjalankan dan mengelola aplikasi ASP.NET Core sisi serverBlazor.
Opsi penanganan jalur sisi server
Konfigurasikan sirkuit dengan CircuitOptions. Lihat nilai default di sumber referensi.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Baca atau atur opsi dalam file Program dengan opsi delegasi ke AddInteractiveServerComponents.
{OPTION} penampung mewakili opsi, dan {VALUE} penampung adalah nilainya.
Dalam file Program:
builder.Services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
options.{OPTION} = {VALUE};
});
Baca atau atur opsi dalam file Program dengan opsi delegasi ke AddServerSideBlazor.
{OPTION} penampung mewakili opsi, dan {VALUE} penampung adalah nilainya.
Dalam file Program:
builder.Services.AddServerSideBlazor(options =>
{
options.{OPTION} = {VALUE};
});
Baca atau atur opsi di Startup.ConfigureServices dengan menggunakan opsi delegasi untuk AddServerSideBlazor.
{OPTION} penampung mewakili opsi, dan {VALUE} penampung adalah nilainya.
Dalam Startup.ConfigureServices dari Startup.cs:
services.AddServerSideBlazor(options =>
{
options.{OPTION} = {VALUE};
});
Untuk mengonfigurasi HubConnectionContext, gunakan HubConnectionContextOptions dengan AddHubOptions. Lihat default untuk opsi konteks koneksi hub di referensi sumber. Untuk deskripsi opsi dalam dokumentasi {OPTION} penampung mewakili opsi, dan {VALUE} penampung adalah nilainya.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Dalam file Program:
builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
Dalam file Program:
builder.Services.AddServerSideBlazor().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
Dalam Startup.ConfigureServices dari Startup.cs:
services.AddServerSideBlazor().AddHubOptions(options =>
{
options.{OPTION} = {VALUE};
});
Peringatan
Nilai MaximumReceiveMessageSize default adalah 32 KB. Meningkatkan nilai dapat meningkatkan risiko serangan Denial of Service (DoS).
Blazor bergantung pada MaximumParallelInvocationsPerClient yang diatur ke 1, yang merupakan nilai default. Untuk informasi selengkapnya, lihat MaximumParallelInvocationsPerClient > 1 memutus unggahan file dalam Blazor Server mode (dotnet/aspnetcore #53951).
Untuk informasi tentang manajemen memori, lihat Mengelola memori di aplikasi sisi Blazor server ASP.NET Core yang disebarkan.
Blazor opsi hub
Konfigurasikan MapBlazorHub opsi untuk mengontrol HttpConnectionDispatcherOptions dari Blazor hub. Lihat default untuk opsi dispatcher koneksi hub di referensi sumber.
{OPTION} penampung mewakili opsi, dan {VALUE} penampung adalah nilainya.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Lakukan panggilan app.MapBlazorHub setelah panggilan ke app.MapRazorComponents dalam file aplikasi Program :
app.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
Mengonfigurasi hub yang digunakan oleh AddInteractiveServerRenderMode dengan MapBlazorHub gagal dengan AmbiguousMatchException:
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
Untuk mengatasi masalah aplikasi yang menargetkan .NET 8/9, lakukan pendekatan berikut.
Di bagian atas file Program, tambahkan statement using untuk Microsoft.AspNetCore.Http.Connections:
using Microsoft.AspNetCore.Http.Connections;
Ketika MapRazorComponents dipanggil, sambungkan konvensi titik akhir berikut ke RazorComponentsEndpointConventionBuilder:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.Add(e =>
{
var metadata = e.Metadata;
var dispatcherOptions = metadata.OfType<HttpConnectionDispatcherOptions>().FirstOrDefault();
if (dispatcherOptions != null)
{
dispatcherOptions.CloseOnAuthenticationExpiration = true;
}
});
Untuk informasi selengkapnya, lihat sumber daya berikut:
-
Konfigurasi MapBlazorHub di NET8 menimbulkan pengecualian permintaan mencocokkan beberapa titik akhir (
dotnet/aspnetcore#51698) - Upaya untuk memetakan beberapa titik entri blazor dengan MapBlazorHub menyebabkan Kesalahan Rute Ambigu. Ini bekerja dengan net7 (
dotnet/aspnetcore#52156) -
[Blazor] Menyediakan akses ke yang mendasar SignalR HttpConnectionDispatcherOptions di AddInteractiveServerRenderMode (
dotnet/aspnetcore#63520)
Berikan opsi ke app.MapBlazorHub dalam file aplikasi Program :
app.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
Berikan opsi ke app.MapBlazorHub dalam konfigurasi perutean titik akhir:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub(options =>
{
options.{OPTION} = {VALUE};
});
...
});
Ukuran pesan penerimaan maksimum
Bagian ini hanya berlaku untuk proyek yang mengimplementasikan SignalR.
Ukuran pesan masuk SignalR maksimum yang diizinkan untuk metode hub dibatasi oleh HubOptions.MaximumReceiveMessageSize (default: 32 KB). SignalR pesan yang lebih besar dari MaximumReceiveMessageSize melemparkan kesalahan. Kerangka kerja tidak memberlakukan batasan SignalR ukuran pesan dari hub ke klien.
Saat SignalR pengelogan tidak diatur ke Debug atau Pelacakan, kesalahan ukuran pesan hanya muncul di konsol alat pengembang browser:
Kesalahan: Koneksi terputus dengan kesalahan 'Kesalahan: Server memberikan kesalahan saat penutupan: Koneksi ditutup dengan kesalahan.'.
Saat SignalR logging sisi server diatur ke Debug atau Trace, logging sisi server menampilkan InvalidDataException untuk kesalahan ukuran pesan.
appsettings.Development.json:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
...
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Kesalahan:
System.IO.InvalidDataException: Ukuran pesan maksimum 32768B terlampaui. Ukuran pesan dapat dikonfigurasi di AddHubOptions.
Salah satu pendekatan melibatkan peningkatan batas dengan mengatur MaximumReceiveMessageSize dalam Program file. Contoh berikut mengatur ukuran pesan penerimaan maksimum menjadi 64 KB:
builder.Services.AddRazorComponents().AddInteractiveServerComponents()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
SignalR Meningkatkan batas ukuran pesan masuk dikenakan biaya untuk membutuhkan lebih banyak sumber daya server, dan meningkatkan risiko serangan Denial of Service (DoS). Selain itu, membaca sejumlah besar konten ke memori sebagai string atau array byte juga dapat mengakibatkan alokasi yang bekerja dengan buruk dengan pengumpul sampah, yang mengakibatkan hukuman performa tambahan.
Opsi yang lebih baik untuk membaca payload besar adalah mengirim konten dalam gugus yang lebih kecil dan memproses payload sebagai Stream. Ini dapat digunakan saat membaca payload JSON interop JavaScript (JS) besar atau jika JS data interop tersedia sebagai byte mentah. Sebagai contoh yang menunjukkan pengiriman payload biner besar di aplikasi sisi server yang menggunakan teknik serupa dengan komponen InputFile, lihat Aplikasi Contoh Binary Submit dan BlazorInputLargeTextArea Contoh Komponen.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Formulir yang memproses payload besar melalui SignalR juga dapat menggunakan interop streaming JS secara langsung. Untuk informasi selengkapnya, lihat Memanggil metode .NET dari fungsi JavaScript di ASP.NET Core Blazor. Untuk contoh formulir yang mengalirkan <textarea> data ke server, lihat Pemecahan Masalah Formulir ASP.NET CoreBlazor.
Salah satu pendekatan melibatkan peningkatan batas dengan mengatur MaximumReceiveMessageSize dalam Program file. Contoh berikut mengatur ukuran pesan penerimaan maksimum menjadi 64 KB:
builder.Services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
SignalR Meningkatkan batas ukuran pesan masuk dikenakan biaya untuk membutuhkan lebih banyak sumber daya server, dan meningkatkan risiko serangan Denial of Service (DoS). Selain itu, membaca sejumlah besar konten ke memori sebagai string atau array byte juga dapat mengakibatkan alokasi yang bekerja dengan buruk dengan pengumpul sampah, yang mengakibatkan hukuman performa tambahan.
Opsi yang lebih baik untuk membaca payload besar adalah mengirim konten dalam gugus yang lebih kecil dan memproses payload sebagai Stream. Ini dapat digunakan saat membaca payload JSON interop JavaScript (JS) besar atau jika JS data interop tersedia sebagai byte mentah. Sebagai contoh yang menunjukkan pengiriman payload biner besar dalam Blazor Server yang menggunakan teknik mirip dengan komponen InputFile, lihat aplikasi contoh Binary Submit dan contoh Komponen BlazorInputLargeTextArea.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Formulir yang memproses payload besar melalui SignalR juga dapat menggunakan interop streaming JS secara langsung. Untuk informasi selengkapnya, lihat Memanggil metode .NET dari fungsi JavaScript di ASP.NET Core Blazor. Untuk contoh formulir yang mengalirkan data <textarea> dalam aplikasi Blazor Server, lihat Troubleshoot ASP.NET Core Blazor forms.
Tingkatkan batas dengan mengatur MaximumReceiveMessageSize di Startup.ConfigureServices:
services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
SignalR Meningkatkan batas ukuran pesan masuk dikenakan biaya untuk membutuhkan lebih banyak sumber daya server, dan meningkatkan risiko serangan Denial of Service (DoS). Selain itu, membaca sejumlah besar konten ke memori sebagai string atau array byte juga dapat mengakibatkan alokasi yang bekerja dengan buruk dengan pengumpul sampah, yang mengakibatkan hukuman performa tambahan.
Pertimbangkan panduan berikut saat mengembangkan kode yang mentransfer sejumlah besar data:
- Manfaatkan dukungan interop streaming JS asli untuk mentransfer data yang lebih besar dari SignalR batas ukuran pesan masuk:
- Tips umum:
- Jangan alokasikan objek besar dalam JS dan kode C#.
- Memori yang digunakan akan dibebaskan ketika proses selesai atau dibatalkan.
- Berlakukan persyaratan tambahan berikut untuk tujuan keamanan:
- Deklarasikan file maksimum atau ukuran data yang dapat diteruskan.
- Nyatakan tingkat unggahan minimum dari klien ke server.
- Setelah data diterima oleh server, data dapat berupa:
- Disimpan sementara dalam buffer memori hingga semua segmen dikumpulkan.
- Segera dikonsumsi. Misalnya, data dapat segera disimpan dalam database atau ditulis ke disk saat setiap segmen diterima.
- Ikat data menjadi potongan yang lebih kecil, dan kirim segmen data secara berurutan hingga semua data diterima oleh server.
- Jangan alokasikan objek besar dalam JS dan kode C#.
- Jangan memblokir utas UI utama untuk jangka waktu yang lama saat mengirim atau menerima data.
- Memori yang digunakan akan dibebaskan ketika proses selesai atau dibatalkan.
- Berlakukan persyaratan tambahan berikut untuk tujuan keamanan:
- Deklarasikan file maksimum atau ukuran data yang dapat diteruskan.
- Nyatakan tingkat unggahan minimum dari klien ke server.
- Setelah data diterima oleh server, data dapat berupa:
- Disimpan sementara dalam buffer memori hingga semua segmen dikumpulkan.
- Segera dikonsumsi. Misalnya, data dapat segera disimpan dalam database atau ditulis ke disk saat setiap segmen diterima.
Blazor konfigurasi rute titik akhir Hub sisi server
Dalam file Program, panggil MapBlazorHub untuk memetakan BlazorHub ke jalur bawaan aplikasi. Skrip Blazor(blazor.*.js) secara otomatis mengarahkan ke endpoint yang dibuat oleh MapBlazorHub.
Mencerminkan status koneksi sisi server di UI
Jika klien mendeteksi koneksi (sirkuit) yang hilang ke server, UI default ditampilkan kepada pengguna saat klien mencoba menyambungkan kembali:
Jika koneksi ulang gagal, pengguna diinstruksikan untuk mencoba kembali atau memuat ulang halaman:
Jika koneksi ulang berhasil, status pengguna sering hilang. Kode kustom dapat ditambahkan ke komponen apa pun untuk menyimpan dan memuat ulang status pengguna di seluruh kegagalan koneksi. Untuk informasi selengkapnya, lihat gambaran umum manajemen status inti Blazor ASP.NET dan manajemen status sisi server inti Blazor ASP.NET.
Untuk membuat elemen UI yang melacak status koneksi ulang, tabel berikut ini menjelaskan:
- Sekumpulan
components-reconnect-*kelas CSS (kolom kelas CSS) yang ditetapkan atau dihapus oleh Blazor pada elemen denganiddaricomponents-reconnect-modal. -
components-reconnect-state-changedPeristiwa (Kolom peristiwa) yang menunjukkan perubahan status koneksi ulang.
| Kelas CSS | Peristiwa | Menunjukkan... |
|---|---|---|
components-reconnect-show |
show |
Koneksi yang hilang. Klien mencoba menyambungkan kembali. Modal koneksi ulang ditampilkan. |
components-reconnect-paused |
paused |
Koneksi sedang dijeda. Untuk informasi selengkapnya, lihat Menjeda dan melanjutkan sirkuit. |
components-reconnect-hide |
hide |
Koneksi aktif dibuat ulang ke server. Model koneksi ulang ditutup. |
components-reconnect-retrying |
retrying |
Klien mencoba menyambungkan kembali. |
components-reconnect-failed |
failed |
Koneksi ulang gagal, mungkin karena kegagalan jaringan. |
components-reconnect-rejected |
rejected |
Koneksi ulang ditolak. |
Ketika status koneksi ulang berubah components-reconnect-state-changed menjadi failed, gunakan Blazor.reconnect() di JavaScript untuk mencoba menyambungkan kembali.
Ketika perubahan status koneksi ulang adalah rejected, server dihubungi namun menolak koneksi, dan status koneksi pengguna di server hilang. Untuk memuat ulang aplikasi, panggil location.reload() di JavaScript. Status koneksi ini dapat mengakibatkan ketika:
- Terjadi crash pada sirkuit sisi server.
- Klien terputus cukup lama sehingga server menghapus status pengguna. Instans komponen pengguna dibuang.
- Server dimulai ulang, atau proses kerja aplikasi diulang.
Pengembang menambahkan pendengar peristiwa pada elemen modal koneksi ulang untuk memantau dan bereaksi terhadap perubahan status koneksi ulang, seperti yang terlihat dalam contoh berikut:
const reconnectModal = document.getElementById("components-reconnect-modal");
reconnectModal.addEventListener("components-reconnect-state-changed",
handleReconnectStateChanged);
function handleReconnectStateChanged(event) {
if (event.detail.state === "show") {
reconnectModal.showModal();
} else if (event.detail.state === "hide") {
reconnectModal.close();
} else if (event.detail.state === "failed") {
Blazor.reconnect();
} else if (event.detail.state === "rejected") {
location.reload();
}
}
Elemen dengan id senilai components-reconnect-max-retries menampilkan jumlah maksimum percobaan koneksi ulang.
<span id="components-reconnect-max-retries"></span>
Elemen dengan id dari components-reconnect-current-attempt menampilkan upaya koneksi ulang saat ini:
<span id="components-reconnect-current-attempt"></span>
Elemen yang memiliki id dari components-seconds-to-next-attempt menampilkan jumlah detik ke sesi koneksi ulang berikutnya.
<span id="components-seconds-to-next-attempt"></span>
Blazor Web App Templat proyek mencakup ReconnectModal komponen (Components/Layout/ReconnectModal.razor) dengan lembar gaya yang dikolokasi dan file JavaScript (ReconnectModal.razor.css, ReconnectModal.razor.js) yang dapat disesuaikan sesuai kebutuhan. File-file ini dapat diperiksa di sumber referensi ASP.NET Core atau dengan memeriksa aplikasi yang dibuat dari Blazor Web App templat proyek. Komponen ditambahkan ke proyek ketika proyek dibuat di Visual Studio dengan mode render Interaktif diatur ke Server atau Otomatis atau dibuat dengan .NET CLI dengan opsi --interactivity server (default) atau --interactivity auto.
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Untuk mengkustomisasi UI, tentukan satu elemen dengan id dari components-reconnect-modal di dalam konten elemen <body>. Contoh berikut menempatkan elemen dalam App komponen.
App.razor:
Untuk mengkustomisasi UI, tentukan satu elemen dengan id dari components-reconnect-modal di dalam konten elemen <body>. Contoh berikut menempatkan elemen di halaman host.
Pages/_Host.cshtml:
Untuk mengkustomisasi UI, tentukan satu elemen dengan id dari components-reconnect-modal di dalam konten elemen <body>. Contoh berikut menempatkan elemen di halaman tata letak.
Pages/_Layout.cshtml:
Untuk mengkustomisasi UI, tentukan satu elemen dengan id dari components-reconnect-modal di dalam konten elemen <body>. Contoh berikut menempatkan elemen di halaman host.
Pages/_Host.cshtml:
<div id="components-reconnect-modal">
Connection lost.<br>Attempting to reconnect...
</div>
Catatan
Jika lebih dari satu elemen dengan id dari components-reconnect-modal dirender oleh aplikasi, hanya elemen pertama yang dirender yang menerima perubahan kelas CSS untuk menampilkan atau menyembunyikan elemen tersebut.
Tambahkan gaya CSS berikut ke lembar gaya situs.
wwwroot/app.css:
wwwroot/css/site.css:
#components-reconnect-modal {
display: none;
}
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
display: block;
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
text-align: center;
box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
margin: 50px 50px;
position: fixed;
top: 0;
z-index: 10001;
}
Tabel berikut ini menjelaskan kelas CSS yang diterapkan ke components-reconnect-modal elemen oleh Blazor kerangka kerja.
| Kelas CSS | Menunjukkan... |
|---|---|
components-reconnect-show |
Koneksi yang hilang. Klien mencoba menyambungkan kembali. Tampilkan modal. |
components-reconnect-hide |
Koneksi aktif dibuat ulang ke server. Sembunyikan modal. |
components-reconnect-failed |
Koneksi ulang gagal, mungkin karena kegagalan jaringan. Untuk mencoba menyambungkan kembali, panggil window.Blazor.reconnect() di JavaScript. |
components-reconnect-rejected |
Koneksi ulang ditolak. Server telah diakses namun menolak koneksi, dan status pengguna di server hilang. Untuk memuat ulang aplikasi, panggil location.reload() di JavaScript. Status koneksi ini dapat mengakibatkan ketika:
|
Sesuaikan penundaan sebelum antarmuka pengguna untuk koneksi ulang muncul dengan mengatur properti transition-delay di CSS situs untuk elemen modal. Contoh berikut mengatur penundaan transisi dari 500 md (default) menjadi 1.000 md (1 detik).
wwwroot/app.css:
wwwroot/css/site.css:
#components-reconnect-modal {
transition: visibility 0s linear 1000ms;
}
Untuk menampilkan upaya koneksi ulang saat ini, tentukan elemen dengan idcomponents-reconnect-current-attempt. Untuk menampilkan jumlah maksimum percobaan ulang koneksi, tentukan elemen dengan id dan components-reconnect-max-retries. Contoh berikut menempatkan elemen ini di dalam elemen modal percobaan koneksi ulang berdasarkan contoh sebelumnya.
<div id="components-reconnect-modal">
There was a problem with the connection!
(Current reconnect attempt:
<span id="components-reconnect-current-attempt"></span> /
<span id="components-reconnect-max-retries"></span>)
</div>
Saat modal sambungan ulang kustom muncul, ini menampilkan konten berikut dengan penghitung jumlah upaya sambungan ulang.
Ada masalah dengan koneksi! (Upaya koneksi ulang saat ini: 1 / 8)
Renderisasi Sisi Server
Secara default, komponen telah dirender di server sebelum koneksi klien ke server dibuat. Untuk informasi selengkapnya, lihat ASP.NET Blazor Persistensi status core yang telah dirender sebelumnya.
Secara default, komponen telah dirender di server sebelum koneksi klien ke server dibuat. Untuk informasi selengkapnya, lihat Pembantu Tag Komponen di ASP.NET Core.
Memantau aktivitas sirkuit sisi server
Pantau aktivitas sirkuit masuk menggunakan CreateInboundActivityHandler metode pada CircuitHandler. Aktivitas sirkuit masuk adalah aktivitas apa pun yang dikirim dari browser ke server, seperti peristiwa UI atau panggilan interop JavaScript-to-.NET.
Misalnya, Anda dapat menggunakan handler aktivitas sirkuit untuk mendeteksi apakah klien menganggur dan mencatat ID sirkuitnya (Circuit.Id):
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.Options;
using Timer = System.Timers.Timer;
public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
private Circuit? currentCircuit;
private readonly ILogger logger;
private readonly Timer timer;
public IdleCircuitHandler(ILogger<IdleCircuitHandler> logger,
IOptions<IdleCircuitOptions> options)
{
timer = new Timer
{
Interval = options.Value.IdleTimeout.TotalMilliseconds,
AutoReset = false
};
timer.Elapsed += CircuitIdle;
this.logger = logger;
}
private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
{
logger.LogInformation("{CircuitId} is idle", currentCircuit?.Id);
}
public override Task OnCircuitOpenedAsync(Circuit circuit,
CancellationToken cancellationToken)
{
currentCircuit = circuit;
return Task.CompletedTask;
}
public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
Func<CircuitInboundActivityContext, Task> next)
{
return context =>
{
timer.Stop();
timer.Start();
return next(context);
};
}
public void Dispose() => timer.Dispose();
}
public class IdleCircuitOptions
{
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(5);
}
public static class IdleCircuitHandlerServiceCollectionExtensions
{
public static IServiceCollection AddIdleCircuitHandler(
this IServiceCollection services,
Action<IdleCircuitOptions> configureOptions)
{
services.Configure(configureOptions);
services.AddIdleCircuitHandler();
return services;
}
public static IServiceCollection AddIdleCircuitHandler(
this IServiceCollection services)
{
services.AddScoped<CircuitHandler, IdleCircuitHandler>();
return services;
}
}
Daftarkan layanan dalam Program file. Contoh berikut mengonfigurasi batas waktu menganggur default lima menit hingga lima detik untuk menguji implementasi sebelumnya IdleCircuitHandler :
builder.Services.AddIdleCircuitHandler(options =>
options.IdleTimeout = TimeSpan.FromSeconds(5));
Penangan aktivitas sirkuit juga menyediakan pendekatan untuk mengakses layanan berlingkup Blazor dari ruang lingkup non-injeksi ketergantungan Blazor (DI) lainnya. Untuk informasi dan contoh selengkapnya, lihat:
Blazor Startup
Konfigurasikan memulai manual sirkuit BlazorSignalR di dalam file App.razor dari Blazor Web App.
Konfigurasikan pengaturan awal manual BlazorSignalR sirkuit dalam file Pages/_Host.cshtml (Blazor Server):
Konfigurasikan pengaturan awal manual BlazorSignalR sirkuit dalam file Pages/_Layout.cshtml (Blazor Server):
Konfigurasikan pengaturan awal manual BlazorSignalR sirkuit dalam file Pages/_Host.cshtml (Blazor Server):
- Tambahkan atribut
autostart="false"ke tag<script>pada skripblazor.*.js. - Tempatkan skrip yang memanggil
Blazor.start()setelah Blazor skrip dimuat dan di dalam tag penutup</body>.
Saat autostart dinonaktifkan, aspek apa pun dari aplikasi yang tidak bergantung pada sirkuit berfungsi secara normal. Misalnya, perutean sisi klien berfungsi. Namun, aspek apa pun yang tergantung pada sirkuit tidak beroperasi sampai Blazor.start() dipanggil. Perilaku aplikasi tidak dapat diprediksi tanpa sirkuit yang mapan. Misalnya, metode komponen gagal dijalankan saat sirkuit terputus.
Untuk informasi selengkapnya, termasuk cara menginisialisasi Blazor saat dokumen siap dan cara menautkan ke JS Promise, lihat ASP.NET Core Blazor startup.
Mengonfigurasi SignalR batas waktu dan Keep-Alive pada klien
Konfigurasikan nilai berikut untuk klien:
-
withServerTimeout: Mengonfigurasi batas waktu server dalam milidetik. Jika batas waktu ini berlalu tanpa menerima pesan apa pun dari server, koneksi dihentikan dengan kesalahan. Nilai batas waktu default adalah 30 detik. Waktu batas server harus setidaknya dua kali lipat dari nilai yang ditetapkan untuk interval Keep-Alive (withKeepAliveInterval). -
withKeepAliveInterval: Mengonfigurasi interval Keep-Alive dalam milidetik (interval default untuk melakukan ping server). Pengaturan ini memungkinkan server mendeteksi pemutusan sambungan keras, seperti ketika klien mencabut sambungan komputernya dari jaringan. Ping terjadi paling sering sama seperti ping server. Jika server melakukan ping setiap lima detik, menetapkan nilai yang lebih rendah dari5000(5 detik) akan tetap menyebabkan ping setiap lima detik. Nilai defaultnya adalah 15 detik. Interval Keep-Alive harus kurang dari atau sama dengan setengah nilai yang ditetapkan ke batas waktu server (withServerTimeout).
Contoh berikut untuk App.razor file (Blazor Web App) menunjukkan penetapan nilai default.
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
configureSignalR: function (builder) {
builder.withServerTimeout(30000).withKeepAliveInterval(15000);
}
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Contoh berikut untuk Pages/_Host.cshtml file (Blazor Server, semua versi kecuali ASP.NET Core di .NET 6) atau Pages/_Layout.cshtml file (Blazor Server, ASP.NET Core di .NET 6).
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
builder.withServerTimeout(30000).withKeepAliveInterval(15000);
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Saat membuat koneksi hub dalam komponen, atur ServerTimeout (default: 30 detik) dan KeepAliveInterval (default: 15 detik) pada HubConnectionBuilder. Atur HandshakeTimeout (default: 15 detik) pada bawaan HubConnection. Contoh berikut menunjukkan penetapan nilai default:
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.WithServerTimeout(TimeSpan.FromSeconds(30))
.WithKeepAliveInterval(TimeSpan.FromSeconds(15))
.Build();
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
Konfigurasikan nilai berikut untuk klien:
-
serverTimeoutInMilliseconds: Batas waktu server dalam milidetik. Jika batas waktu ini berlalu tanpa menerima pesan apa pun dari server, koneksi dihentikan dengan kesalahan. Nilai batas waktu default adalah 30 detik. Waktu batas server harus setidaknya dua kali lipat dari nilai yang ditetapkan untuk interval Keep-Alive (keepAliveIntervalInMilliseconds). -
keepAliveIntervalInMilliseconds: Interval default untuk melakukan ping pada server. Pengaturan ini memungkinkan server mendeteksi pemutusan sambungan keras, seperti ketika klien mencabut sambungan komputernya dari jaringan. Ping terjadi paling sering sama seperti ping server. Jika server melakukan ping setiap lima detik, menetapkan nilai yang lebih rendah dari5000(5 detik) akan tetap menyebabkan ping setiap lima detik. Nilai defaultnya adalah 15 detik. Interval Keep-Alive harus kurang dari atau sama dengan setengah nilai yang ditetapkan ke batas waktu server (serverTimeoutInMilliseconds).
Contoh berikut untuk Pages/_Host.cshtml file (Blazor Server, semua versi kecuali ASP.NET Core di .NET 6) atau Pages/_Layout.cshtml file (Blazor Server, ASP.NET Core di .NET 6):
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 30000;
c.keepAliveIntervalInMilliseconds = 15000;
builder.build = () => {
return c;
};
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Saat membuat koneksi hub dalam komponen, atur ServerTimeout (default: 30 detik), HandshakeTimeout (default: 15 detik), dan KeepAliveInterval (default: 15 detik) di bawaan HubConnection. Contoh berikut menunjukkan penetapan nilai default:
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromSeconds(30);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(15);
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(15);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
Saat mengubah nilai batas waktu server (ServerTimeout) atau interval Keep-Alive (KeepAliveInterval):
- Batas waktu server harus setidaknya dua kali lipat nilai yang diberikan kepada interval Keep-Alive.
- Interval Keep-Alive harus kurang dari atau sama dengan setengah nilai yang ditetapkan ke timeout server.
Untuk informasi selengkapnya, lihat bagian Penyebaran global dan kegagalan koneksi dari artikel berikut ini:
- Menghosting dan menyebarkan aplikasi sisi Blazor server ASP.NET Core
- Menghosting dan menyebarkan ASP.NET Core Blazor WebAssembly
Menyesuaikan jumlah dan interval upaya koneksi ulang pada sisi server
Untuk menyesuaikan jumlah dan interval coba lagi koneksi ulang, atur jumlah percobaan ulang (maxRetries) dan periode dalam milidetik yang diizinkan untuk setiap upaya coba lagi (retryIntervalMilliseconds).
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000
}
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
reconnectionOptions: {
maxRetries: 3,
retryIntervalMilliseconds: 2000
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Saat pengguna menavigasi kembali ke aplikasi dengan sirkuit yang terputus, koneksi ulang dicoba segera daripada menunggu durasi interval koneksi ulang berikutnya. Perilaku ini berusaha melanjutkan koneksi secepat mungkin untuk pengguna.
Waktu koneksi ulang default menggunakan strategi backoff yang dihitung. Beberapa upaya koneksi ulang pertama terjadi secara berturut-turut sebelum penundaan komputasi diperkenalkan di antara upaya. Logika bawaan untuk menghitung interval coba lagi adalah detail implementasi yang dapat berubah tanpa pemberitahuan sebelumnya, tetapi Anda dapat menemukan logika bawaan yang digunakan oleh kerangka kerja dalam fungsi Blazor (sumber referensi).
Catatan
Tautan dokumentasi ke sumber referensi .NET biasanya memuat cabang default repositori, yang mewakili pengembangan saat ini untuk rilis .NET berikutnya. Untuk memilih tag untuk rilis tertentu, gunakan menu dropdown Beralih cabang atau tag. Untuk informasi lebih lanjut, lihat Cara memilih tag versi kode sumber ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Sesuaikan perilaku interval coba lagi dengan menentukan fungsi untuk menghitung interval coba lagi. Dalam contoh backoff eksponensial berikut, jumlah usaha koneksi ulang sebelumnya dikalikan dengan 1.000 ms untuk menghitung interval percobaan ulang. Ketika jumlah upaya sebelumnya untuk menyambungkan kembali (previousAttempts) lebih besar dari batas coba lagi maksimum (maxRetries), null ditetapkan ke interval coba lagi (retryIntervalMilliseconds) untuk menghentikan upaya koneksi ulang lebih lanjut:
Blazor Web App:
Blazor.start({
circuit: {
reconnectionOptions: {
retryIntervalMilliseconds: (previousAttempts, maxRetries) =>
previousAttempts >= maxRetries ? null : previousAttempts * 1000
},
},
});
Blazor Server:
Blazor.start({
reconnectionOptions: {
retryIntervalMilliseconds: (previousAttempts, maxRetries) =>
previousAttempts >= maxRetries ? null : previousAttempts * 1000
},
});
Alternatifnya adalah menentukan urutan interval coba lagi yang tepat. Setelah interval coba lagi terakhir yang ditentukan, upaya coba diulang dihentikan karena fungsi retryIntervalMilliseconds mengembalikan undefined.
Blazor Web App:
Blazor.start({
circuit: {
reconnectionOptions: {
retryIntervalMilliseconds:
Array.prototype.at.bind([0, 1000, 2000, 5000, 10000, 15000, 30000]),
},
},
});
Blazor Server:
Blazor.start({
reconnectionOptions: {
retryIntervalMilliseconds:
Array.prototype.at.bind([0, 1000, 2000, 5000, 10000, 15000, 30000]),
},
});
Untuk informasi lebih lanjut tentang startup Blazor, lihat startup BlazorASP.NET Core.
Kontrol kapan antarmuka menghubungkan kembali muncul
Mengontrol kapan antarmuka pengguna koneksi ulang muncul dapat berguna dalam situasi berikut:
- Aplikasi yang disebarkan sering menampilkan antarmuka pengguna untuk koneksi ulang karena waktu habis ping yang disebabkan oleh jaringan internal atau latensi Internet, dan Anda ingin menambah jeda.
- Aplikasi harus melaporkan kepada pengguna bahwa koneksi telah turun lebih cepat, dan Anda ingin mempersingkat penundaan.
Waktu munculnya UI koneksi ulang dipengaruhi oleh penyesuaian interval Keep-Alive dan batas waktu pada klien. UI koneksi ulang muncul ketika batas waktu server tercapai pada klien (withServerTimeout, bagian Konfigurasi klien). Namun, mengubah nilai withServerTimeout memerlukan perubahan pada pengaturan Keep-Alive, timeout, dan handshake lainnya yang dijelaskan dalam panduan berikut.
Sebagai rekomendasi umum untuk petunjuk selanjutnya:
- Interval Keep-Alive harus cocok antara konfigurasi klien dan server.
- Batas waktu seharusnya minimal dua kali lipat dari nilai yang ditetapkan untuk interval Keep-Alive.
Konfigurasi server
Atur berikut ini:
- ClientTimeoutInterval (default: 30 detik): Jendela waktu bagi klien untuk mengirim pesan sebelum server menutup koneksi.
- HandshakeTimeout (default: 15 detik): Interval yang digunakan oleh server untuk waktu habis permintaan jabat tangan masuk oleh klien.
- KeepAliveInterval (default: 15 detik): Interval yang digunakan oleh server untuk mengirim ping tetap hidup ke klien yang terhubung. Perhatikan bahwa ada juga pengaturan interval Keep-Alive pada klien, yang harus cocok dengan nilai server.
ClientTimeoutInterval dan HandshakeTimeout dapat ditingkatkan, dan KeepAliveInterval dapat tetap sama. Pertimbangan pentingnya adalah bahwa jika Anda mengubah nilai, pastikan bahwa waktu menunggu setidaknya dua kali nilai interval Keep-Alive dan interval Keep-Alive tersebut sesuai antara server dan klien. Untuk informasi selengkapnya, lihat bagian Mengonfigurasi SignalR batas waktu dan Keep-Alive di klien .
Dalam contoh berikut:
- ditingkatkan ClientTimeoutInterval menjadi 60 detik (nilai default: 30 detik).
- ditingkatkan HandshakeTimeout menjadi 30 detik (nilai default: 15 detik).
- KeepAliveInterval tidak diatur dalam kode pengembang dan menggunakan nilai defaultnya 15 detik. Mengurangi nilai interval Keep-Alive meningkatkan frekuensi ping komunikasi, yang meningkatkan beban pada aplikasi, server, dan jaringan. Perhatian harus diambil untuk menghindari penurunan kinerja saat menurunkan interval Keep-Alive.
Blazor Web App (.NET 8 atau yang lebih baru) dalam file proyek Program server:
builder.Services.AddRazorComponents().AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
Blazor Server dalam Program file:
builder.Services.AddServerSideBlazor()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
Untuk informasi selengkapnya, lihat bagian Opsi penanganan sirkuit sisi server.
Konfigurasi klien
Atur berikut ini:
-
withServerTimeout(default: 30 detik): Mengonfigurasi batas waktu server, yang ditentukan dalam milidetik, untuk koneksi hub sirkuit. -
withKeepAliveInterval(default: 15 detik): Interval, yang ditentukan dalam milidetik, di mana koneksi mengirim pesan Keep-Alive.
Batas waktu server dapat ditingkatkan, dan interval Keep-Alive dapat tetap sama. Pertimbangan pentingnya adalah bahwa jika Anda mengubah nilai, pastikan bahwa batas waktu server setidaknya dua kali lipat nilai interval Keep-Alive dan bahwa nilai interval Keep-Alive cocok antara server dan klien. Untuk informasi selengkapnya, lihat bagian Mengonfigurasi SignalR batas waktu dan Keep-Alive di klien .
Dalam contoh konfigurasi startup berikut (lokasi Blazor skrip), nilai kustom 60 detik digunakan untuk batas waktu server. Interval Keep-Alive (withKeepAliveInterval) tidak diatur dan menggunakan nilai defaultnya 15 detik.
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
configureSignalR: function (builder) {
builder.withServerTimeout(60000);
}
}
});
</script>
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
builder.withServerTimeout(60000);
}
});
</script>
Saat membuat koneksi hub dalam komponen, atur batas waktu server (WithServerTimeout, default: 30 detik) pada HubConnectionBuilder. Atur HandshakeTimeout (default: 15 detik) pada bawaan HubConnection. Pastikan bahwa batas waktu setidaknya dua kali lipat dari interval Keep-Alive (WithKeepAliveInterval/KeepAliveInterval) serta nilai Keep-Alive di server dan klien harus sesuai.
Contoh berikut didasarkan pada Index komponen dalam SignalR dengan Blazor tutorial. Batas waktu server ditingkatkan menjadi 60 detik, dan batas waktu jabat tangan ditingkatkan menjadi 30 detik. Interval Keep-Alive tidak diatur dan menggunakan nilai defaultnya 15 detik.
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.WithServerTimeout(TimeSpan.FromSeconds(60))
.Build();
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
Atur berikut ini:
-
serverTimeoutInMilliseconds(default: 30 detik): Mengonfigurasi batas waktu server, yang ditentukan dalam milidetik, untuk koneksi hub sirkuit. -
keepAliveIntervalInMilliseconds(default: 15 detik): Interval, yang ditentukan dalam milidetik, di mana koneksi mengirim pesan Keep-Alive.
Batas waktu server dapat ditingkatkan, dan interval Keep-Alive dapat tetap sama. Pertimbangan pentingnya adalah bahwa jika Anda mengubah nilai, pastikan bahwa batas waktu server setidaknya dua kali lipat nilai interval Keep-Alive dan bahwa nilai interval Keep-Alive cocok antara server dan klien. Untuk informasi selengkapnya, lihat bagian Mengonfigurasi SignalR batas waktu dan Keep-Alive di klien .
Dalam contoh konfigurasi startup berikut (lokasi Blazor skrip), nilai kustom 60 detik digunakan untuk batas waktu server. Interval Keep-Alive (keepAliveIntervalInMilliseconds) tidak diatur dan menggunakan nilai defaultnya 15 detik.
Di Pages/_Host.cshtml:
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 60000;
builder.build = () => {
return c;
};
}
});
</script>
Saat membuat koneksi hub dalam komponen, atur ServerTimeout (default: 30 detik) dan HandshakeTimeout (default: 15 detik) pada bawaan HubConnection. Konfirmasikan bahwa waktu habis setidaknya dua kali lipat dari interval Keep-Alive. Konfirmasikan bahwa interval Keep-Alive cocok antara server dan klien.
Contoh berikut didasarkan pada Index komponen dalam SignalR dengan Blazor tutorial. ditingkatkan ServerTimeout menjadi 60 detik, dan HandshakeTimeout ditingkatkan menjadi 30 detik. Interval Keep-Alive (KeepAliveInterval) tidak diatur dan menggunakan nilai defaultnya 15 detik.
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromSeconds(60);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);
hubConnection.On<string, string>("ReceiveMessage", (user, message) => ...
await hubConnection.StartAsync();
}
Mengubah pengelola penyambungan ulang sisi server
Peristiwa penyambungan sirkuit oleh penangan rekoneksi dapat dimodifikasi untuk perilaku kustom, seperti:
- Untuk memberi tahu pengguna jika koneksi terputus.
- Untuk melakukan pengelogan (dari klien) saat sirkuit tersambung.
Untuk mengubah peristiwa koneksi, daftarkan panggilan balik untuk perubahan koneksi berikut:
- Koneksi yang dihilangkan menggunakan
onConnectionDown. - Koneksi yang dibuat/dibuat ulang menggunakan
onConnectionUp.
Keduanya onConnectionDown dan onConnectionUp harus ditentukan.
Blazor Web App:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
circuit: {
reconnectionHandler: {
onConnectionDown: (options, error) => console.error(error),
onConnectionUp: () => console.log("Up, up, and away!")
}
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Blazor Server:
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
Blazor.start({
reconnectionHandler: {
onConnectionDown: (options, error) => console.error(error),
onConnectionUp: () => console.log("Up, up, and away!")
}
});
</script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Kontrol terprogram dari perilaku koneksi ulang dan pemuatan ulang
Blazor secara otomatis mencoba menyambungkan ulang dan me-refresh browser saat koneksi ulang gagal. Untuk informasi selengkapnya, lihat bagian Menyesuaikan jumlah dan interval coba lagi koneksi ulang sisi server . Namun, kode pengembang dapat mengimplementasikan handler koneksi ulang kustom untuk mengambil kontrol penuh atas perilaku koneksi ulang.
Perilaku koneksi ulang default mengharuskan pengguna untuk mengambil tindakan manual untuk menyegarkan halaman setelah koneksi ulang gagal. Namun, kode pengembang dapat mengimplementasikan handler koneksi ulang kustom untuk mengambil kontrol penuh atas perilaku koneksi ulang, termasuk menerapkan refresh halaman otomatis setelah upaya koneksi ulang gagal.
App.razor:
Pages/_Host.cshtml:
<div id="reconnect-modal" style="display: none;"></div>
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script src="boot.js"></script>
Dalam contoh sebelumnya, {BLAZOR SCRIPT} tempat penampung adalah Blazor jalur skrip dan nama file. Untuk lokasi skrip, lihat struktur proyek ASP.NET CoreBlazor.
Buat file berikut wwwroot/boot.js .
Blazor Web App:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
circuit: {
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
}
});
})();
Blazor Server:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
});
})();
Untuk informasi lebih lanjut tentang startup Blazor, lihat startup BlazorASP.NET Core.
Putuskan sambungan sirkuit BlazorSignalR dari klien
Blazor
SignalR Sirkuit terputus saat unload peristiwa halaman dipicu. Untuk memutuskan sambungan sirkuit untuk skenario lain pada klien, panggil Blazor.disconnect di penanganan aktivitas yang sesuai. Dalam contoh berikut, sirkuit terputus saat halaman disembunyikan (pagehide peristiwa):
window.addEventListener('pagehide', () => {
Blazor.disconnect();
});
Untuk informasi lebih lanjut tentang startup Blazor, lihat startup BlazorASP.NET Core.
Pengendali sirkuit sisi server
Anda dapat menentukan penangan sirkuit, yang memungkinkan menjalankan kode ketika ada perubahan pada status sirkuit pengguna. Handler sirkuit diimplementasikan dengan turunan dari CircuitHandler dan mendaftarkan kelas dalam kontainer layanan aplikasi. Contoh pengendali sirkuit berikut ini memantau koneksi yang terbuka SignalR.
TrackingCircuitHandler.cs:
using Microsoft.AspNetCore.Components.Server.Circuits;
public class TrackingCircuitHandler : CircuitHandler
{
private HashSet<Circuit> circuits = new();
public override Task OnConnectionUpAsync(Circuit circuit,
CancellationToken cancellationToken)
{
circuits.Add(circuit);
return Task.CompletedTask;
}
public override Task OnConnectionDownAsync(Circuit circuit,
CancellationToken cancellationToken)
{
circuits.Remove(circuit);
return Task.CompletedTask;
}
public int ConnectedCircuits => circuits.Count;
}
Pengendali sirkuit didaftarkan menggunakan DI. Instans terlingkup dibuat untuk setiap instance sirkuit. Menggunakan TrackingCircuitHandler dalam contoh sebelumnya, layanan singleton dibuat karena status semua sirkuit harus dilacak.
Dalam file Program:
builder.Services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
Dalam Startup.ConfigureServices dari Startup.cs:
services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
Jika metode pada handler sirkuit kustom menghasilkan pengecualian yang tidak tertangani, pengecualiannya bersifat fatal bagi sirkuit. Untuk mengatasi pengecualian dalam kode handler atau dalam metode yang dipanggil, bungkus kode dalam satu atau beberapa pernyataan try-catch dengan penanganan kesalahan dan pengelogan.
Ketika sirkuit berakhir karena pengguna telah terputus dan kerangka kerja membersihkan status sirkuit, kerangka kerja membuang cakupan DI sirkuit. Membuang cakupan akan membuang layanan-layanan DI yang berada dalam cakupan sirkuit mana pun, dan yang menerapkan System.IDisposable. Jika ada layanan DI yang menghasilkan pengecualian yang tidak tertangani selama proses pembersihan, kerangka kerja akan mencatat pengecualian tersebut. Untuk informasi selengkapnya, lihat Injeksi dependensi ASP.NET Core Blazor.
Pengelola sirkuit sisi server untuk mengumpulkan pengguna bagi layanan kustom
Gunakan CircuitHandler untuk mengambil pengguna dari AuthenticationStateProvider dan menetapkan pengguna tersebut dalam layanan. Untuk informasi selengkapnya dan contoh kode, lihat ASP.NET Core sisi server dan skenario keamanan tambahan.
Penutupan sirkuit ketika tidak ada komponen Server Interaktif yang tersisa
Komponen Server Interaktif menangani peristiwa UI web menggunakan koneksi real-time dengan browser yang disebut sirkuit. Sirkuit dan status terkait dibuat ketika komponen akar Server Interaktif dirender. Sirkuit ditutup ketika tidak ada komponen Server Interaktif yang tersisa di halaman, yang membebaskan sumber daya server.
Memulai sirkuit SignalR di URL yang berbeda
Cegah aplikasi agar tidak mulai secara otomatis dengan menambahkan autostart="false" ke tag Blazor<script> (mulai skrip Blazor di lokasi). Buat URL sirkuit secara manual menggunakan Blazor.start. Contoh berikut menggunakan jalur /signalr.
Blazor Web AppS:
- <script src="_framework/blazor.web.js"></script>
+ <script src="_framework/blazor.web.js" autostart="false"></script>
+ <script>
+ Blazor.start({
+ circuit: {
+ configureSignalR: builder => builder.withUrl("/signalr")
+ },
+ });
+ </script>
Blazor Server:
- <script src="_framework/blazor.server.js"></script>
+ <script src="_framework/blazor.server.js" autostart="false"></script>
+ <script>
+ Blazor.start({
+ configureSignalR: builder => builder.withUrl("/signalr")
+ });
+ </script>
Tambahkan panggilan MapBlazorHub berikut dengan jalur hub ke alur pemrosesan middleware di file Program aplikasi server.
Blazor Web AppS:
app.MapBlazorHub("/signalr");
Blazor Server:
Biarkan panggilan yang ada untuk MapBlazorHub dalam file dan tambahkan panggilan baru ke MapBlazorHub dengan jalur :
app.MapBlazorHub();
+ app.MapBlazorHub("/signalr");
Peniruan identitas untuk Autentikasi Windows
Koneksi hub terautentikasi (HubConnection) dibuat dengan UseDefaultCredentials untuk menunjukkan penggunaan kredensial default untuk permintaan HTTP. Untuk informasi selengkapnya, lihat SignalR autentikasi dan otorisasi.
Saat aplikasi berjalan di IIS Express sebagai pengguna aktif di bawah Autentikasi Windows, yang kemungkinan merupakan akun pribadi atau kerja pengguna, kredensial default adalah kredensial dari pengguna yang masuk.
Saat aplikasi diterbitkan ke IIS, aplikasi berjalan di bawah Kumpulan Aplikasi Identity. HubConnection terhubung sebagai akun "pengguna" IIS yang menghosting aplikasi, bukan pengguna yang mengakses halaman.
Terapkan peniruan dengan HubConnection untuk menggunakan identitas pengguna yang menjelajah.
Dalam contoh berikut:
- Pengguna dari penyedia status autentikasi dilemparkan ke WindowsIdentity.
- Token akses identitas diteruskan ke WindowsIdentity.RunImpersonatedAsync bersama kode yang membangun dan menjalankan HubConnection.
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState?.User.Identity is not null)
{
var user = authState.User.Identity as WindowsIdentity;
if (user is not null)
{
await WindowsIdentity.RunImpersonatedAsync(user.AccessToken,
async () =>
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavManager.ToAbsoluteUri("/hub"), config =>
{
config.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("name", userName =>
{
name = userName;
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
});
}
}
}
Dalam kode sebelumnya, NavManager adalah NavigationManager, dan AuthenticationStateProvider adalah instans layanan AuthenticationStateProvider ( dokumentasiAuthenticationStateProvider).
Mengonfigurasi titik akhir URL hub untuk koneksi loopback
Bagian ini hanya berlaku untuk aplikasi sisi Blazor server.
Jika permintaan HTTP di aplikasi sisi server Blazor gagal terhubung ke dirinya sendiri saat menggunakan NavigationManager.ToAbsoluteUri, Anda mungkin memiliki load balancer atau proksi yang tidak mengharapkan permintaan dari server backend. Dalam skenario ini, Anda dapat mencoba mengubah URL hub yang digunakan klien untuk terhubung langsung ke server backend.
Contoh berikut:
- Mengonfigurasi URL hub menggunakan UriBuilder dan meneruskannya ke WithUrl.
- Mengatur URI berdasarkan alamat server (IServerAddressesFeature) dan nama komputer (MachineName).
@using System.Net
@using System.Net.Sockets
@using Microsoft.AspNetCore.Hosting.Server
@using Microsoft.AspNetCore.Hosting.Server.Features
@using Microsoft.AspNetCore.SignalR.Client
@inject IHostEnvironment Environment
@inject IServer Server
...
@code {
private HubConnection? hubConnection;
protected override async Task OnInitializedAsync()
{
var serverAddress = Server.Features
.Get<IServerAddressesFeature>()?
.Addresses
.FirstOrDefault(a => a.StartsWith("http://") || a.StartsWith("https://"));
if (serverAddress is null)
{
throw new InvalidOperationException("No server address available.");
}
var uri = new UriBuilder(serverAddress + "/chathub");
// If Kestrel is bound to a wildcard, substitute a real IP
if (uri.Host is "0.0.0.0" or "[::]" or "+" or "*")
{
var addresses = await Dns.GetHostAddressesAsync(
System.Environment.MachineName);
var ip = addresses.FirstOrDefault(a =>
a.AddressFamily == AddressFamily.InterNetwork
&& !IPAddress.IsLoopback(a));
if (ip is null)
{
throw new InvalidOperationException("No suitable IP address.");
}
uri.Host = ip.ToString();
}
hubConnection = new HubConnectionBuilder()
.WithUrl(uri.Uri)
.Build();
hubConnection.On<ChatMessage>("ReceiveMessage", (message) =>
{
...
});
await hubConnection.StartAsync();
}
}
Sumber daya sisi server tambahan
- Panduan penyebaran dan host sisi server: SignalR konfigurasi
- Gambaran umum ASP.NET Core SignalR
- Konfigurasi ASP.NET Core SignalR
- Dokumentasi keamanan sisi server
- IHttpContextAccessor/HttpContext di aplikasi ASP.NET Core Blazor
- Peristiwa koneksi ulang sisi server dan peristiwa siklus hidup komponen
- Apa itu Azure SignalR Service?
- Panduan performa untuk Layanan Azure SignalR
- Menerbitkan aplikasi ASP.NET Core SignalR ke Azure App Service
-
Blazor (
dotnet/blazor-samples) (cara mengunduh)
ASP.NET Core