Kaynaklar Arası Hizmet Çalışanları - Yabancı Getirme ile Deneme Yapma

Arka plan

Hizmet çalışanları, web geliştiricilerine web uygulamalarının yaptığı ağ isteklerine yanıt verme olanağı tanır. Bu sayede geliştiriciler, çevrimdışıyken bile çalışmaya devam edebilir, kablosuz bağlantı sorunlarını giderebilir ve yeniden doğrulama sırasında eski gibi karmaşık önbellek etkileşimlerini uygulayabilir. Ancak hizmet çalışanları geçmişte belirli bir kaynağa bağlıydı. Web uygulamasının sahibi olarak, web uygulamanızın yaptığı tüm ağ isteklerine müdahale etmek için bir hizmet çalışanı yazıp dağıtmak sizin sorumluluğunuzdadır. Bu modelde her hizmet çalışanı, üçüncü taraf API'lere veya web yazı tiplerine yönelik kaynak ötesi istekleri bile işlemekten sorumludur.

Bir API'nin, web yazı tiplerinin veya yaygın olarak kullanılan diğer hizmetlerin üçüncü taraf sağlayıcısı, diğer kaynak tarafından kendi kaynaklarına gönderilen istekleri işleme şansı elde eden kendi hizmet işçisini dağıtma yetkisine sahip olsaydı ne olurdu? Sağlayıcılar kendi özel ağ mantıklarını uygulayabilir ve yanıtlarını depolamak için tek bir yetkili önbelleğe alma örneğinden yararlanabilir. Artık yabancı getirme sayesinde bu tür üçüncü taraf hizmet işçisi dağıtımı gerçek oldu.

Yabancı getirmeyi uygulayan bir hizmet çalışanı dağıtmak, tarayıcılardan gelen HTTPS istekleri aracılığıyla erişilen bir hizmetin sağlayıcısı için mantıklıdır. Hizmetinizin, tarayıcılarının ortak bir kaynak önbelleğinden yararlanabileceği, ağa bağlı olmayan bir sürümünü sağlayabileceğiniz senaryoları düşünün. Bu durumdan yararlanabilecek hizmetler şunlardır ancak bunlarla sınırlı değildir:

  • RESTful arayüzleri olan API sağlayıcılar
  • Web yazı tipi sağlayıcıları
  • Analiz sağlayıcılar
  • Resim barındırma sağlayıcıları
  • Genel içerik yayınlama ağları

Örneğin, bir analiz sağlayıcı olduğunuzu varsayalım. Yabancı bir getirme hizmeti işleyicisi dağıtarak, kullanıcı çevrimdışıyken hizmetinize gönderilen ve başarısız olan tüm isteklerin sıraya eklenmesini ve bağlantı geri geldiğinde yeniden oynatılmasını sağlayabilirsiniz. Bir hizmetin istemcilerinin birinci taraf hizmet işçileri aracılığıyla benzer davranışları uygulaması mümkün olsa da her istemcinin hizmetiniz için özel mantık yazmasını zorunlu kılmak, dağıttığınız paylaşılan yabancı getirme hizmet işçisine güvenmek kadar ölçeklenebilir değildir.

Ön koşullar

Kaynak deneme jetonu

Yabancı getirme özelliği hâlâ deneysel olarak kabul edilir. Bu tasarımın, tarayıcı tedarikçileri tarafından tam olarak belirtilip üzerinde anlaşmaya varılmadan erken bir şekilde uygulanmasını önlemek için Chrome 54'te Kaynak Deneme olarak uygulandı. Dış getirme deneysel olarak kaldığı sürece bu yeni özelliği barındırdığınız hizmetle kullanmak için hizmetinizin belirli kaynağına göre kapsamlı bir jeton istemeniz gerekir. Jeton, yabancı getirme aracılığıyla işlemek istediğiniz kaynaklara yönelik tüm kaynak dışı isteklerde ve hizmet çalışanı JavaScript kaynağınızın yanıtında HTTP yanıtı üst bilgisi olarak eklenmelidir:

