1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BlazorでWebPushを送ろう

Posted at

WebPushをBlazor WASMに送ろう

WebPushを送る案件に出会いました。WebPushに関しJavascriptでの文献は色々ありますが、Blazor + .NETでの実装は見かけることがありませんでしたので、JavascriptをBlazorに変換してみました。

環境

.NET8
Blazor WASM

ソース

上記のコードに後述するPublicKey、PrivateKeyを設定することで試すことが出来ます。

PublicKey、PrivateKeyの作成

web-pushコマンドがインストールされていない場合はPowerShellなどでインストールします

npm install web-push -g

PublicKey、PrivateKeyを生成

web-push generate-vapid-keys

生成されたPublicKey(PublicKey),PrivateKey(PrivateKey)を大切に保管してください(GithubなどにもPushしないでください)

=======================================

Public Key:
BCrj9mDVHUW7Lw5e1v4OnusRY6A4Z02pwl5nfTex0YjH--o9pUsYHqlAewyWzSysFkq0-EIXDZLs42diK10sKOM

Private Key:
JMHs03rsWmF-adpMCz1ocGOQ5YanmdsiyekkQFI9aAc

=======================================

組み込み

Server

WebAPIを作成しWebPush.Serverを参照設定
Program.csに

builder.Services.AddWebPush(config =>
{
    config.PublicKey = "{{PublicKey}}";
    config.PrivateKey = "{{PrivateKey}}";
});

本来は環境変数から取得してください

新たにControllerを追加(WebPushController.cs)

using BlazorWebPush.Shared;
using Microsoft.AspNetCore.Mvc;
using PushService.Server;

namespace BlazorTest.Server.Controllers;

[Route("api/[controller]")]
[ApiController]
public class WebPushController : ControllerBase
{
    private ILogger<WebPushController> _logger { get; init; }
    private static List<Subscription> _subscriptions { get; } = new();
    private IPushNotification _pushNotification { get; init; }

    public WebPushController(ILogger<WebPushController> logger, IPushNotification pushNotification)
    {
        _logger = logger;
        _pushNotification = pushNotification;

    }

    /// <summary>
    /// 購読追加
    /// </summary>
    /// <param name="subscription"></param>
    /// <returns></returns>
    [HttpPost("Subscribe")]
    public IActionResult Subscribe([FromBody] Subscription subscription)
    {
        try
        {
            _logger.LogInformation("Subscribe: {0}", subscription);
            _subscriptions.Add(subscription);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Subscribe: {0}", subscription);
            return BadRequest();
        }

        return Ok();
    }

    /// <summary>
    /// WebPush通知送信
    /// </summary>
    /// <returns></returns>
    [HttpPost("Send")]
    public async Task<IActionResult> SendAsync()
    {
        try
        {
            _logger.LogInformation("SendAsync");
            foreach (var token in _subscriptions)
            {
                _logger.LogInformation("SendAsync: {0}", token);
                await _pushNotification.PushAsync(token.EndPoint, token.P256dh, token.Auth, "Hello, World!!!!");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "SendAsync");
            return BadRequest();
        }
        return Ok();
    }
}

  • SubscribeはClientで購読した情報を登録
  • Sendは購読一覧にメッセージを送信

Client

WebPush.Clientを参照設定します。

購読管理を行うページに

<WebPushControl @ref="webPushControl" WebPushPublicKey="@webPushPublicKey"></WebPushControl>

{{PublicKey}}は置き換えてください

@code{
    private string webPushPublicKey = "{{PublicKey}}";
}

購読処理(サンプルではhome.razorにInitボタンで行っています)

<button @onclick="RequestNotificationSubscriptionAsync">Init</button>
    public required WebPushControl webPushControl;
    async Task RequestNotificationSubscriptionAsync()
    {
        var subscription = await webPushControl.RequestNotificationSubscriptionAsync();

        // subscriptionがnullじゃない場合Consoleに出力
        if (subscription != null)
        {
            Console.WriteLine(subscription.Url);
            Console.WriteLine(subscription.P256dh);
            Console.WriteLine(subscription.Auth);

            await _webPushUsecase.SubscribeAsync(subscription.Url!, subscription.P256dh!, subscription.Auth!);
        }
    }

WebPushControlは機能的にはほぼ何もしておらず、WebPushJsInteropを呼んでいます。
WebPushJsInteropはJavascriptのロードやServiceWorker登録を行っています。

    public class WebPushJsInterop : IAsyncDisposable
    {
        private readonly Lazy<Task<IJSObjectReference>> moduleTask;

        public WebPushJsInterop(IJSRuntime jsRuntime)
        {
            moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
                "import", "./_content/WebPush.Client/web-push.js").AsTask());
        }

        public async ValueTask ServiceWorkRegister()
        {
            var module = await moduleTask.Value;
            await module.InvokeVoidAsync("serviceWorkRegister", null);
        }

        public async ValueTask<NotificationSubscription> RequestSubscription(string publicKey)
        {
            var module = await moduleTask.Value;
            return await module.InvokeAsync< NotificationSubscription>("requestSubscription", publicKey);
        }
        public async ValueTask DisposeAsync()
        {
            if (moduleTask.IsValueCreated)
            {
                var module = await moduleTask.Value;
                await module.DisposeAsync();
            }
        }
    }

動作確認

上記を組み込み、ClientのInitボタンを押すと

許可を求めるダイアログが表示され、許可を押下します
image.png

swaggerを開きSendを実行します。
image.png

画面右下に通知が出ることを確認します。
image.png

購読を解除する場合は

Edge:設定→Cookieとサイトのアクセス許可→通知を選択し削除してください。

Chrome:設定→プライバシーとセキュリティ→サイトの設定→通知

Edgeの場合ダイアログではなく
image.png
アドレスバーにベルマークが出る場合があります。
その場合、ベルマークを押すと許可することが出来ます。

WebPushの内容

WebPushはWebPush.Clientプロジェクトにあるservice-worker.js

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

でおこなっていますので、ここをカスタマイズしてください。

参考ページ

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?