Kullanıcının Abone Olması

Matt Gaunt

İlk adım, kullanıcıdan push mesajı göndermek için izin almaktır. Ardından PushSubscription elde edebiliriz.

Bunu yapmak için kullanacağınız JavaScript API oldukça basittir. Mantık akışını adım adım inceleyelim.

Özellik algılama

Öncelikle mevcut tarayıcının push mesajlaşmayı destekleyip desteklemediğini kontrol etmemiz gerekiyor. İki basit kontrolle push'in desteklenip desteklenmediğini kontrol edebiliriz.

  1. navigator üzerinde serviceWorker olup olmadığını kontrol edin.
  2. Pencere'de PushManager olup olmadığını kontrol edin.
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;
}

Hem servis çalışanı hem de push mesajlaşma için tarayıcı desteği hızla artıyor olsa da her ikisinde de özellik algılamak ve kademeli olarak geliştirmek her zaman iyi bir fikirdir.

Hizmet çalışanı kaydetme

Özellik algılama özelliğiyle hem servis çalışanlarının hem de Push'ın desteklendiğini biliyoruz. Sonraki adım, hizmet işleyicimizi "kaydetmek"tir.

Bir hizmet çalışanını kaydettiğimizde tarayıcıya hizmet çalışanı dosyamızın nerede olduğunu bildiririz. Dosya hâlâ yalnızca JavaScript'tir ancak tarayıcı, itme dahil olmak üzere hizmet çalışanı API'lerine "erişim verir". Daha doğrusu, tarayıcı dosyayı bir hizmet çalışanı ortamında çalıştırır.

Bir hizmet çalışanı kaydetmek için navigator.serviceWorker.register() işlevini çağırarak dosyanızın yolunu iletin. Örneğin:

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);
    });
}

Bu işlev, tarayıcıya bir hizmet çalışanı dosyamız olduğunu ve bu dosyanın nerede bulunduğunu bildirir. Bu durumda, hizmet çalışanı dosyası /service-worker.js adresindedir. Arka planda tarayıcı, register() çağrıldıktan sonra aşağıdaki adımları uygular:

  1. Hizmet çalışanı dosyasını indirin.

  2. JavaScript'i çalıştırın.

  3. Her şey doğru şekilde çalışırsa ve hata yoksa register() tarafından döndürülen söz çözülür. Herhangi bir hata varsa söz reddedilir.

register() reddedilirse Chrome Geliştirici Araçları'nda JavaScript'inizi yazım hataları / hatalar açısından tekrar kontrol edin.

register() çözüldüğünde bir ServiceWorkerRegistration döndürür. PushManager API'ye erişmek için bu kaydı kullanırız.

PushManager API tarayıcı uyumluluğu

Tarayıcı desteği

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Kaynak

İzin isteme

Hizmet işleyicimizi kaydettik ve kullanıcıyı abone etmeye hazırız. Bir sonraki adım, kullanıcıdan push mesajı göndermek için izin almaktır.

İzin almak için kullanılan API nispeten basittir. Bunun dezavantajı, API'nin yakın zamanda geri çağırma yerine Promise döndürmeye geçmesidir. Bununla ilgili sorun, mevcut tarayıcı tarafından API'nin hangi sürümünün uygulandığını belirleyemememizdir. Bu nedenle, her ikisini de uygulamanız ve yönetmeniz gerekir.

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.");
    }
  });
}

Yukarıdaki kodda önemli kod snippet'i Notification.requestPermission() çağrısıdır. Bu yöntem, kullanıcıya bir istem gösterir:

Masaüstü ve mobil Chrome'da izin istemi.