Origin-Trial: token_obtained_from_signup

Deneme sürümü Mart 2017'de sona erecek. Bu tarihe kadar, özelliğin kararlı olması için gerekli tüm değişiklikleri yapmış ve (umarım) varsayılan olarak etkinleştirmiş olacağız. Yabancı getirme özelliği bu tarihe kadar varsayılan olarak etkinleştirilmezse mevcut Origin Trial jetonlarına bağlı işlev çalışmayı durdurur.

Resmi Origin Trial jetonuna kaydolmak için yabancı getirme özelliğini denemeden önce Chrome'da yerel bilgisayarınız için bu şartı atlamak isterseniz chrome://flags/#enable-experimental-web-platform-features adresine gidip "Deneysel Web Platformu özellikleri" işaretini etkinleştirebilirsiniz. Bu işlemin, yerel denemelerinizde kullanmak istediğiniz her Chrome örneğinde yapılması gerektiğini, kaynak deneme jetonu kullanıldığında ise özelliğin tüm Chrome kullanıcılarınız tarafından kullanılabileceğini lütfen unutmayın.

HTTPS

Tüm hizmet çalışanı dağıtımlarında olduğu gibi, hem kaynaklarınızı hem de hizmet çalışanı komut dosyanızı yayınlamak için kullandığınız web sunucusuna HTTPS üzerinden erişilmesi gerekir. Ayrıca, yabancı getirme işleminin kesilmesi yalnızca güvenli kaynaklarda barındırılan sayfalardan gelen istekler için geçerlidir. Bu nedenle, hizmetinizin istemcilerinin yabancı getirme uygulamanızdan yararlanmak için HTTPS kullanması gerekir.

Yabancı getirmeyi kullanma

Gerekli ön koşullar tamamlandığında, yabancı getirme hizmeti işçisini ayarlamak ve çalıştırmak için gereken teknik ayrıntılara göz atalım.

Hizmet çalışanınızı kaydetme

Karşılaşacağınız ilk zorluk, hizmet çalışanınızı kaydetme olacaktır. Daha önce servis çalışanlarıyla çalıştıysanız aşağıdakileri muhtemelen biliyorsunuzdur:

// You can't do this!
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('service-worker.js');
}

Birinci taraf hizmet çalışanı kaydı için bu JavaScript kodu, kullanıcının kontrol ettiğiniz bir URL'ye gittiğinde tetiklenen bir web uygulaması bağlamında anlamlıdır. Ancak tarayıcının sunucunuzla tek etkileşimi tam gezinme değil, belirli bir alt kaynağı istemek olduğunda üçüncü taraf hizmet çalışanı kaydetme yaklaşımı uygun değildir. Tarayıcı, örneğin, sizin yönettiğiniz bir CDN sunucusundan bir resim isterse bu JavaScript snippet'ini yanıtınıza ekleyip çalıştırılmasını bekleyemezsiniz. Normal JavaScript yürütme bağlamının dışında farklı bir hizmet çalışanı kaydı yöntemi gerekir.

Çözüm, sunucunuzun herhangi bir yanıta dahil edebileceği bir HTTP üst bilgisi biçimindedir:

Link: </service-worker.js>; rel="serviceworker"; scope="/"

Bu örnek üstbilgiyi, her biri ; karakteriyle ayrılmış bileşenlerine ayıralım.

  • </service-worker.js> zorunludur ve hizmet çalışanı dosyanızın yolunu belirtmek için kullanılır (/service-worker.js değerini komut dosyanızın uygun yoluyla değiştirin). Bu, navigator.serviceWorker.register()'a ilk parametre olarak iletilebilecek scriptURL dizesine doğrudan karşılık gelir. Değerin <> karakterleri içine alınması gerekir (Link başlık spesifikasyonu uyarınca). Mutlak URL yerine göreli URL sağlanırsa bu URL, yanıtın konumuna göreli olarak yorumlanır.
  • rel="serviceworker" da gereklidir ve özelleştirmeye gerek kalmadan eklenmelidir.
  • scope=/, navigator.serviceWorker.register() işlevinin ikinci parametresi olarak iletebileceğiniz options.scope dizesine eşdeğer olan isteğe bağlı bir kapsam beyanı. Birçok kullanım alanında varsayılan kapsamı kullanabilirsiniz. Bu nedenle, ihtiyacınız olmadığını bildiğiniz sürece bu kapsamı eklemeyebilirsiniz. İzin verilen maksimum kapsamla ilgili aynı kısıtlamalar ve Service-Worker-Allowed başlığı aracılığıyla bu kısıtlamaları gevşetme olanağı Link başlık kayıtları için de geçerlidir.

