WebPush機能を使ってみる

はじめましてtaanatsuです よろしくお願いいたします。

初記事ということで、今回はWebPush機能を作っていこうと思います。
それでは、やっていきましょうか。

流れ

  1. Push通知を許可するかの確認用リンク作成
  2. manifest.jsonの作成
  3. サービスワーカー用のJavaScriptを作成
  4. 公開鍵と秘密鍵を使ってサーバーキーを作成
  5. WebサーバからPush通知を送信できるようにする
  6. Push通知を無効化する

以上です

Push通知を許可するかの確認用リンク作成

まずはPush通知を受信する、ブラウザ用のコードを書いていきます。
以下のようなHTMLを作成します。

index.htmlとして保存します。

<!DOCTYPE html>
<html lang="ja">
<head>
   <title>WebPushテスト</title>

   <meta charset='utf-8'>
   <meta name='viewport' content='width=device-width,initial-scale=1'>

   <meta name="apple-mobile-web-app-capable" content="yes">
   <meta name="apple-mobile-web-app-status-bar-style" content="black">
   <meta name="apple-mobile-web-app-title" content="WebPusher">
   <link rel="apple-touch-icon" href="icon-152x152.png">

   <!-- ウェブアプリマニフェストの読み込み -->
   <link rel="manifest" href="manifest.json">

   <link rel='icon' type='image/png' href='favicon.png'>

    <script defer src='service-worker.js'></script>
    <script src='webpush.js'></script>
</head>

<body>

    <a href="javascript:allowWebPush()">WebPushを許可する</a>

</body>
</html>

manifest.jsonの作成

こちらも簡易的ではありますが、作成します。

{
    "name": "example webpush",
    "short_name": "webpush",
    "theme_color": "#44518d",
    "background_color": "#2e3659",
    "display": "standalone",
    "scope": "/",
    "start_url": "/"
}

サービスワーカー用のJavaScriptを作成

ここからが本番です。
サービスワーカーを利用して、プッシュ通知の機能をつけていきます。

以下のファイルをservice-worker.jsとして保存します。

/**
 * プッシュ通知を受け取ったときのイベント
 */
self.addEventListener('push', function (event) {
    const title = 'Push通知テスト';
    const options = {
        body: event.data.text(),  // サーバーからのメッセージ
        tag: title,               // タイトル
        icon: 'icon-512x512.png', // アイコン
        badge: 'icon-512x512.png' // アイコン
    };

    event.waitUntil(self.registration.showNotification(title, options));
});


/**
 * プッシュ通知をクリックしたときのイベント
 */
self.addEventListener('notificationclick', function (event) {
    event.notification.close();

    event.waitUntil(
        // push通知がクリックされたら開くページ
        clients.openWindow('https://localhost:8080')
    );
});

公開鍵と秘密鍵を使ってサーバーキーを作成

以下のサイト様で発行できます。
Public KeyPrivate Keyをコピペで持ってきます。

https://web-push-codelab.glitch.me/

これらの情報を用いてサーバーキーを作成します。
以下のコードをwebpush.jsとして保存します。
また、3行目のPUBLIC_KEYに上記サイトで取得したPublic Keyを当て込めてください。

/**
 * 共通変数
 */
const PUBLIC_KEY = 'ここに取得した「Public Key」を書く';


/**
 * サービスワーカーの登録
 */
 self.addEventListener('load', async () => {
    if ('serviceWorker' in navigator) {
        window.sw = await navigator.serviceWorker.register('/service-worker.js', {scope: '/'});
    }
});


/**
 * WebPushを許可する仕組み
 */
async function allowWebPush() {
    if ('Notification' in window) {
        let permission = Notification.permission;

        if (permission === 'denied') {
            alert('Push通知が拒否されているようです。ブラウザの設定からPush通知を有効化してください');
            return false;
        }

        if (permission === 'granted') {
            alert('すでにWebPushを許可済みです');
            return false;
        }
    }
    // 取得したPublicKeyを「UInt8Array」形式に変換する
    const applicationServerKey = urlB64ToUint8Array(PUBLIC_KEY);

    // push managerにサーバーキーを渡し、トークンを取得
    let subscription = undefined;
    try {
        subscription = await window.sw.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey
        });
    } catch (e) {
        alert('Push通知機能が拒否されたか、エラーが発生したか、iPhoneでの実行かが原因でPush通知は送信されません。');
        return false;
    }


    // 必要なトークンを変換して取得(これが重要!!!)
    const key = subscription.getKey('p256dh');
    const token = subscription.getKey('auth');
    const request = {
        endpoint: subscription.endpoint,
        publicKey: btoa(String.fromCharCode.apply(null, new Uint8Array(key))),
        authToken: btoa(String.fromCharCode.apply(null, new Uint8Array(token)))
    };

    console.log(request);
}