Kullanıcı izin istemiyle etkileşime geçtikten sonra (İzin ver, Engelle'ye basarak veya istemi kapatarak) sonuç bize bir dize olarak gönderilir: 'granted', 'default' veya 'denied'.

Yukarıdaki örnek kodda, izin verilirse askPermission() tarafından döndürülen söz çözülür. Aksi takdirde, söz reddedilmesini sağlayan bir hata atarız.

Kullanıcının "Engelle" düğmesini tıklaması gibi uç durumlarla da ilgilenmeniz gerekir. Bu durumda web uygulamanız kullanıcıdan tekrar izin isteyemez. Kullanıcıların, ayarlar panelinde bulunan izin durumunu değiştirerek uygulamanızın "engelini manuel olarak kaldırmaları" gerekir. Kullanıcıdan nasıl ve ne zaman izin isteyeceğinizi dikkatlice düşünün. Kullanıcı engellemeyi tıklarsa bu kararı geri almak kolay değildir.

Neyse ki çoğu kullanıcı, izin neden istediğini anladığı sürece izin vermekten memnuniyet duyar.

Bazı popüler sitelerin izin isteğinde bulunma şeklini daha sonra inceleyeceğiz.

PushManager ile kullanıcıları abone etme

Hizmet işleyicimizi kaydettikten ve izin aldıktan sonra registration.pushManager.subscribe() işlevini çağırarak kullanıcıları abone edebiliriz.

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;
    });
}

subscribe() yöntemini çağırırken hem zorunlu hem de isteğe bağlı parametrelerden oluşan bir options nesnesi iletiyoruz.

İletilebilen tüm seçeneklere göz atalım.

userVisibleOnly seçenekleri

Push, tarayıcılara ilk kez eklendiğinde geliştiricilerin bildirim göstermeden push mesajı gönderip gönderemeyeceği konusunda belirsizlik vardı. Kullanıcının arka planda bir şey olduğunu bilmediği için bu işleme genellikle sessiz push adı verilir.

Geliştiricilerin, kullanıcının bilgisi olmadan sürekli olarak konumunu izlemek gibi kötü şeyler yapabileceği endişesi vardı.

Bu senaryoyu önlemek ve spesifikasyon yazarlarına bu özelliği en iyi şekilde nasıl destekleyebileceklerini düşünmeleri için zaman tanımak amacıyla userVisibleOnly seçeneği eklendi. true değeri gönderildiğinde, web uygulamasının her push aldığında bildirim göstereceği (yani sessiz push olmaz) konusunda tarayıcı ile sembolik bir sözleşme yapılmış olur.

Şu anda true değeri göndermeniz gerekir. userVisibleOnly anahtarını veya geçişini false içine eklemezseniz aşağıdaki hatayı alırsınız:

Chrome şu anda yalnızca kullanıcı tarafından görülebilen mesajlarla sonuçlanacak abonelikler için Push API'yi desteklemektedir. Bunun yerine pushManager.subscribe({userVisibleOnly: true}) numaralı telefonu arayarak bu durumu belirtebilirsiniz. Daha fazla bilgi için https://goo.gl/yqv4Q4 adresine göz atın.

Şu anda, sessiz push'in Chrome'da hiçbir zaman uygulanmayacağı anlaşılıyor. Bunun yerine, spesifikasyon yazarları, web uygulamalarının kullanımına göre belirli sayıda sessiz push mesajı göndermesine olanak tanıyacak bir bütçe API'si fikrini araştırıyor.

applicationServerKey seçeneği

Önceki bölümde "uygulama sunucusu anahtarları"ndan kısaca bahsetmiştik. "Uygulama sunucusu anahtarları", bir kullanıcıya abone olan uygulamayı tanımlamak ve aynı uygulamanın bu kullanıcıya mesaj göndermesini sağlamak için bir push hizmeti tarafından kullanılır.

Uygulama sunucusu anahtarları, uygulamanıza özgü bir genel ve özel anahtar çiftidir. Özel anahtar, uygulamanızda gizli tutulmalıdır. Ortak anahtar ise serbestçe paylaşılabilir.

subscribe() çağrısına iletilen applicationServerKey seçeneği, uygulamanın herkese açık anahtarıdır. Tarayıcı, kullanıcıyı abone ederken bunu bir push hizmetine iletir. Bu sayede push hizmeti, uygulamanızın herkese açık anahtarını kullanıcının PushSubscription ile bağlayabilir.

Aşağıdaki şemada bu adımlar gösterilmektedir.

  1. Web uygulamanız bir tarayıcıya yüklenir ve herkese açık uygulama sunucusu anahtarınızı ileterek subscribe() işlevini çağırırsınız.
  2. Ardından tarayıcı, bir uç nokta oluşturacak, bu uç noktayı uygulamanın ortak anahtarıyla ilişkilendirecek ve uç noktayı tarayıcıya döndürecek bir push hizmetine ağ isteği gönderir.
  3. Tarayıcı, bu uç noktayı subscribe() vaadi aracılığıyla döndürülen PushSubscription'e ekler.