"Geleneksel" bir hizmet çalışanı kaydında olduğu gibi, Link üstbilgisinin kullanılması, kayıtlı kapsama karşı yapılan sonraki istek için kullanılacak bir hizmet çalışanı yükler. Özel başlığı içeren yanıtın gövdesi olduğu gibi kullanılır ve yabancı servis çalışanının kurulumu tamamlamasını beklemeden sayfa tarafından hemen kullanılabilir.

Yabancı getirmenin şu anda Kaynak Deneme olarak uygulandığını unutmayın. Bu nedenle, Bağlantı yanıt üstbilginizin yanı sıra geçerli bir Origin-Trial üstbilgisi de eklemeniz gerekir. Yabancı getirme hizmeti işleyicinizi kaydettirmek için eklenecek minimum yanıt başlığı grubu:

Link: </service-worker.js>; rel="serviceworker"
Origin-Trial: token_obtained_from_signup

Kayıt işleminde hata ayıklama

Geliştirme sırasında, yabancı getirme hizmet işçinizin düzgün şekilde yüklenip istekleri işlediğini doğrulamak isteyebilirsiniz. İşlemlerin beklendiği gibi çalıştığını onaylamak için Chrome'un Geliştirici Araçları'nda kontrol edebileceğiniz birkaç şey vardır.

Doğru yanıt üstbilgileri gönderiliyor mu?

Yabancı getirme hizmet işçisini kaydetmek için bu gönderinin önceki bölümlerinde açıklandığı gibi, alanınızda barındırılan bir kaynağa verilen yanıtta bir Bağlantı üstbilgisi ayarlamanız gerekir. Kaynak deneme dönemi sırasında, chrome://flags/#enable-experimental-web-platform-features ayarlanmamışsa Origin-Trial yanıt üst bilgisini de ayarlamanız gerekir. Web sunucunuzun bu üstbilgileri ayarladığını, DevTools'un Ağ panelindeki girişe bakarak doğrulayabilirsiniz:

Ağ panelinde gösterilen üstbilgiler.

Foreign Fetch hizmet çalışanı düzgün şekilde kayıtlı mı?

Ayrıca, DevTools'un Uygulama panelinde hizmet işçilerinin tam listesine bakarak kapsamı da dahil olmak üzere temel hizmet işçisi kaydını onaylayabilirsiniz. Varsayılan olarak yalnızca geçerli kaynak için hizmet işçilerini göreceğinizden "Tümünü göster" seçeneğini belirlediğinizden emin olun.

Uygulamalar panelindeki yabancı getirme hizmet çalışanı.

Yükleme etkinliği işleyicisi

Üçüncü taraf hizmet işleyicinizi kaydettiğiniz için bu işleyici, diğer hizmet işleyiciler gibi install ve activate etkinliklerine yanıt verme şansı elde eder. Örneğin, install etkinliği sırasında önbellekleri gerekli kaynaklarla doldurmak veya activate etkinliğinde güncel olmayan önbellekleri kaldırmak için bu etkinliklerden yararlanabilir.

Normal install etkinlik önbelleğe alma etkinliklerinin yanı sıra, üçüncü taraf hizmet çalışanınızın install etkinlik işleyicisinde gerekilen ek bir adım vardır. Kodunuzun, aşağıdaki örnekte olduğu gibi registerForeignFetch()'yi çağırması gerekir:

