ASP.NET Core Blazor Progressive Web Uygulamaları (PWA) için push bildirimleri

Uyarı

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 10 sürümüne bakın.

Uyarı

ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için .NET ve .NET Core Destek Politikası'na bakın. Geçerli sürüm için bu makalenin .NET 10 sürümüne bakın.

Blazor PWA'lar, kullanıcı uygulamayı etkin olarak kullanmasa bile arka uç sunucusundan anında iletme bildirimleri (veri iletileri) alabilir ve görüntüleyebilir. Örneğin, farklı bir kullanıcı yüklü PWA'sında bir eylem gerçekleştirdiğinde veya uygulama veya arka uç sunucu uygulamasıyla doğrudan etkileşim kuran kullanıcılar bir eylem gerçekleştirdiğinde anında iletme bildirimleri gönderilebilir.

Aşağıdakiler için anında iletme bildirimlerini kullanın:

  • Kullanıcılara önemli bir şey olduğunu bildirerek uygulamaya geri dönmelerini isteyin.
  • Haber akışı gibi uygulamada depolanan verileri güncelleştirerek kullanıcının anında iletme bildirimi yayınlandığında çevrimdışı olsa bile uygulamaya bir sonraki dönüşünde yeni veriler edinmesi sağlanır.

Anında iletme bildirimi gönderme, alma ve görüntüleme mekanizmaları Blazor WebAssembly bağımsızdır. Anında iletme bildirimi gönderme, herhangi bir teknolojiyi kullanabilen arka uç sunucusu tarafından uygulanır. İstemcide push bildirimi alma ve görüntüleme, hizmet çalışanı JavaScript (JS) dosyasında uygulanır.

Bu makaledeki örnek, Blazing Pizza Workshop PWA tanıtım uygulamasını temel alan bir pizza restoranının müşterilerine sipariş durumu güncelleştirmeleri sağlamak için anında iletme bildirimlerini kullanır. Bu makaleyi kullanmak için çevrimiçi atölyeye katılmanız gerekmez, ancak atölye PWA geliştirmeye yararlı bir giriş Blazor niteliğindedir.

Uyarı

Blazing Pizza uygulaması, kullanıcı arabirimi katmanı ile veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için depo desenini benimser. Daha fazla bilgi için bkz. İş Birimi (UoW) düzeni ve Altyapı kalıcılık katmanını tasarlama.

Ortak ve özel anahtarlar oluşturma

Anında iletme bildirimlerini yerel olarak, örneğin PowerShell veya IIS ile ya da çevrimiçi bir araç kullanarak güvenli hale getirmek için şifreleme ortak ve özel anahtarları oluşturun.

Bu makalenin örnek kodunda kullanılan yer tutucular:

  • {PUBLIC KEY}: Ortak anahtar.
  • {PRIVATE KEY}: Özel anahtar.

Bu makalenin someone@example.com C# örnekleri için, e-posta adresini özel anahtar çiftini oluştururken kullanılan adresle eşleşecek şekilde güncelleştirin.

Anında iletme bildirimleri uygularken şifreleme anahtarlarının güvenli bir şekilde yönetildiğinden emin olun:

  • Anahtar oluşturma: Ortak ve özel anahtarları oluşturmak için güvenilir bir kitaplık veya araç kullanın. Zayıf veya güncel olmayan algoritmalar kullanmaktan kaçının.
  • Anahtar depolama: Donanım güvenlik modülü (HSM) veya şifrelenmiş depolama gibi güvenli bir depolama mekanizması kullanarak özel anahtarları sunucuda güvenli bir şekilde depolayın. Özel anahtarları hiçbir zaman istemciye göstermeyin.
  • Anahtar kullanımı: Özel anahtarı yalnızca anında iletme bildirimi yüklerini imzalamak için kullanın. Ortak anahtarın istemcilere güvenli bir şekilde dağıtılmasını sağlayın.

Şifreleme en iyi yöntemleri hakkında daha fazla bilgi için bkz . Şifreleme Hizmetleri.

Abonelik oluşturma

Kullanıcıya anında iletme bildirimleri göndermeden önce uygulamanın kullanıcıdan izin istemesi gerekir. Bildirimleri alma izni veren tarayıcıları, uygulamanın bildirimleri kullanıcıya yönlendirmek için kullanabileceği belirteç kümesini içeren bir abonelik oluşturur.

İzin uygulama tarafından herhangi bir zamanda alınabilir, ancak kullanıcılardan yalnızca uygulamadan gelen bildirimlere neden abone olmak istediklerinin açık olduğu durumlarda izin istemenizi öneririz. Aşağıdaki örnek, kullanıcılara ödeme sayfasına (Checkout bileşen) ne zaman geldiklerini sorar, çünkü bu noktada kullanıcının sipariş verme konusunda ciddi olduğu açıktır.