Abone olma yönteminde kullanılan herkese açık uygulama sunucusu anahtarını gösteren resim.

Daha sonra bir push mesajı göndermek istediğinizde, uygulama sunucunuzun gizli anahtarıyla imzalanmış bilgileri içeren bir Yetkilendirme başlığı oluşturmanız gerekir. Push hizmeti, push mesajı gönderme isteği aldığında, isteği alan uç noktaya bağlı ortak anahtarı arayarak bu imzalı Yetkilendirme üstbilgisini doğrulayabilir. İmza geçerliyse itme hizmeti, eşleşen özel anahtarla uygulama sunucusundan gelmiş olması gerektiğini bilir. Temel olarak, bir uygulamanın kullanıcılarına başkalarının mesaj göndermesini engelleyen bir güvenlik önlemidir.

Mesaj gönderirken özel uygulama sunucusu anahtarı nasıl kullanılır?

Teknik olarak applicationServerKey isteğe bağlıdır. Ancak Chrome'da en kolay uygulama için bu gereklidir ve diğer tarayıcılarda gelecekte gerekli olabilir. Firefox'ta isteğe bağlıdır.

Uygulama sunucusu anahtarının nasıl olması gerektiğini tanımlayan spesifikasyon VAPID spesifikasyonudur. "Uygulama sunucusu anahtarları" veya "VAPID anahtarları" ile ilgili bir şey okuduğunuzda bunların aynı şey olduğunu unutmayın.

Uygulama sunucusu anahtarları oluşturma

web-push-codelab.glitch.me adresini ziyaret ederek herkese açık ve özel bir uygulama sunucusu anahtar grubu oluşturabilir veya aşağıdakileri yaparak anahtar oluşturmak için web-push komut satırını kullanabilirsiniz:

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

Uygulamanız için bu anahtarları yalnızca bir kez oluşturmanız gerekir. Özel anahtarı gizli tuttuğunuzdan emin olun. (Evet, bunu az önce söyledim.)

İzinler ve subscribe()

subscribe() çağrısının bir yan etkisi vardır. Web uygulamanız, subscribe() çağrısı sırasında bildirim gösterme iznine sahip değilse tarayıcı sizin adınıza izinleri ister. Kullanıcı arayüzünüz bu akışla çalışıyorsa bu yöntem faydalıdır. Ancak daha fazla kontrol sahibi olmak istiyorsanız (ki çoğu geliştiricinin bunu isteyeceğini düşünüyorum) daha önce kullandığımız Notification.requestPermission() API'sini kullanın.

PushSubscription nedir?

subscribe() işlevini çağırırız, bazı seçenekleri iletiriz ve karşılığında bir PushSubscription değerine çözümlenen bir söz alırız. Bu da aşağıdaki gibi bir kodla sonuçlanır:

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;
    });
}

PushSubscription nesnesi, ilgili kullanıcıya push mesajı göndermek için gereken tüm bilgileri içerir. İçeriği JSON.stringify() kullanarak yazdırırsanız aşağıdakileri görürsünüz:

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

endpoint, push hizmetleri URL'sidir. Bir push mesajı tetiklemek için bu URL'ye bir POST isteği gönderin.

keys nesnesi, push mesajıyla gönderilen mesaj verilerini şifrelemek için kullanılan değerleri içerir (bu bölümde daha sonra ele alacağız).

Süre sonunu önlemek için düzenli olarak yeniden abone olma

Push bildirimlerine abone olduğunuzda genellikle null ile ilgili bir PushSubscription.expirationTime alırsınız. Teorik olarak bu, aboneliğin süresinin hiçbir zaman dolmayacağı anlamına gelir (aboneliğin süresinin tam olarak ne zaman dolacağını belirten bir DOMHighResTimeStamp aldığınız durumlara kıyasla). Ancak pratikte, tarayıcıların aboneliklerin süresinin dolmasına izin vermesi yaygındır. Örneğin, uzun süre boyunca push bildirimi alınmadıysa veya tarayıcı, kullanıcının push bildirimi izni olan bir uygulama kullanmadığını algıladıysa abonelik süresi dolabilir. Bunu önlemek için kullanabileceğiniz yöntemlerden biri, aşağıdaki snippet'te gösterildiği gibi, her bildirim aldığında kullanıcıyı yeniden abone etmektir. Bu, tarayıcıda aboneliğin otomatik olarak süresinin dolmaması için bildirimleri yeterince sık göndermenizi gerektirir. Bu nedenle, geçerli bildirim ihtiyaçlarının avantajlarını ve dezavantajlarını, yalnızca aboneliğin süresinin dolmaması için kullanıcıya istemeden spam gönderme durumuna karşı çok dikkatli bir şekilde tartmanız gerekir. Sonuç olarak, kullanıcıyı uzun zaman önce unutulmuş bildirim aboneliklerinden korumaya çalışan tarayıcıya karşı çıkmamalısınız.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Sunucunuza abonelik gönderme