self.addEventListener('install', event => {
    event.registerForeignFetch({
    scopes: [self.registration.scope], // or some sub-scope
    origins: ['*'] // or ['https://example.com']
    });
});

İki yapılandırma seçeneği vardır ve her ikisi de gereklidir:

  • scopes, her biri bir foreignfetch etkinliğini tetikleyecek isteklerin kapsamını temsil eden bir veya daha fazla dize dizisi alır. Ama bekleyin, hizmet çalışanı kaydı sırasında zaten bir kapsam tanımladım diye düşünebilirsiniz. Bu doğrudur ve genel kapsam hâlâ geçerlidir. Burada belirttiğiniz her kapsam, hizmet çalışanının genel kapsamına eşit veya bu kapsamın alt kapsamı olmalıdır. Buradaki ek kapsam kısıtlamaları, hem birinci taraf fetch etkinliklerini (kendi sitenizden yapılan istekler için) hem de üçüncü taraf foreignfetch etkinliklerini (diğer alanlardan yapılan istekler için) işleyebilecek çok amaçlı bir hizmet çalışanı dağıtmanıza olanak tanır ve foreignfetch'ın yalnızca daha geniş kapsamınızın bir alt kümesini tetiklemesi gerektiğini açıkça belirtir. Uygulamada, yalnızca üçüncü taraf foreignfetch etkinliklerini işlemeye özel bir hizmet çalışanı dağıtıyorsanız hizmet çalışanınızın genel kapsamına eşit tek bir açık kapsam kullanmak istersiniz. Yukarıdaki örnekte, self.registration.scope değeri kullanılarak bu işlem yapılır.
  • origins, bir veya daha fazla dize dizisi de alır ve foreignfetch işleyicinizi yalnızca belirli alanlardan gelen isteklere yanıt verecek şekilde kısıtlamanıza olanak tanır. Örneğin, "https://example.com" adresine açıkça izin verirseniz https://example.com/path/to/page.html adresinde barındırılan bir sayfadan yabancı getirme kapsamınızdan sunulan bir kaynak için yapılan istek, yabancı getirme işleyicinizi tetikler ancak https://random-domain.com/path/to/page.html adresinden yapılan istekler işleyicinizi tetiklemez. Yabancı getirme mantığınızı yalnızca uzak kaynakların bir alt kümesi için tetiklemeniz için belirli bir nedeniniz yoksa dizideki tek değer olarak '*''ü belirtmeniz yeterlidir. Bu durumda tüm kaynaklara izin verilir.

foreignfetch etkinlik işleyicisi

Üçüncü taraf hizmet işleyicinizi yüklediğinize ve registerForeignFetch() aracılığıyla yapılandırdığınıza göre, yabancı getirme kapsamına giren sunucunuza yönelik kaynak alanı çapraz alt kaynak isteklerini durdurma şansı elde eder.

Geleneksel, birinci taraf hizmet işçisinde her istek, hizmet işçinizin yanıtlama şansı bulduğu bir fetch etkinliği tetikler. Üçüncü taraf hizmet işleyicimize, foreignfetch adlı biraz farklı bir etkinliği işleme şansı verilir. Kavramsal olarak bu iki etkinlik oldukça benzerdir ve size gelen isteği inceleme ve isteğe isteğe bağlı olarak respondWith() üzerinden yanıt verme fırsatı sunar:

self.addEventListener('foreignfetch', event => {
    // Assume that requestLogic() is a custom function that takes
    // a Request and returns a Promise which resolves with a Response.
    event.respondWith(
    requestLogic(event.request).then(response => {
        return {
        response: response,
        // Omit to origin to return an opaque response.
        // With this set, the client will receive a CORS response.
        origin: event.origin,
        // Omit headers unless you need additional header filtering.
        // With this set, only Content-Type will be exposed.
        headers: ['Content-Type']
        };
    })
    );
});

