ASP.NET Core SignalR ile anlık bildirim ve canlı veri akışı kurmak
Beceri rehberi

ASP.NET Core SignalR ile anlık bildirim ve canlı veri akışı kurmak

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 7 dk okuma

ASP.NET Core SignalR ile hub yazma, JavaScript istemcisi bağlama, grup ve kullanıcıya mesaj gönderme, yeniden bağlanma ve sık yapılan hatalar.

SignalR nedir ve hangi sorunu çözer?

Klasik web uygulamasında tarayıcı istek gönderir, sunucu yanıt verir; sunucu kendi başına istemciye bir şey iletemez. Canlı bildirim, anlık sipariş panosu, sohbet ya da ortak düzenleme gibi senaryolarda ise sunucunun olay olduğu anda istemcilere bilgi göndermesi gerekir. ASP.NET Core SignalR bu ihtiyaç için hazırlanmış bir kütüphanedir.

Microsoft belgesinin tanımıyla SignalR'ın Hubs API'si, bağlı istemcilerin sunucudaki metotları çağırmasını ve sunucunun da istemcideki metotları çağırmasını sağlar. İstemciler arası iletişim dolaylıdır: hub aracılık eder ve mesaj tek bir istemciye, bir gruba ya da bağlı herkese gönderilebilir. Bağlantı yönetimi, serileştirme ve iletim ayrıntılarını kütüphane üstlenir; geliştirici yalnızca hub metotlarına ve istemci metotlarına odaklanır.

Kullanım alanları şunlardır: • Sipariş, ödeme ya da sistem uyarısı gibi olayların kullanıcıya anında bildirilmesi. • Canlı gösterge panoları ve izleme ekranları. • Sohbet ve destek hattı arayüzleri. • Birden fazla kullanıcının aynı belgeyi ya da panoyu eş zamanlı gördüğü ekranlar.

Her güncelleme için gerçek zamanlı kanal gerekmez. Veri yalnızca belirli aralıklarla değişiyorsa ve birkaç saniyelik gecikme kabul ediliyorsa, düzenli sorgulama (polling) daha basit bir çözüm olabilir. SignalR, gecikmenin kritik olduğu ve sunucunun inisiyatif aldığı durumlarda değer üretir.

Sunucu tarafında hub nasıl yazılır?

İlk adım Hub sınıfından türeyen bir sınıf tanımlamaktır. Sınıfın public metotları istemciler tarafından çağrılabilir hâle gelir:

public class ChatHub : Hub { public async Task SendMessage(string user, string message) => await Clients.All.SendAsync("ReceiveMessage", user, message); }

Bu metot, çağrıldığında bağlı tüm istemcilerde ReceiveMessage adlı istemci metodunu tetikler. Hub'ı çalıştırmak için Program dosyasında birkaç satır yeterlidir: biri servisi kaydeder, diğeri hub'ı bir adrese bağlar:

builder.Services.AddSignalR();

app.MapHub<ChatHub>("/chathub");

Hub metotları parametre ve dönüş değeri alabilir; karmaşık nesneler ve diziler dahil. Serileştirme işini SignalR yapar.

Önemli kurallar vardır. Biri, her hub metodu çağrısı yeni bir hub örneğinde çalışır; bu yüzden hub sınıfının özelliklerinde durum tutulmaz. Kalıcı durum bir servis ya da veritabanında olmalıdır. Diğeri, hub içinde bağımlılık enjeksiyonu desteklenir; örneğin bir veritabanı bağlamı fabrikası kurucudan alınıp mesajı kaydetmek için kullanılabilir. Bağlam iş parçacığı güvenli olmadığı için aynı bağlam örneğiyle eş zamanlı işlem yapılmamalıdır.

Asenkron çağrılar await ile beklenmelidir. Belgede belirtildiği gibi await kullanılmayan bir çağrı, hub metodu tamamlandıktan sonra hataya düşebilir.

İstemci tarafı bağlantısı nasıl kurulur?

JavaScript istemcisi npm paketi olarak dağıtılır: npm install @microsoft/signalr komutuyla kurulur ya da bir CDN üzerinden sayfaya eklenir. Bağlantı HubConnectionBuilder ile oluşturulur:

const connection = new signalR.HubConnectionBuilder() .withUrl("/chathub") .configureLogging(signalR.LogLevel.Information) .build();

connection.on("ReceiveMessage", (user, message) => { const li = document.createElement("li"); li.textContent = user + ": " + message; document.getElementById("messageList").appendChild(li); });

async function start() { try { await connection.start(); console.log("SignalR Connected."); } catch (err) { console.log(err); } }

start();

Sunucudaki SendAsync çağrısında verilen ad ile istemcideki connection.on adı eşleşir; SignalR hangi istemci metodunun çalışacağını bu ad ve parametrelerle belirler. Belgenin önerdiği pratik, start çağrısını connection.on ile yapılan dinleyici kayıtlarından sonra yapmaktır; böylece mesaj gelmeden önce dinleyiciler hazır olur.