Kullanıcı bildirimleri almayı kabul ederse, aşağıdaki örnek anında iletme bildirimi abonelik verilerini sunucuya gönderir; burada anında iletme bildirimi belirteçleri daha sonra kullanılmak üzere veritabanında depolanır.

Abonelik istemek için anında iletme bildirimleri JS dosyası ekleyin:

Blazing Pizza uygulamasında JS dosyası pushNotifications.js olarak adlandırılır ve çözümün wwwroot sınıf kitaplığı projesinin ortak statik varlıklar klasöründe (Razor) bulunur (BlazingPizza.ComponentsLibrary). blazorPushNotifications.requestSubscription İşlev bir abonelik istemektedir.

BlazingPizza.ComponentsLibrary/wwwroot/pushNotifications.js:

(function () {
    const applicationServerPublicKey = '{PUBLIC KEY}';

    window.blazorPushNotifications = {
        requestSubscription: async () => {
            const worker = await navigator.serviceWorker.getRegistration();
            const existingSubscription = await worker.pushManager.getSubscription();
            if (!existingSubscription) {
                const newSubscription = await subscribe(worker);
                if (newSubscription) {
                    return {
                        url: newSubscription.endpoint,
                        p256dh: arrayBufferToBase64(newSubscription.getKey('p256dh')),
                        auth: arrayBufferToBase64(newSubscription.getKey('auth'))
                    };
                }
            }
        }
    };

    async function subscribe(worker) {
        try {
            return await worker.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey: applicationServerPublicKey
            });
        } catch (error) {
            if (error.name === 'NotAllowedError') {
                return null;
            }
            throw error;
        }
    }

    function arrayBufferToBase64(buffer) {
        var binary = '';
        var bytes = new Uint8Array(buffer);
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return window.btoa(binary);
    }
})();

Uyarı

Önceki arrayBufferToBase64 işlev hakkında daha fazla bilgi almak için bkz: ArrayBuffer'ı base64 kodlu bir dizeye nasıl dönüştürebilirim? (Stack Overflow).

Sunucuda bir abonelik nesnesi ve bildirim abonelik uç noktası oluşturulur. Uç birim, şifreleme belirteçleri de dahil olmak üzere push bildirimi abonelik verileriyle istemci web API'si çağrılarını alır. Veriler her uygulama kullanıcısı için veritabanında depolanır.

Blazing Pizza uygulamasında abonelik nesnesi sınıfıdır NotificationSubscription . P256dh ve Auth özellikleri kullanıcının şifreleme belirteçleridir.

BlazingPizza.Shared/NotificationSubscription.cs:

public class NotificationSubscription
{
    public int? NotificationSubscriptionId { get; set; }

    public string? UserId { get; set; }

    public string? Url { get; set; }

    public string? P256dh { get; set; }

    public string? Auth { get; set; }
}

Uygulamanın notifications/subscribe dosyasında çağrılan uzantı yönteminde, uygulama için web API uç noktalarını ayarlamak amacıyla MapPizzaApi uç noktası tanımlanır. Kullanıcının anında iletme bildirimi belirteçlerini içeren bildirim aboneliği (NotificationSubscription ) veritabanında depolanır. Kullanıcı başına yalnızca bir abonelik depolanır. Alternatif olarak, kullanıcının farklı tarayıcılardan veya cihazlardan birden çok abonelik kaydetmesine izin vekleyebilirsiniz.

app.MapPut("/notifications/subscribe", 
    [Authorize] async (
    HttpContext context,
    PizzaStoreContext db,
    NotificationSubscription subscription) => 
    {
        var userId = GetUserId(context);

        if (userId is null)
        {
            return Results.Unauthorized();
        }

        // Remove old subscriptions for this user
        var oldSubscriptions = db.NotificationSubscriptions.Where(
            e => e.UserId == userId);
        db.NotificationSubscriptions.RemoveRange(oldSubscriptions);

        // Store the new subscription
        subscription.UserId = userId;
        db.NotificationSubscriptions.Add(subscription);

        await db.SaveChangesAsync();

        return Results.Ok(subscription);
    });

BlazingPizza.Client/HttpRepository.cs yönteminde, SubscribeToNotifications, sunucudaki abonelikler uç noktasına bir HTTP PUT isteği gönderir.

public class HttpRepository : IRepository
{

	private readonly HttpClient _httpClient;

	public HttpRepository(HttpClient httpClient)
	{
		_httpClient = httpClient;
	}

    ...