Kavramsal benzerliklere rağmen, ForeignFetchEvent üzerinde respondWith() çağrısı yaparken pratikte birkaç fark vardır. FetchEvent ile yaptığınız gibi respondWith() için yalnızca bir Response (veya Response ile çözüldüğünde Promise olan Promise) sağlamak yerine, ForeignFetchEvent'nin respondWith() özelliğine belirli özelliklere sahip bir Nesne ile çözüldüğünde Promise olan bir Promise göndermeniz gerekir:

  • response gereklidir ve isteği yapan istemciye döndürülecek Response nesnesine ayarlanmalıdır. Geçerli bir Response dışında bir şey sağlarsanız istemcinin isteği bir ağ hatasıyla sonlandırılır. fetch etkinlik işleyicisinde respondWith()'i çağırırken aksine, burada Response ile çözülen bir Promise değil, bir Response sağlamanız gerekir. Yanıtınızı bir söz zinciri aracılığıyla oluşturabilir ve bu zinciri foreignfetch'nin respondWith() parametresi olarak iletebilirsiniz. Ancak zincir, response özelliği Response nesnesine ayarlanmış bir nesne içeren bir nesneyle çözülmelidir. Bunun bir gösterimini yukarıdaki kod örneğinde görebilirsiniz.
  • origin isteğe bağlıdır ve döndürülen yanıtın saydam olup olmadığını belirlemek için kullanılır. Bu özelliği eklemezseniz yanıt opak olur ve istemcinin yanıtın gövdesine ve üstbilgilerine erişimi sınırlı olur. İstek mode: 'cors' ile gönderildiyse opak bir yanıt döndürülmesi hata olarak değerlendirilir. Ancak uzak istemcinin kaynağına eşit bir dize değeri belirtirseniz (event.origin aracılığıyla elde edilebilir) istemciye CORS özellikli bir yanıt sağlamak için açıkça etkinleştirme yaparsınız.
  • headers da isteğe bağlıdır ve yalnızca origin'yi de belirtiyor ve bir CORS yanıtı döndürüyorsanız yararlıdır. Varsayılan olarak, yanıtınıza yalnızca CORS güvenli listelenmiş yanıt üstbilgi listesindeki başlıklar dahil edilir. Döndürülenleri daha fazla filtrelemeniz gerekiyorsa headers, bir veya daha fazla üstbilgi adının listesini alır ve bu listeyi yanıtta gösterilecek üstbilgilerin izin verilenler listesi olarak kullanır. Bu sayede, hassas olabilecek yanıt başlıklarının doğrudan uzak istemciye gösterilmesini önlerken CORS'u etkinleştirebilirsiniz.

foreignfetch işleyicisi çalıştırıldığında hizmet çalışanını barındıran kaynağın tüm kimlik bilgilerine ve çevresel yetkisine erişebildiğini unutmayın. Getirme işlemini yabancı bir hizmet çalışanına dağıtan bir geliştirici olarak, bu kimlik bilgileri sayesinde erişilemeyecek ayrıcalıklı yanıt verilerini sızdırmamanız sizin sorumluluğunuzdadır. CORS yanıtları için etkinleştirme gerektirmek, yanlışlıkla maruz kalmayı sınırlamak için atılacak adımlardan biridir. Ancak geliştirici olarak, foreignfetch işleyicinizde fetch() isteklerini açıkça göndererek belirtilen kimlik bilgilerini kullanmayabilirsiniz:

self.addEventListener('foreignfetch', event => {
    // The new Request will have credentials omitted by default.
    const noCredentialsRequest = new Request(event.request.url);
    event.respondWith(
    // Replace with your own request logic as appropriate.
    fetch(noCredentialsRequest)
        .catch(() => caches.match(noCredentialsRequest))
        .then(response => ({response}))
    );
});

Müşterilerle ilgili dikkat edilmesi gereken noktalar

Yabancı getirme hizmet işçinizin, hizmetinizin istemcilerinden gelen istekleri nasıl işlediğini etkileyen bazı ek hususlar vardır.

Kendi birinci taraf hizmet işçisine sahip müşteriler

