Subskrybowanie użytkownika

Pierwszym krokiem jest uzyskanie od użytkownika zgody na wysyłanie mu wiadomości push, a następnie przekazanie nam urządzenia PushSubscription.

Interfejs JavaScript API, który służy do tego celu, jest dość prosty, dlatego przejdźmy przez cały proces logiki.

Wykrywanie funkcji

Najpierw musimy sprawdzić, czy obecna przeglądarka obsługuje przesyłanie wiadomości push. Aby sprawdzić, czy funkcja push jest obsługiwana, mamy 2 proste testy.

  1. Sprawdź, czy w narzędziu navigator występuje serviceWorker.
  2. Sprawdź, czy w oknie widoczna jest usługa PushManager.
if (!('serviceWorker' in navigator)) {
  // Service Worker isn't supported on this browser, disable or hide UI.
  return;
}

if (!('PushManager' in window)) {
  // Push isn't supported on this browser, disable or hide UI.
  return;
}

Choć obsługa skryptów service worker i komunikatorów push rośnie szybko, warto włączyć wykrywanie obu tych funkcji i stopniowo je ulepszać.

Rejestrowanie skryptu service worker

Funkcja wykrywa, że obsługiwane są zarówno mechanizmy Service Worker, jak i Push. Kolejnym krokiem jest „zarejestrowanie” naszego skryptu service worker.

Gdy rejestrujemy skrypt service worker, przeglądarka informuje przeglądarkę, gdzie znajduje się nasz plik. Plik wciąż ma format JavaScript, ale przeglądarka „przyzna mu dostęp” do jego interfejsów API, w tym „push”, Mówiąc ściśle, przeglądarka uruchamia plik w środowisku skryptu service worker.

Aby zarejestrować skrypt service worker, wywołaj navigator.serviceWorker.register(), przekazując ścieżkę do naszego pliku. W ten sposób:

function registerServiceWorker() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      console.log('Service worker successfully registered.');
      return registration;
    })
    .catch(function (err) {
      console.error('Unable to register service worker.', err);
    });
}

Ta funkcja informuje przeglądarkę, że istnieje plik skryptu service worker i gdzie się on znajduje. W tym przypadku plik skryptu service worker znajduje się pod adresem /service-worker.js. W tle przeglądarka wykona te czynności po wywołaniu funkcji register():

  1. Pobierz plik skryptu service worker.

  2. Uruchom kod JavaScript.

  3. Jeśli wszystko działa prawidłowo i nie wystąpią błędy, obietnica zwrócona przez funkcję register() zostanie rozpatrzona. Jeśli wystąpią jakiekolwiek błędy, obietnica zostanie odrzucona.

Jeśli register() odrzuca kod JavaScript, sprawdź go w Narzędziach deweloperskich w Chrome pod kątem literówek i błędów.

Gdy register() zostanie rozwiązany, zwróci ServiceWorkerRegistration. Skorzystamy z tej rejestracji, aby uzyskać dostęp do interfejsu PushManager API.

Zgodność przeglądarek z interfejsem PushManager API

Obsługa przeglądarek

  • 42
  • 17
  • 44
  • 16

Źródło

Prośba o zgodę

Zarejestrowaliśmy nasz mechanizm service worker i jest on gotowy do zasubskrybowania użytkownika. Następnym krokiem jest uzyskanie od niego uprawnień do wysyłania mu wiadomości push.

Interfejs API służący do uzyskiwania uprawnień jest stosunkowo prosty, ale jego wadą jest to, że niedawno zmienił się sposób obsługi wywołania zwrotnego na zwracanie obietnicy. Problem polega na tym, że nie jesteśmy w stanie określić, która wersja interfejsu API jest zaimplementowana w bieżącej przeglądarce, więc trzeba wdrożyć obie wersje i obsługować obie.

