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.

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

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

Tarayıcı desteği hem Service Worker hem de push mesajlaşma için hızla büyüse de, hem hizmet çalışanı hem de push mesajlaşma için özellik tespit etmek ve sürekli olarak geliştirmek iyi bir fikirdir.

Hizmet çalışanı kaydetme

Özellik algılama özelliğiyle hem servis çalışanlarının hem de Push'ın desteklendiği bilinmektedir. Bir sonraki adım, hizmet çalışanımızı "kaydetmek".

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 izni 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 service Worker dosyamızın olduğunu ve bu dosyanın bulunduğu yeri bildirir. Bu durumda, hizmet çalışanı dosyası /service-worker.js adresindedir. Perde arkasında tarayıcı, register() çağrısından sonra aşağıdaki adımları uygular:

  1. Service Worker dosyasını indirin.

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

  3. Her şey doğru şekilde çalışıyorsa ve hata yoksa register() tarafından verilen taahhüt çözümlenir. Herhangi bir hata varsa taahhüt reddedilir.

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

register() çözümlediğinde bir ServiceWorkerRegistration döndürür. Bu kaydı, PushManager API'ye erişmek için kullanacağız.

PushManager API tarayıcı uyumluluğu

Tarayıcı Desteği

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

Kaynak

İzin isteniyor

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 işlevini yerine getirmek 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.

userGörünürlük seçenekleri

Push, tarayıcılara ilk kez eklendiğinde, geliştiricilerin bildirim gösterip göstermeme konusunda push mesajı gönderip gönderemeyecekleri 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 yapabilmeleri endişe vericiydi.

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 Chrome'da hiçbir zaman sınırsız sessiz itme uygulanmayacak gibi görünüyor. Spesifikasyon yazarları bunun yerine, web uygulamalarının kullanımına bağlı olarak belirli sayıda sessiz push mesajı yayınlamasına izin verecek bir bütçe API'si kavramı üzerine çalışmalar yürütü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 sunucu anahtarınızı ileterek subscribe() işlevini çağırırsınız.
  2. Ardından tarayıcı bir push hizmetine ağ isteğinde bulunur. Bu hizmet uç nokta oluşturur, bu uç noktayı uygulamanın ortak anahtarıyla ilişkilendirir ve uç noktayı tarayıcıya döndürür.
  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 özel anahtarı ile imzalanan bilgileri içeren bir Yetkilendirme üst bilgisi 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 da gelecekte gerekli olabilir. Firefox'ta isteğe bağlıdır.

Uygulama sunucusu anahtarının ne olması gerektiğini tanımlayan spesifikasyon VAPID spesifikasyonudur. "Uygulama sunucusu anahtarları" veya "VAPID anahtarları" ile ilgili bir şey okuduğunuzda her zaman aynı şey olduklarını 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

Bu anahtarları uygulamanız için yalnızca bir kez oluşturmanız yeterlidir. Ö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 istiyorsanız (ve bence çoğu geliştiricinin isteyeceğini) düşünüyorsanız daha önce kullandığımız Notification.requestPermission() API'yi kullanın.

Push Aboneliği nedir?

subscribe() işlevini çağırıp bazı seçenekler iletiyoruz. Bunun karşılığında, PushSubscription değerine çözümlenen bir söz alıyoruz. 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 konuyu bu bölümde daha sonra ele alacağız).

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

Push bildirimlerine abone olduğunuzda genellikle null ile ilgili bir PushSubscription.expirationTime alırsınız. Teoride bu, aboneliğin hiçbir zaman sona ermeyeceği anlamına gelir (DOMHighResTimeStamp aldığınızdan farklı olarak, aboneliğin süresinin tam olarak ne zaman dolacağını gösterir). 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ının aboneliğin süresini otomatik olarak sonlandırmaması 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öndermeyle karşılaştırmalı olarak çok dikkatli bir şekilde değerlendirmeniz gerekir. Sonuç olarak, kullanıcıyı uzun zamandır unutulmuş bildirim aboneliklerinden korumak için tarayıcıyla mücadele etmeye çalışmamalı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önderin

Push aboneliğiniz olduğunda bunu sunucunuza göndermek isteyebilirsiniz. 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 şekilde 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ıyla, kullanıcılarımıza istediğimiz zaman bir ileti gönderebiliyoruz.

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 aboneliklerin süresi dolar. 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 yöntem, tarayıcının aboneliğin otomatik olarak sona ermemesi için yeterince sık bildirim göndermenizi gerektirir ve aboneliğin süresinin dolmaması için meşru bildirim ihtiyaçlarının kullanıcıya spam yapma karşısında avantaj ve dezavantajlarını çok dikkatli bir şekilde değerlendirmeniz gerekir. Sonuç olarak, kullanıcıyı uzun zamandır unutulmuş bildirim aboneliklerinden korumak için tarayıcıyla mücadele etmeye çalışmamalı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 öğesini oluşturan ayrıntıları almak için push hizmetine ağ isteklerinde bulunur.

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

Tüm push hizmetlerinde aynı API beklenir.

Bu yaygın API, Web Aktarma 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 belirtmek isteriz.

Yakında gidilecek yerler

Codelab uygulamaları