Hizmetinizin bazı istemcilerinin, web uygulamalarından gelen istekleri işleyen kendi birinci taraf hizmet işçileri olabilir. Bu, üçüncü taraf yabancı getirme hizmet işçiniz için ne anlama gelir?

İsteği kapsayan bir kapsamda foreignfetch etkinleştirilmiş bir üçüncü taraf hizmet çalışanı olsa bile, birinci taraf hizmet çalışanındaki fetch işleyiciler, web uygulaması tarafından yapılan tüm isteklere yanıt verme konusunda ilk fırsatı elde eder. Ancak birinci taraf servis işçileri olan istemciler, yabancı getirme servis işçinizden yararlanmaya devam edebilir.

Birinci taraf hizmet çalışanının içinde, kaynak ötesi kaynakları almak için fetch() kullanılması, uygun yabancı getirme hizmet çalışanını tetikler. Bu, aşağıdaki gibi kodların foreignfetch işleyicinizden yararlanabileceği anlamına gelir:

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    // If event.request is under your foreign fetch service worker's
    // scope, this will trigger your foreignfetch handler.
    event.respondWith(fetch(event.request));
});

Benzer şekilde, birinci taraf getirme işleyicileri varsa ancak kaynaklarınızın kaynak farklılığı olan isteklerini işlerken event.respondWith() çağrılmıyorsa istek otomatik olarak foreignfetch işleyicinize "geçer":

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    if (event.request.mode === 'same-origin') {
    event.respondWith(localRequestLogic(event.request));
    }

    // Since event.respondWith() isn't called for cross-origin requests,
    // any foreignfetch handlers scoped to the request will get a chance
    // to provide a response.
});

Birinci taraf bir fetch işleyicisi event.respondWith()'yi çağırır ancak yabancı getirme kapsamınız kapsamında bir kaynak istemek için fetch()'yi kullanmazsa yabancı getirme hizmet çalışanınız isteği işleme şansı bulamaz.

Kendi hizmet işçisine sahip olmayan müşteriler

Bir üçüncü taraf hizmetine istek gönderen tüm istemciler, hizmet yabancı bir getirme hizmet işçisi dağıttığında kendi hizmet işçilerini kullanmasalar bile bu durumdan yararlanabilir. İstemcilerin, yabancı getirme hizmeti işleyicisini kullanmayı etkinleştirmek için yapması gereken belirli bir işlem yoktur (bu özelliği destekleyen bir tarayıcı kullanıyor olmaları gerekir). Bu, yabancı bir getirme hizmet işçisi dağıtarak özel istek mantığınızın ve paylaşılan önbelleğinizin, hizmetinizin müşterilerinin çoğuna başka bir işlem yapmadan hemen fayda sağlayacağı anlamına gelir.

Tümünü bir araya getirin: Müşterilerin yanıt aradığı yerler

Yukarıdaki bilgileri dikkate alarak, istemcinin kaynaktan kaynakta istek için yanıt bulmak üzere kullanacağı bir kaynak hiyerarşisi oluşturabiliriz.

  1. Birinci taraf hizmet çalışanının fetch işleyicisi (varsa)
  2. Üçüncü taraf hizmet çalışanının foreignfetch işleyicisi (varsa ve yalnızca kaynakta çapraz isteklerde)
  3. Tarayıcının HTTP önbelleği (taze bir yanıt varsa)

Tarayıcı en üstten başlar ve hizmet çalışanı uygulamasına bağlı olarak, yanıtın kaynağını bulana kadar listede aşağı doğru devam eder.

Daha fazla bilgi

En son gelişmeleri kaçırmayın

Chrome'un yabancı getirme kaynak denemesi uygulaması, geliştiricilerden gelen geri bildirimleri ele aldığımız için değişebilir. Satır içi değişikliklerle bu yayını güncel tutacağız ve yapılan değişiklikleri aşağıda belirteceğiz. Önemli değişikliklerle ilgili bilgileri @chromiumdev Twitter hesabı üzerinden de paylaşacağız.