こんにちは。鈴木商店の若林 (@itigoore01) です。
Laravel + AngularでWeb Push通知を実装してみたので、実装方法やハマりどころを紹介していきます。
こんな感じです。
対象読者
- Laravel、Angularをある程度使ったことがある
- Laravel、AngularでWeb Push通知を実装したい、したいけどできなかった
環境
以下の環境で開発、動作確認をしています。
コマンド、パスなどはご自身の環境に合わせて読みかえてください。
OS: macOS Catalina 10.15.4
デモ
こちらにソースコードがあるので、適当なフォルダにクローンしてください。
1 2 3 |
git clone https://github.com/itigoore01/web-push-notification-example cd web-push-notification-example make setup |
make setup
が完了したらhttp://localhostをブラウザで開くと確認できます。
実装方法
Laravelのところから読みたい方はこちら
Angularのところから読みたい方はこちら
Laravel
必要なライブラリをインストールする
ライブラリを使ったほうが圧倒的に楽なので、ライブラリをインストールします。
Installationに従ってインストールしてください。
https://github.com/laravel-notification-channels/webpush
注意点として、このライブラリはGMP (GNU Multiple Precision)
のphp extensionが必要ですので、各々の環境に合わせてインストールしてください。
Laradock
を使って開発している場合は.env
ファイルの設定を変更することでインストールできます。
GMPと名のつく設定はいくつかありますが、最低限以下の項目をtrue
にしてやればOKなはずです。
設定を変更したあとはdocker-compose build
し直すのを忘れずに。
1 2 3 |
WORKSPACE_INSTALL_GMP=true PHP_FPM_INSTALL_GMP=true PHP_WORKER_INSTALL_GMP=true |
Subscriptionを登録するControllerを実装する
フロントから渡されるPush Subscriptionを登録するコントローラーを実装していきます。
関連ソースコードは以下の通りです。
https://github.com/itigoore01/web-push-notification-example/blob/master/backend/app/Http/Controllers/Api/SubscriptionController.php
https://github.com/itigoore01/web-push-notification-example/blob/master/backend/routes/api.php#L27-L28
ポイントはログインユーザーのSubscriptionを更新すること。
通常、ユーザーIDをリクエストボディで受け取るような作りにしてはいけません。
自分以外のユーザーIDを指定して登録することで、ある人に送信したいプッシュ通知を赤の他人が受け取れてしまうからです。
もちろん、ユーザー認証がそもそも不要な、ブロードキャスト的な通知であれば問題ありません。
例えば不特定多数に新着ブログ投稿を送信するようなケースです。
1 2 3 4 5 6 7 8 |
/** @var \App\User $user */ $user = auth()->user(); // 必ずログインユーザーに対してupdatePushSubscriptionを実行する $user->updatePushSubscription( $request->input('endpoint'), $request->input('keys.p256dh'), $request->input('keys.auth') ); |
Notificationを実装する
次にWeb Push用のNotificationクラスを実装していきます。
関連ソースコードは以下の通りです。
https://github.com/itigoore01/web-push-notification-example/blob/master/backend/app/Notifications/WebPush/WebPushMessage.php
https://github.com/itigoore01/web-push-notification-example/blob/master/backend/app/Notifications/MessageSent.php
https://github.com/itigoore01/web-push-notification-example/blob/master/backend/app/Notifications/Reminder.php
ここで重要なポイントです。
まずは以下のNotificationクラスをご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace App\Notifications; use App\Notifications\WebPush\WebPushMessage; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use NotificationChannels\WebPush\WebPushChannel; class MessageSent extends Notification implements ShouldQueue { use Queueable; /** * @var string */ private $body; /** * Create a new notification instance. * * @param string $body */ public function __construct(string $body) { $this->body = $body; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return [WebPushChannel::class]; } public function toWebPush() { // WebPushMessageがApp\Notifications/WebPushにある独自クラスである必要がある return (new WebPushMessage()) ->title('Message received | Web push notification example') ->body($this->body) ->icon('/assets/icons/icon-128x128.png'); } } |
コメントにもあるように、WebPushMessage
はライブラリのものではなく独自に実装したものを使用しています。
といっても内容はシンプルにnotification
という名前でエンベロープしているだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App\Notifications\WebPush; class WebPushMessage extends \NotificationChannels\WebPush\WebPushMessage { public function toArray() { // Angular標準のngsw-worker.jsはnotification以下にtitleなどがないと通知を表示できない return [ 'notification' => parent::toArray() ]; } } |
しかし、これがないとAngular標準のSwPushでは通知を表示してくれません。
もちろん自前でServiceWorkerを実装すればこの限りではないですが、SwPushでサクッと試したいときにこれでドハマリしてしまうので注意が必要です。
以上がLaravelでの実装です。
その他
説明をWeb Pushだけに絞りましたが、それ以外にもAPIをCookie認証に変えていたり、サインイン・サインアップ・サインアウトのルーターを追加したり、通知をQueueに溜めてから送ったりと、他にもいろいろしているので、気になる方はソースコードを覗いてみてください。
Angular
必要なライブラリをインストールする
ServiceWorkerを自前で実装するのは手間なので、@angular/pwa
という公式のライブラリを利用します。
PWA用のアセットキャッシュなど諸々をやってくれるものですが、その中にプッシュ通知を簡単に実装できるものも含まれています。
参考: Angular 日本語ドキュメンテーション – Service Workerを始める
以下のコマンドで簡単にインストールできます。
1 |
ng add @angular/pwa |
Subscriptionをサーバー(Laravel)に登録する
関連ソースコードは以下の通りです。
https://github.com/itigoore01/web-push-notification-example/blob/master/frontend/src/app/core/push/subscription.service.ts
https://github.com/itigoore01/web-push-notification-example/blob/master/frontend/src/app/home/pages/home-page/home-page.component.ts#L95-L142
先にサーバーに投げるためのサービスを実装しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class SubscriptionService { constructor( private http: HttpClient, ) { } update(subscription: PushSubscriptionJSON) { return this.http.put<void>('/api/subscription', subscription); } delete(endpoint: string) { return this.http.delete<void>(`/api/subscription/${endpoint}`); } } |
次に、適当なコンポーネントでSubscriptionを受け取り、先程作成したSubscriptionService
を使ってサーバーに登録します。
以下はサンプルのHomeComponent
にある処理を簡略化したものです。
最低限これだけあれば問題なく動きます。
1 2 3 4 5 6 7 8 9 |
async subscribe() { const subscription = await this.swPush.requestSubscription({ // サンプルではAPIで受け取るようにしている // environment.tsにapplicationServerKeyをベタ書きしてもいい serverPublicKey: environment.applicationServerKey, }); this.subscriptionService.update(subscription.toJSON()).subscribe(); } |
environment.applicationServerKey
の部分は、Laravelプロジェクトフォルダ直下の.env
ファイル内にあるVAPID_PUBLIC_KEY
の値が入っている必要があります。
GitHubのサンプルではAPIで受け取るようになっていますが、environment.ts
に書いてしまってもいいです。
ただしVAPID_PRIVATE_KEY
のほうは間違っても書いてはいけませんし、ましてやgitにあげてしまってはいけません。
くれぐれも注意してください。
[2020/04/16 追記]
書き忘れていましたが、ng serve
などのコマンドではSwPush
含め、ライブラリが提供してくれるサービスワーカーが機能しないので注意が必要です。 (ng serve --prod
でも同様です)
ローカルでの実行は Angular 日本語ドキュメンテーション – Service Workerを始める を参照してください。
以上がAngularでの実装です。
その他
説明をWeb Pushだけに絞りましたが、サインイン・サインアップ画面や、Cookie認証でのAuth Guard実装など、他にもいろいろしているので、気になる方はソースコードを覗いてみてください。
ハマりどころ・注意点まとめ
- https://github.com/laravel-notification-channels/webpushはAngularではそのままでは通知を表示できない
- Angularは
notification
というプロパティの中にtitle
などがないと表示できない - 他の言語、ライブラリにおいても注意が必要
- Angularは
VAPID_PRIVATE_KEY
の取り扱いには要注意- 特にバージョン管理システムには残さないようにすること
ng serve
、ng serve --prod
ではSwPushのテストはできないので注意