/**
 * トークンを変換するときに使うロジック
 * @param {*} base64String 
 */
function urlB64ToUint8Array (base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/');

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

ここまでで、フロントエンドの準備は完了です。
サーバ環境で実行してみます。

PHPがインストールされていれば、簡易サーバを簡単に立ち上げることができるのでそれを利用してみてもいいでしょう、

$ cd index.htmlがおいてあるディレクトリ
$ php -S localhost:8080

上記コマンドの場合、 http://localhost:8080 にアクセスするとページが表示されるかと思います。

「WebPushを許可する」を押してPush通知を許可してみます。
これでPush通知を受信する手はずは整いました。

ここでブラウザコンソールを見てみると、

  • endpoint
  • publicKey
  • authToken

の情報が表示されると思います。
これらはPush通知を送信する際に必要になりますので、適当な場所にコピペして残しておいてください。
webpush.jsの52〜54行目にこれらを表示するコードが仕込んであります)
なお、本来の使い方としては、これらはDBなどに保存しておき、PHPなどから利用します。

WebサーバからPush通知を送信できるようにする

ここからはPush通知を送信するお話。
PHPでサーバからブラウザに向けてPush通知を送信していきます。

まずは以下のライブラリをcomposerを利用して取得します。
https://github.com/web-push-libs/web-push-php

$ composer require minishlink/web-push

次に、Push通知を送信するコードを作成していきます。 SendPush.phpとして保存します。 コード中のキーなどをの情報は適宜変更してください。

<?php
require_once 'vendor/autoload.php';

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

const VAPID_SUBJECT = 'ここにあなたのWebサイトのURL(http://localhost:8080/ など)';
const PUBLIC_KEY = '公開鍵( https://web-push-codelab.glitch.me/ で取得したもの )';
const PRIVATE_KEY = '秘密鍵( https://web-push-codelab.glitch.me/ で取得したもの )';

// push通知認証用のデータ
$subscription = Subscription::create([
    'endpoint' => 'ブラウザのコンソールで表示されていた「endpoint」',
    'publicKey' => 'ブラウザのコンソールで表示されていた「publicKey」',
    'authToken' => 'ブラウザのコンソールで表示されていた「authToken」',
]);

// ブラウザに認証させる
$auth = [
    'VAPID' => [
        'subject' => VAPID_SUBJECT,
        'publicKey' => PUBLIC_KEY,
        'privateKey' => PRIVATE_KEY,
    ]
];

$webPush = new WebPush($auth);

$report = $webPush->sendOneNotification(
    $subscription,
    'push通知の本文です'
);

$endpoint = $report->getRequest()->getUri()->__toString();

if ($report->isSuccess()) {
    echo '送信成功ヽ(=´▽`=)ノ';
} else {
    echo '送信失敗(´;ω;`)';
}

これで準備は完了です。
実行してみましょう。

$ php SendPush.php

これでブラウザにPush通知が表示されたと思います。

Push通知を無効化する

次はPush通知を無効化していきましょう。 webpush.jsに以下のコードを追加します。

/**
 * WebPushの許可を取り消す
 */
async function denyWebPush() {
    const subscription = await window.sw.pushManager.getSubscription();
    if (subscription) {
        // Push通知を許可している場合は許可を取り消す
        subscription.unsubscribe();
        const request = {
            endpoint: subscription.endpoint
        };

        // このendpointをキーに、DBから情報を消したりする
        console.log(request);
    }
}

HTMLの方には以下のコードを追記

<a href="javascript:denyWebPush()">WebPushを拒否する</a>

リンクをクリックするとブラウザコンソールにendpointが表示されると思います。
これでPush通知は無効化されました。

試しにPush通知を送信してみてください。

$ php SendPiush.php

すると送信失敗のメッセージが表示されると思います。

ブラウザコンソールに表示させたendpointは、Push通知の許可を行った際に発行されるendpointと同値になるはずです。
DBにPush通知の送信先情報を登録していた場合、このendpointの情報をもとにDBからデータを削除するのに使います。

以上となります。

終わりに

ブラウザでPush通知を行える時代が来ております。
お手軽にPush通知を試せ、サービスにもお手軽に組み込めそうで良い感じですね。(iPhoneは対応してくれませんが…)
極力コピペで作れるようにしておりますので、ぜひともお試しください。

今回はここまで。
それではまた次回お会いしましょう。

参考にさせていただいたサイト様

記事を書く上でお世話になりました。