    public async Task SubscribeToNotifications(NotificationSubscription subscription)
	    {
		    var response = await _httpClient.PutAsJsonAsync("notifications/subscribe", 
                subscription);
		    response.EnsureSuccessStatusCode();
	}
}

Depo arabirimi (BlazingPizza.Shared/IRepository.cs), yönteminin imzasını SubscribeToNotificationsiçerir:

public interface IRepository
{
    ...

	Task SubscribeToNotifications(NotificationSubscription subscription);
}

Abonelik oluşturulursa abonelik istemek ve bildirimlere abone olmak için bir yöntem tanımlayın. Aboneliği daha sonra kullanmak üzere veritabanına kaydedin.

Checkout Blazing Pizza uygulamasının bileşeninde RequestNotificationSubscriptionAsync yöntemi aşağıdaki görevleri yerine getirir:

  • Abonelik, JS çağrılarak blazorPushNotifications.requestSubscription interoperasyon yoluyla oluşturulur. Bileşen, IJSRuntime işlevini çağırmak için JS hizmetini enjekte eder.
  • SubscribeToNotifications yöntemi, aboneliği kaydetmek için çağrılır.

BlazingPizza.Client/Components/Pages/Checkout.razor'da:

async Task RequestNotificationSubscriptionAsync()
{
    var subscription = await JSRuntime.InvokeAsync<NotificationSubscription>(
        "blazorPushNotifications.requestSubscription");

    if (subscription is not null)
    {
        try
        {
            await Repository.SubscribeToNotifications(subscription);
        }
        catch (AccessTokenNotAvailableException ex)
        {
            ex.Redirect();
        }
    }
}

Checkout bileşeninde, RequestNotificationSubscriptionAsyncOnInitialized yaşam döngüsü yönteminde çağrılır ve bileşenin başlatılması sırasında yürütülür. Yöntem zaman uyumsuzdur ancak arka planda çalıştırılabilir ve döndürdüğü Task atılabilir. Bu nedenle, yöntem zaman uyumsuz yaşam döngüsü yöntemi olan bileşen başlatma yöntemi OnInitializedAsync içinde çağrılmaz. Bu yaklaşım, bileşeni daha hızlı işler.

protected override void OnInitialized()
{
    _ = RequestNotificationSubscriptionAsync();
}

Kodun nasıl çalıştığını göstermek için Blazing Pizza uygulamasını çalıştırın ve sipariş vermeye başlayın. Abonelik isteğini görmek için ödeme ekranına gidin.

Blazing Pizza uygulaması, kullanıcıdan bildirimleri göstermek için izin istemektedir.

İzin Ver'i seçin ve tarayıcı geliştirici araçları konsolunda hatalar olup olmadığını denetleyin. 'nin PizzaApiExtensions kodunda MapPut("/notifications/subscribe"...)bir kesme noktası ayarlayabilir ve tarayıcıdan gelen verileri incelemek için hata ayıklama ile uygulamayı çalıştırabilirsiniz. Veriler bir uç nokta URL'si ve şifreleme belirteçleri içerir.

Kullanıcı belirli bir site için bildirimlere izin verdikten veya bildirimleri engelledikten sonra tarayıcı bir daha sormaz. Google Chrome veya Microsoft Edge'de daha fazla test yapma iznini sıfırlamak için tarayıcının adres çubuğunun sol kısmındaki "bilgi" simgesini (🛈) seçin ve aşağıdaki görüntüde gösterildiği gibi Bildirimler'iYeniden Sor (varsayılan) olarak değiştirin:

Bildirimleri devre dışı duruma geri döndürmek için bu uygulamadan

Bildirim gönderme

Bildirim göndermek, aktarımdaki verileri korumak için sunucuda bazı karmaşık şifreleme işlemleri gerçekleştirmeyi içerir. Karmaşıklığın büyük bir kısmı uygulama için Blazing Pizza uygulamasında sunucu projesi (WebPush) tarafından kullanılan bir üçüncü taraf NuGet paketi BlazingPizza.Servertarafından işlenir.

SendNotificationAsync yöntemi, yakalanan aboneliği kullanarak sipariş bildirimlerini gönderir. Aşağıdaki kod, bildirimi göndermek için API'leri kullanır WebPush . Bildirimin yükü, JSON formatında serileştirilmiştir ve bir ileti ile bir URL içerir. İleti kullanıcıya görüntülenir ve URL kullanıcının bildirimle ilişkili pizza siparişine ulaşmasını sağlar. Diğer bildirim senaryoları için gereken şekilde ek parametreler seri hale getirilebilir.

Dikkat