Bir push aboneliğiniz olduğunda bunu sunucunuza göndermeniz gerekir. Bunu nasıl yapacağınız size bağlıdır ancak abonelik nesnesinden gerekli tüm verileri almak için JSON.stringify() kullanmanızı öneririz. Alternatif olarak, aynı sonucu manuel olarak aşağıdaki gibi oluşturabilirsiniz:

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);

Abonelik, web sayfasında aşağıdaki gibi gönderilir:

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.');
      }
    });
}

Düğüm sunucusu bu isteği alır ve verileri daha sonra kullanmak üzere bir veritabanına kaydeder.

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.',
          },
        }),
      );
    });
});

Sunucumuzdaki PushSubscription ayrıntıları sayesinde istediğimiz zaman kullanıcımıza mesaj gönderebiliriz.

Süre sonunu önlemek için düzenli olarak yeniden abone olma

Push bildirimlerine abone olduğunuzda genellikle null ile ilgili bir PushSubscription.expirationTime alırsınız. Teorik olarak bu, aboneliğin süresinin hiçbir zaman dolmayacağı anlamına gelir (aboneliğin süresinin tam olarak ne zaman dolacağını belirten bir DOMHighResTimeStamp aldığınız durumlara kıyasla). Ancak pratikte, tarayıcıların aboneliklerin süresinin dolmasına izin vermesi yaygındır. Örneğin, uzun süre boyunca push bildirimi alınmadıysa veya tarayıcı, kullanıcının push bildirimi izni olan uygulamayı kullanmadığını algıladıysa abonelik süresi dolabilir. Bunu önlemek için kullanabileceğiniz yöntemlerden biri, aşağıdaki snippet'te gösterildiği gibi, her bildirim aldığında kullanıcıyı yeniden abone etmektir. Bu durumda, tarayıcı aboneliğin süresini otomatik olarak sonlandırmaması için bildirimleri yeterince sık göndermeniz gerekir. Ayrıca, geçerli bildirim ihtiyaçlarının avantajlarını ve dezavantajlarını, yalnızca aboneliğin süresinin dolmaması için kullanıcıya spam göndermeyle karşılaştırmalı olarak çok dikkatli bir şekilde değerlendirmeniz gerekir. Sonuç olarak, kullanıcıyı uzun zaman önce unutulmuş bildirim aboneliklerinden korumaya çalışan tarayıcıya karşı çıkmamalısınız.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

SSS

Bu aşamada kullanıcıların sıklıkla sorduğu bazı soruları aşağıda bulabilirsiniz:

Bir tarayıcının kullandığı push hizmetini değiştirebilir miyim?

Hayır. Push hizmeti tarayıcı tarafından seçilir ve subscribe() çağrısında gördüğümüz gibi tarayıcı, PushSubscription'ı oluşturan ayrıntıları almak için push hizmetine ağ istekleri gönderir.

Her tarayıcı farklı bir Push Hizmeti kullanıyor. Farklı API'leri yok mu?

Tüm push hizmetleri aynı API'yi bekler.

Bu yaygın API, Web Push Protokolü olarak adlandırılır ve uygulamanızın bir push mesajını tetiklemek için yapması gereken ağ isteğini açıklar.

Bir kullanıcıyı masaüstünde abone edersem telefonunda da abone olur mu?

Maalesef hayır. Kullanıcıların, mesaj almak istedikleri her tarayıcıda push'e kaydolması gerekir. Bunun için kullanıcının her cihazda izin vermesi gerektiğini de unutmayın.

Sonraki adımlar

Codelab uygulamaları