İstemciden sunucuya mesaj göndermek için invoke kullanılır:

try { await connection.invoke("SendMessage", user, message); } catch (err) { console.error(err); }

invoke bir Promise döndürür ve sunucudan dönen değeri bekler. Yanıt beklemeden göndermek isteyen kod send metodunu kullanabilir, ancak bu durumda sunucu hatası istemciye dönmez. Farklı bir adresten bağlanılıyorsa (başka alan adı), istemci mutlak adres kullanmalı ve sunucuda CORS etkinleştirilmelidir; UseCors çağrısı MapHub'dan önce yapılmalıdır.

Mesaj kimlere gönderilir: kullanıcı, grup ve herkes?

Hub'ın Clients özelliği, mesajın hedefini seçmeyi sağlar. Belgedeki başlıca seçenekler şunlardır: • Clients.All: Bağlı tüm istemciler. • Clients.Caller: Metodu çağıran istemci. • Clients.Others: Çağıran dışındaki herkes. • Clients.Group: Belirli bir gruptaki bağlantılar. • Clients.User: Belirli bir kullanıcıya ait tüm bağlantılar. • Clients.AllExcept, Clients.OthersInGroup gibi dışlama seçenekleri.

Örnek olarak bir destek ekranında, bir müşterinin mesajını yalnızca ilgili destek grubuna iletmek için:

public async Task SendMessageToGroup(string user, string message) => await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);

Grup üyeliği genellikle bağlantı kurulduğunda eklenir:

public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnConnectedAsync(); }

Context.ConnectionId her bağlantıya SignalR tarafından atanan benzersiz kimliktir. Context.User ise bağlantıya ait kimlik bilgisidir; varsayılan olarak kullanıcı tanımlayıcısı kimlik hak talebinden alınır. Bağlantı koptuğunda gruptan çıkarma işlemini ayrıca yapmanıza gerek yoktur, otomatik yapılır.

Gerçek senaryoda işlem hub dışından tetiklenir: örneğin bir sipariş servisi ödeme alındığında ilgili kullanıcıya haber vermek ister. Bu durumda hub örneği doğrudan enjekte edilmez; IHubContext kullanılır. Böylece arka plan servislerinden ve denetleyicilerden de istemcilere mesaj gönderilebilir.

Tipli hub, hata yönetimi ve yeniden bağlanma nasıl ele alınır?

SendAsync, istemci metodunu metin olarak bekler. Adı yanlış yazarsanız hata ancak çalışma zamanında anlaşılır. Çözüm tipli hub kullanmaktır: istemci metotları bir arayüzde tanımlanır ve hub bu arayüzle genelleştirilir.

public interface IChatClient { Task ReceiveMessage(string user, string message); }

public class StronglyTypedChatHub : Hub<IChatClient> { public async Task SendMessage(string user, string message) => await Clients.All.ReceiveMessage(user, message); }

Böylece istemci metodu adı derleme zamanında denetlenir. Tipli hub'da SendAsync kullanılamaz.

Hata yönetiminde belgenin ilkesi şudur: hub metodunda atılan istisna çağıran istemciye döner ama varsayılan olarak ayrıntı gizlenir ve genel bir mesaj gösterilir; çünkü ham istisna mesajları (örneğin veritabanı sunucusu adı) gizli bilgi sızdırabilir. İstemciye bilerek iletilecek durumlar için HubException atılır ve yalnızca Message bilgisi iletilir:

public Task ThrowException() => throw new HubException("Bu hata istemciye iletilir.");

Bağlantı kopmalarına karşı JavaScript istemcisinde otomatik yeniden bağlanma varsayılan olarak kapalıdır; withAutomaticReconnect ile açılır:

const connection = new signalR.HubConnectionBuilder() .withUrl("/chathub") .withAutomaticReconnect() .build();

Yeniden bağlanma başlamadan önce onreconnecting, başarılı olunca onreconnected geri çağrıları çalışır; kullanıcıya bağlantı kesildi bilgisi verilip düğmeler pasifleştirilebilir. Yeniden bağlanan istemci sunucu için yeni bir bağlantı sayılır ve yeni bir bağlantı kimliği alır; bu yüzden grup üyelikleri gibi bağlantıya bağlı durumlar yeniden kurulmalıdır. İlk start başarısızlığı ise otomatik yeniden bağlanma kapsamında değildir, ayrıca ele alınmalıdır.

