1. 課題とスコープ
Eコマースの Web Push 通知で注文詳細を開く必要があります。アプリが閉じられている場合、既存のウィンドウが開いている場合、またはユーザーがアクションをアクティブにする場合があります。navigate オプションを使用してディープリンクを設計し、URL パース、権限、クリックのフォールバック、重複オープン、Service Worker のライフタイムをカバーしてください。
2. 面接官が見ているポイント
NotificationOptions.navigateがナビゲーション URL であり、Notification.navigateがパースされた絶対 URL または空文字列を公開することを理解しているか。- デフォルトの通知ナビゲーション、アクションナビゲーション、および
notificationclickのフォールバック処理を区別できるか。 - 権限、HTTPS、同一オリジンポリシー(same-origin policy)、オープンリダイレクト、重複通知、既存ウィンドウの再利用を適切に処理できるか。
- ブラウザのナビゲーションを認可として扱うのではなく、ルート復元、認証、およびべき等性をアプリケーション層に保持できるか。
3. 最初に明確にすべき質問
showNotificationを呼び出すのはページコードですか、それとも Service Worker ですか?- 外部 URL は許可されていますか?また、認証が必要な注文ページはサインイン後にどのように再開すべきですか?
- 本体のクリックとアクションのクリックは、異なるルートを開くべきですか、それとも異なる操作を実行すべきですか?
- 同一オリジンのウィンドウが存在する場合、プロダクトはそれにフォーカスすべきですか、それとも別のウィンドウを作成すべきですか?
4. 30秒の回答
HTTPS 環境下で、Service Worker に同一オリジンの許可リストをパスする navigate URL を持つ永続的な通知を作成させます。ナビゲーション URL を持つ本体またはアクションはブラウザによって処理可能です。URL を持たないアクションは notificationclick にフォールバックし、そこで既存のウィンドウにフォーカスするかリカバリルートを開きます。権限、無効な URL、認証の失敗は、依然としてアプリケーション側で明示的に処理する必要があります。
5. ステップごとの詳細解説
ステップ 1: 検証済み通知 URL の構築
const target = new URL(`/orders/${orderId}`, self.location.origin);
await self.registration.showNotification("Order shipped", {
body: "View tracking details",
tag: `order-${orderId}`,
navigate: target.href,
data: { orderId },
});navigate は、通知が作成されたときに使用されたベース URL に基づいて解決されます。サーバーから提供されたパスは、同一オリジンおよびルート許可リストのチェックを通過しなければなりません。任意のユーザー入力が決してリダイレクト先になってはなりません。
ステップ 2: 本体とアクションのナビゲーションの分離
await self.registration.showNotification("Order needs confirmation", {
body: "Choose an action",
navigate: "/orders/123",
actions: [
{ action: "open", title: "View order", navigate: "/orders/123" },
{ action: "help", title: "Contact support" },
],
});アクションがアクティブになると、その独自のアクションの navigate が優先されます。その URL を持たないアクションは notificationclick に入り、そこでアプリケーション固有の動作が実行されます。本体とアクションのルートは、同じ許可リストと認証リカバリールールを共有する必要があります。
ステップ 3: クリックフォールバックと既存ウィンドウの処理
self.addEventListener("notificationclick", (event) => {
event.notification.close();
if (event.action === "help") {
event.waitUntil(clients.openWindow("/support"));
return;
}
event.waitUntil(clients.matchAll({ type: "window", includeUncontrolled: true })
.then((windows) => windows[0]?.focus() ?? clients.openWindow("/orders/123")));
});本番コードでは、注文をハードコーディングするのではなく、ウィンドウの URL を検証し、Service Worker の制御を待ち、信頼できる data を渡す必要があります。ブラウザはトップレベルウィンドウを再利用するか新規作成する可能性があるため、アプリケーションのリカバリは両方の結果をサポートしなければなりません。
ステップ 4: 権限、プロトコル、およびセキュリティ
通知にはユーザーの権限が必要であり、永続的な通知には Service Worker が必要です。これらの API はセキュアコンテキストに依存します。権限はビジネス上の認可ではありません。注文 API はページが開いた後に再度認証を行う必要があります。オープンリダイレクトやフィッシングリンクを防ぐために、ナビゲーションを同一オリジンまたは明示的に信頼された外部オリジンに制限してください。
ステップ 5: 状態のべき等な復元
開いた後、ルートと data.orderId を読み取り、ローディング状態を表示してから、注文を取得して本人確認を行います。同一の tag を持つ通知は、プロダクトのポリシーに従ってマージまたは更新されるべきです。1 回のクリックで重複したリクエストや状態遷移が発生しないよう、ルート変更、フォーカス復元、アナリティクスはべき等である必要があります。
6. 模範的な高評価の回答
私は Service Worker に、許可リストにある同一オリジンのnavigateURL のみを作成させ、安定したtagを割り当てます。本体のクリックはブラウザのナビゲーションに従い、アクション URL が優先されます。URL を持たないアクションはnotificationclickを使用して既存のウィンドウにフォーカスするか、フォールバックを開きます。ページは再度認証を行い、注文パラメータを読み取り、状態をべき等に復元します。権限、HTTPS、オープンリダイレクト、外部 URL は個別のチェック事項であり、通知ナビゲーションが認可になることは決してありません。
7. よくある間違い
- ユーザー入力を直接
navigateに配置する → オープンリダイレクトの脆弱性 → 同一オリジンおよびルートの許可リストを適用する。 - すべてのクリックがカスタムコードに到達すると想定する → 本体のナビゲーションはブラウザによって処理される場合がある → URL のないアクションに対してのみ
notificationclickに依存する。 - 通知権限を注文の認可として扱う → データ漏洩 → ページおよび API で再度認証する。
- 常に
openWindowを呼び出す → ウィンドウやリクエストの重複 → 同一オリジンのウィンドウをマッチングし、リカバリをべき等にする。 - Service Worker のライフタイムを無視する → 非同期処理が中断される → 完了プロミスを
event.waitUntil内に保持する。
8. フォローアップの質問
フォローアップ 1: Notification.navigate は何を返しますか?
これは通知のシリアライズされた絶対ナビゲーション URL を含む読み取り専用の文字列、または有効な URL が設定されていない場合は空文字列です。
フォローアップ 2: アクションに navigate がない場合はどうなりますか?
アクションは自動的にナビゲーションしません。そのアクティベーションは、Service Worker 内の notificationclick で処理できます。
フォローアップ 3: なぜページ内で再度認証するのですか?
URL と data はナビゲーションのヒントであり、アイデンティティやリソースの認可ではありません。注文 API は現在のセッションとサーバーサイドの権限を再度チェックする必要があります。
フォローアップ 4: ウィンドウ間の動作をどのようにテストしますか?
ウィンドウがない場合、既存の同一オリジンウィンドウがある場合、制御されていないウィンドウがある場合、本体およびアクションのクリック、権限の拒否、無効な URL などをカバーし、アプリケーションのリカバリが 1 回のリクエストのみを行うことをアサートします。