function askPermission() {
  return new Promise(function (resolve, reject) {
    const permissionResult = Notification.requestPermission(function (result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function (permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
  });
}

Ważnym fragmentem kodu powyżej jest wywołanie Notification.requestPermission(). W ten sposób użytkownik zobaczy komunikat:

Prośba o przyznanie uprawnień w Chrome na komputery i urządzenia mobilne.

Gdy użytkownik wejdzie w interakcję z prośbą o przyznanie uprawnień, naciskając Zezwól, Zablokuj lub po prostu ją zamknie, otrzymasz wynik w postaci ciągu znaków: 'granted', 'default' lub 'denied'.

W przykładowym kodzie powyżej obietnica zwrócona przez funkcję askPermission() rozwiązuje się po jej przyznaniu. W przeciwnym razie zwracamy błąd uniemożliwiający jej odrzucenie.

Obsługiwanym przypadkiem jest między innymi kliknięcie przycisku „Zablokuj”. W takiej sytuacji Twoja aplikacja internetowa nie będzie mogła ponownie poprosić użytkownika o zgodę. Będą oni musieli ręcznie „odblokować” aplikację, zmieniając jej stan uprawnień znajdujący się w panelu ustawień. Zastanów się dobrze, w jaki sposób i kiedy prosisz użytkownika o zgodę – jeśli kliknie on „Blokuj”, nie da się łatwo cofnąć tej decyzji.

Dobra wiadomość jest taka, że większość użytkowników chętnie udzieli zgody, o ile wie, dlaczego prosi o nie.

Później przyjrzymy się temu, w jaki sposób niektóre popularne witryny proszą o pozwolenie.

Subskrybowanie użytkownika przy użyciu PushManager

Gdy już zarejestrujemy naszego mechanizmu Service Worker i będziemy mieć pozwolenie, możemy zasubskrybować użytkownika, wywołując registration.pushManager.subscribe().

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Wywołując metodę subscribe(), przekazujemy obiekt options, który składa się z parametrów wymaganych i opcjonalnych.

Przyjrzyjmy się wszystkim opcjom, które możemy przekazać.

Opcje uservisibleOnly

Gdy po raz pierwszy wprowadziliśmy w przeglądarkach funkcję push, nie wiadomo było, czy deweloperzy powinni wysyłać wiadomości push bez ich wyświetlania. Zwykle nazywa się to cichą funkcją push, ponieważ użytkownik niewie, że coś się stało w tle.

Obawiano się, że deweloperzy mogą robić nieodpowiednie rzeczy, takie jak śledzenie lokalizacji użytkownika na bieżąco bez jego wiedzy.

Aby uniknąć tej sytuacji i dać autorom specyfikacji czas na zastanowienie się, jak najlepiej obsługiwać tę funkcję, dodaliśmy opcję userVisibleOnly, a przekazywanie wartości true oznacza symboliczną umowę z przeglądarką, że aplikacja internetowa będzie wyświetlać powiadomienie po każdym otrzymaniu żądania push (czyli brak cichego push).

Obecnie musisz przekazać wartość true. Jeśli nie podasz klucza lub biletu userVisibleOnly w false, pojawi się ten błąd:

Chrome obsługuje obecnie interfejs Push API tylko w przypadku subskrypcji, dzięki którym wiadomości będą widoczne dla użytkowników. Możesz to zgłosić, wywołując metodę pushManager.subscribe({userVisibleOnly: true}). Więcej informacji znajdziesz na stronie https://goo.gl/yqv4Q4.

Obecnie wygląda na to, że w Chrome nigdy nie zostanie zaimplementowana dyskretna funkcja push. Zamiast tego autorzy specyfikacji analizują koncepcję interfejsu API budżetu, który umożliwi aplikacjom internetowym określoną liczbę dyskretnych wiadomości push na podstawie wykorzystania aplikacji internetowej.

Opcja ApplicationServerKey

W poprzedniej sekcji wspomnieliśmy pokrótce o „kluczach serwera aplikacji”. „Klucze serwera aplikacji” są używane przez usługę push do identyfikowania aplikacji subskrybującej użytkownika i gwarantowania, że ta sama aplikacja przesyła do niego wiadomości.

Klucze serwera aplikacji to para kluczy publiczny i prywatny, który jest unikalny dla Twojej aplikacji. Klucz prywatny należy przechowywać w tajemnicy dla aplikacji, a klucz publiczny można swobodnie udostępniać.

Opcja applicationServerKey przekazywana do wywołania subscribe() jest kluczem publicznym aplikacji. Przeglądarka przekazuje te informacje do usługi push podczas subskrybowania użytkownika. Oznacza to, że usługa push może powiązać klucz publiczny aplikacji z kluczem PushSubscription użytkownika.

Poniższy diagram przedstawia te kroki.

  1. Twoja aplikacja internetowa jest wczytywana w przeglądarce i wywołujesz subscribe(), przekazując publiczny klucz serwera aplikacji.
  2. Następnie przeglądarka wysyła żądanie sieciowe do usługi push, która wygeneruje punkt końcowy, powiąże ten punkt końcowy z kluczem publicznym aplikacji i zwróci punkt końcowy do przeglądarki.
  3. Przeglądarka doda ten punkt końcowy do obiektu PushSubscription, który jest zwracany za pomocą obietnicy subscribe().

Ilustracja publicznego klucza serwera aplikacji jest używana w metodzie subskrybowania.

Jeśli zechcesz później wysłać wiadomość push, konieczne będzie utworzenie nagłówka Authorization, który będzie zawierał informacje podpisane kluczem prywatnym serwera aplikacji. Gdy usługa push otrzyma żądanie wysłania wiadomości push, może zweryfikować podpisany nagłówek Authorization, wyszukując klucz publiczny połączony z punktem końcowym, który odbiera żądanie. Jeśli podpis jest prawidłowy, usługa push wie, że musi pochodzić z serwera aplikacji z pasującym kluczem prywatnym. Zasadniczo jest to zabezpieczenie, które zapobiega wysyłaniu wiadomości do użytkowników aplikacji przez inne osoby.

Jak jest używany prywatny klucz serwera aplikacji
podczas wysyłania wiadomości

Technicznie rzecz biorąc, applicationServerKey jest opcjonalny. Najłatwiejsza implementacja w Chrome wymaga jednak tej funkcji i może być wymagana w przyszłości w innych przeglądarkach. W przeglądarce Firefox jest ona opcjonalna.

Specyfikacja, która określa, czym powinien być klucz serwera aplikacji, to specyfikacja VAPID. Gdy czytasz coś nawiązującego do „kluczy serwera aplikacji” lub „kluczy VAPID”, pamiętaj, że są one tym samym.

Jak tworzyć klucze serwera aplikacji

Publiczny i prywatny zestaw kluczy serwera aplikacji możesz utworzyć na stronie web-push-codelab.glitch.me. Możesz też użyć wiersza poleceń web-push, aby wygenerować klucze, wykonując te czynności:

    $ npm install -g web-push
    $ web-push generate-vapid-keys

Te klucze musisz utworzyć dla swojej aplikacji tylko raz. Pamiętaj tylko, by ustawić klucz prywatny jako prywatny. (Tak, właśnie to mówiłem).

Uprawnienia i subskrypcja()

Wywołanie numeru subscribe() ma jeden skutek uboczny. Jeśli aplikacja internetowa nie ma uprawnień do wyświetlania powiadomień w momencie wywoływania funkcji subscribe(), przeglądarka poprosi o ich przyznanie. Jest to przydatne, jeśli Twój interfejs obsługuje ten proces, ale jeśli zależy Ci na większej kontroli (a myślę, że większość deweloperów tak), trzymaj się interfejsu Notification.requestPermission() API, który użyliśmy wcześniej.

Co to jest PushSubscription?

Wywołujemy polecenie subscribe(), przekazujemy pewne opcje, a w zamian otrzymujemy obietnicę, która prowadzi do PushSubscription i powstał w ten sposób kod, na przykład:

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Obiekt PushSubscription zawiera wszystkie informacje wymagane do wysłania wiadomości push do danego użytkownika. Jeśli wydrukujesz treść za pomocą narzędzia JSON.stringify(), zobaczysz coś takiego:

    {
      "endpoint": "https://some.pushservice.com/something-unique",
      "keys": {
        "p256dh":
    "BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
        "auth":"FPssNDTKnInHVndSTdbKFw=="
      }
    }

endpoint to adres URL usługi push. Aby aktywować wiadomość push, wyślij żądanie POST na ten adres URL.

Obiekt keys zawiera wartości używane do szyfrowania danych wiadomości wysyłanych w wiadomości push (które omówimy później w tej sekcji).

Wyślij subskrypcję na Twój serwer

Gdy uzyskasz subskrypcję push, prześlij ją na swój serwer. Od Ciebie zależy, jak to zrobisz, ale najlepsza wskazówka: użyj JSON.stringify(), aby pobrać wszystkie niezbędne dane z obiektu subskrypcji. Możesz też samodzielnie wygenerować ten sam wynik w ten sposób:

const subscriptionObject = {
  endpoint: pushSubscription.endpoint,
  keys: {
    p256dh: pushSubscription.getKeys('p256dh'),
    auth: pushSubscription.getKeys('auth'),
  },
};

// The above is the same output as:

const subscriptionObjectToo = JSON.stringify(pushSubscription);

Subskrypcję możesz wysłać na stronie internetowej w ten sposób:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

Serwer węzłów odbiera to żądanie i zapisuje dane w bazie danych do późniejszego użycia.

app.post('/api/save-subscription/', function (req, res) {
  if (!isValidSaveRequest(req, res)) {
    return;
  }

  return saveSubscriptionToDatabase(req.body)
    .then(function (subscriptionId) {
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({data: {success: true}}));
    })
    .catch(function (err) {
      res.status(500);
      res.setHeader('Content-Type', 'application/json');
      res.send(
        JSON.stringify({
          error: {
            id: 'unable-to-save-subscription',
            message:
              'The subscription was received but we were unable to save it to our database.',
          },
        }),
      );
    });
});

Dzięki szczegółowym informacjom o PushSubscription na naszym serwerze możemy wysyłać do użytkownika wiadomość, kiedy tylko zechce.

Najczęstsze pytania

Kilka często zadawanych pytań przez użytkowników:

Czy mogę zmienić usługę push, z której korzysta przeglądarka?

Nie. Usługa push jest wybierana przez przeglądarkę i jak w wywołaniu subscribe() przeglądarka wysyła żądania sieciowe do usługi push, aby pobrać szczegóły składające się na PushSubscription.

Każda przeglądarka korzysta z innej usługi Push. Czy nie mają one innych interfejsów API?

Wszystkie usługi push będą korzystać z tego samego interfejsu API.

Ten typowy interfejs API nosi nazwę protokołu Web Push Protocol i opisuje żądanie sieciowe, które musi wysłać aplikacja, aby aktywować komunikat push.

Jeśli subskrybuję użytkownika na komputerze, to czy ma on też subskrypcję na telefonie?

Niestety nie. Użytkownik musi zarejestrować się w usłudze push w każdej przeglądarce, w której chce odbierać wiadomości. Pamiętaj też, że będzie to wymagało przyznania uprawnień na każdym z urządzeń.

Co dalej

Laboratoria kodu