Üretimde dikkat edilecek noktalar ve sık hatalar

  • Hub'da durum tutmak: Hub her çağrıda yeniden oluşturulur. Çevrimiçi kullanıcı listesi gibi durum için ortak bir servis ya da dış depo kullanın.
  • await unutmak: Beklenmeyen çağrı, hub metodu bittikten sonra başarısız olabilir.
  • Yetkilendirmeyi atlamak: Hub'a bağlanabilen herkes metotları çağırabilir. Kimlik doğrulama ve yetki kuralları hub düzeyinde uygulanmalı, mesajı kimin alacağı sunucuda belirlenmelidir. İstemciden gelen kullanıcı adı ya da grup bilgisine güvenmeyin.
  • Hata mesajı sızdırmak: Ayrıntılı istisna bilgisini istemciye açmak güvenlik riskidir.
  • CORS yanlış sırası: Farklı alan adından bağlanılırken UseCors çağrısının MapHub'dan önce yapılmaması bağlantıyı engeller.
  • Sekme uykusu: Tarayıcılar etkin olmayan sekmeleri dondurabilir ve bu bağlantıyı kapatabilir. Belgede bunun için bir Web Lock yöntemi örneklenir; kullanıcı deneyimi açısından yeniden bağlanma mantığı yine de gereklidir.
  • Ölçeklenme: Uygulama birden fazla sunucuda çalışırken bağlantılar sunuculara dağılır ve mesajların bütün örneklere ulaşması için ek yapı gerekir. Microsoft, büyük sayıda eş zamanlı bağlantı için Azure SignalR Service'in kullanılmasını önerir. Bu konuda sürüme uygun resmî belgeyi izleyin.
  • Her şeyi gerçek zamanlıya çevirmek: Gerçekten anlık olması gerekmeyen güncellemeler için basit sorgulama ya da sayfa yenileme yeterliyken kalıcı bağlantı yönetmek gereksiz karmaşıklık ve sunucu yükü getirir.

Bu beceri Mindset programlarında nerede çalışılır?

Mindset kataloğundaki ".NET ile Web Uygulamaları" eğitimi, ASP.NET Core ile web uygulaması geliştiren yazılımcılar ve kurumsal .NET ekipleri için uygundur. Modül başlıkları arasında ASP.NET Core ekosistemi, MVC ve Web API tasarımı, Entity Framework Core ile veri katmanı, kimlik doğrulama ve güvenlik, Blazor ile modern ön yüz ve gerçek zamanlı iletişim modülü yer alır.

Gerçek zamanlı iletişim modülü, bildirim ve canlı pano gibi özellikleri mevcut Web API ve veri katmanıyla birleştirme ihtiyacı olan ekipler için anlamlıdır. Güvenlik modülüyle birlikte düşünüldüğünde, hub'ların yetkilendirilmesi de aynı programda ele alınır.

İlgili Mindset programı

.NET ile Web Uygulamaları

Sık sorulanlar

Sık sorulan sorular

SignalR ile WebSocket arasındaki fark nedir?

WebSocket, tarayıcı ile sunucu arasında çift yönlü iletişim sağlayan bir protokoldür. SignalR ise bu tür bağlantıların üzerine kurulmuş bir kütüphanedir; bağlantı yönetimi, hub metotları, gruplar, kullanıcı hedefleme ve serileştirmeyi sizin yerinize halleder. Ham WebSocket ile bunların tamamını elle yazmak gerekir.

SignalR yalnızca JavaScript istemcisiyle mi çalışır?

Hayır. Microsoft belgelerinde JavaScript, .NET ve Java istemcilerine ait örnekler bulunur. Sunucu tarafı C# ile yazılır. Bu sayede bir tarayıcı arayüzü, bir masaüstü ya da mobil .NET uygulaması aynı hub'a bağlanabilir. Kullanılan istemci paketinin sürümünün sunucu sürümüyle uyumlu olması iyi bir uygulamadır.

Kullanıcıya özel mesaj nasıl gönderilir?

Clients.User ile bir kullanıcının tüm bağlantılarına ya da Clients.Client ile belirli bir bağlantıya mesaj gönderilebilir. Kullanıcı tanımlayıcısı varsayılan olarak bağlantıyla ilişkili kimlikteki NameIdentifier hak talebinden alınır. Hub dışından göndermek için IHubContext kullanılır. Hedefi her zaman sunucu belirlemeli, istemciden gelen kullanıcı bilgisine güvenilmemelidir.

SignalR bağlantısı kopunca ne olur?

JavaScript istemcisinde otomatik yeniden bağlanma varsayılan olarak kapalıdır; withAutomaticReconnect ile açılır. Yeniden bağlanma sürerken onreconnecting, başarıdan sonra onreconnected geri çağrıları çalışır ve istemci yeni bir bağlantı kimliği alır. Yeniden bağlanma başarısız olursa onclose çalışır ve sayfayı yenileme önerilebilir.

Hub içinde veritabanına nasıl erişilir?

Hub kurucusuna bağımlılık enjekte edilebilir. Belgede önerilen yaklaşım, veritabanı bağlamı fabrikasını enjekte edip her işlem için ayrı bağlam üretmektir. Doğrudan kapsamlı bağlam enjekte ediliyorsa aynı bağlamda eş zamanlı işlem yapılmamalıdır, çünkü bağlam iş parçacığı güvenli değildir.

SignalR ile Gerçek Zamanlı İletişim: Hub ve İstemci