Aşağıdaki örnekte, özel anahtarı sağlamak için güvenli bir yaklaşım kullanmanızı öneririz. Ortamdaki Development yerel çalışmalarda, Gizli Yönetici aracı kullanılarak uygulamaya özel bir anahtar sağlanabilir. , ve ortamlarında, Azure Key Vault ile Azure Yönetilen Kimlikler kullanılabilir; bu arada, bir anahtar kasasından sertifikanın özel anahtarını almak için sertifikanın dışarı aktarılabilir bir özel anahtarı olması gerektiğini unutmayın.

private static async Task SendNotificationAsync(Order order, 
    NotificationSubscription subscription, string message)
{
    var publicKey = "{PUBLIC KEY}";
    var privateKey = "{PRIVATE KEY}";

    var pushSubscription = new PushSubscription(subscription.Url, 
        subscription.P256dh, subscription.Auth);
    var vapidDetails = new VapidDetails("mailto:<someone@example.com>", publicKey, 
        privateKey);
    var webPushClient = new WebPushClient();

    try
    {
        var payload = JsonSerializer.Serialize(new
        {
            message,
            url = $"myorders/{order.OrderId}",
        });

        await webPushClient.SendNotificationAsync(pushSubscription, payload, 
            vapidDetails);
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine($"Error sending push notification: {ex.Message}");
    }
}

Yukarıdaki örnek, sunucunun bildirim göndermesine olanak tanır, ancak tarayıcı ek mantık olmadan bildirimlere tepki vermez. Bildirimleri görüntüleme, Bildirimleri görüntüle bölümünde ele alınmıştır.

Tarayıcının geliştirici araçları konsolu, Blazing Pizza uygulamasında siparişler alındıktan on saniye sonra bildirimlerin gelişini gösterir. Uygulama sekmesinde Anında İleti gönderme bölümünü açın. Kaydı başlat'a gitmek için daireyi seçin:

Bildirimleri görüntüleme

Uygulamanın bunları görüntüleyebilmesi için PWA'nın hizmet çalışanının (service-worker.js) anında iletme bildirimlerini işlemesi gerekir.

Blazing Pizza uygulamasında aşağıdaki push olay işleyicisi , etkin hizmet çalışanı için bir bildirim oluşturmak üzere çağırır showNotification .

BlazingPizza/wwwroot/service-worker.js'da:

self.addEventListener('push', event => {
  const payload = event.data.json();
  event.waitUntil(
    self.registration.showNotification('Blazing Pizza', {
      body: payload.message,
      icon: 'img/icon-512.png',
      vibrate: [100, 50, 100],
      data: { url: payload.url }
    })
  );
});

Yukarıdaki kod, tarayıcı Installing service worker... günlükleri tuttuğunda, bir sonraki sayfa yüklenene kadar etkili olmaz. Hizmet çalışanını güncelleştirmeye zorlarken, tarayıcının geliştirici araçları konsolundaki Uygulama sekmesini kullanın. Hizmet Çalışanları'nın altında Güncelleştir'i seçin veya Kaydı Kaldır'ı kullanarak bir sonraki yükte yeni bir kayıt yapmaya zorlayabilirsiniz.

Yukarıdaki kodun yerinde olması ve bir kullanıcı yeni bir sipariş verdiğinde, sipariş uygulamanın yerleşik tanıtım mantığına göre 10 saniye sonra teslimat için yola çıktı durumuna geçer. Tarayıcı bir push bildirimi alır.

Kullanıcıya pizza siparişinin gönderildiğini belirten bir açılır bildirim.

Uygulamayı Google Chrome veya Microsoft Edge'de kullanırken, kullanıcı Blazing Pizza uygulamasını etkin olarak kullanmasa bile bildirim görüntülenir. Ancak tarayıcı çalışıyor olmalıdır veya tarayıcı bir sonraki açışınızda bildirim görüntülenir.

Yüklü PWA kullanılırken, kullanıcı uygulamayı çalıştırmasa bile bildirim teslim edilmelidir.

Bildirim tıklamalarını işleme

Bir kullanıcının cihazında anında iletme bildirimi seçmesini (tıklamasını) işlemek için olay işleyicisini kaydedin:notificationclick

Blazing Pizza uygulamasındaki aşağıdaki örnek, kullanıcıyı bildirimle ilgili siparişin sipariş durumu sayfasına götürür. Bildirim yükünde sunucu tarafından gönderilen event.notification.data.url parametresiyle URL sağlanır.

Hizmet çalışanı dosyasında (service-worker.js):

self.addEventListener('notificationclick', event => {
  event.notification.close();
  event.waitUntil(clients.openWindow(event.notification.data.url));
});

PWA cihazda yüklüyse, PWA cihazda gösterilir. PWA yüklü değilse, kullanıcı tarayıcısında uygulamanın sayfasına yönlendirilir.

Ek kaynaklar