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ı, örneğin üçüncü taraf bir API'ye veya web yazı tiplerine yönelik olarak kaynaklar arası istekleri bile yönetmekten sorumludur.

Bir API'nin, web yazı tiplerinin veya yaygın olarak kullanılan diğer bir hizmetin üçü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ğ iletişimi mantığını uygulayabilir ve yanıtlarını depolamak için tek ve yetkili bir önbellek örneğinden yararlanabilir. Yabancı getirme sayesinde bu tür üçüncü taraf hizmet çalışanı dağıtımı artık mümkün.

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ı getirme hizmeti çalışanı dağıtarak, bir kullanıcı çevrimdışıyken hizmetinize başarısız olan tüm isteklerin sıraya alınmasını ve bağlantı tekrar kurulduğunda tekrar 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

Dış 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 noktada, özelliği kararlı hale getirmek için gerekli değişiklikleri tespit edip varsayılan olarak etkinleştirmeyi umuyoruz. 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 bir kaynak denemesi jetonuna kaydolmadan önce yabancı getirmeyle deneme yapmayı kolaylaştırmak amacıyla, chrome://flags/#enable-experimental-web-platform-features adresine gidip "Deneysel Web Platformu özellikleri" işaretini etkinleştirerek yerel bilgisayarınız için Chrome'da bu gereksinimi atlayabilirsiniz. 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 Service Worker'larla birlikte çalıştıysanız muhtemelen şunlar hakkında bilginiz vardır:

// 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 gitmesi tarafından tetiklenen bir web uygulaması bağlamında anlamlıdır. Ancak tarayıcının, sunucunuzla kuracağı tek etkileşim tam gezinme yerine belirli bir alt kaynak isteğinde bulunduğunda bu, üçüncü taraf hizmet çalışanı kaydı için uygun bir yaklaşım 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> gereklidir ve hizmet çalışanı dosyanızın yolunu belirtmek için kullanılır (/service-worker.js yerine komut dosyanızın uygun yolunu yazın). Bu, doğrudan, navigator.serviceWorker.register() parametresine ilk parametre olarak iletilecek scriptURL dizesine 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() öğesine ikinci parametre olarak iletebileceğiniz options.scope dizesine eşdeğer olan isteğe bağlı bir kapsam bildirimidir. 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 bu kısıtlamaları Service-Worker-Allowed başlığı aracılığıyla gevşetme olanağı, Link başlık kayıtları için 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 muhtemelen yabancı getirme hizmet çalışanınızın düzgün şekilde yüklendiğini ve istekleri işlediğini onaylamak isteyebilirsiniz. İşlerin 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 Denemesi süresi boyunca ve chrome://flags/#enable-experimental-web-platform-features politikasını ayarlamadıysanız Origin-Trial yanıt başlığı da 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şleyici

Üçü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 bir de Service Worker 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 tetiklemek üzere belirli bir nedeniniz yoksa dizideki tek değer olarak '*' değerini 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 için respondWith() çağrısı yapılırken pratikte birkaç farklılık vardır. FetchEvent ile yaptığınız gibi respondWith() için yalnızca bir Response (veya Response ile çözünür bir Promise) sağlamak yerine, ForeignFetchEvent'nin respondWith() özelliğine belirli özelliklere sahip bir Nesne ile çözünür 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şleyici içinde respondWith() çağrısının aksine, burada Response ile çözümlenen bir Promise değil, bir Response sağlamanız gerekir. Yanıtınızı bir taahhüt zinciri aracılığıyla oluşturabilir ve bu zinciri, foreignfetch respondWith() öğesine parametre olarak iletebilirsiniz. Ancak zincir, Response nesnesine ayarlanmış response özelliğini içeren bir Nesne ile çözümlenmelidir. 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 opak 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 de isteğe bağlıdır ve yalnızca origin değerini de belirtiyor ve bir CORS yanıtı döndürüyorsanız yararlı olur. Varsayılan olarak, yanıtınıza yalnızca CORS güvenli listeye alınmış yanıt başlığı 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üşterilerin dikkat etmesi 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?

Birinci taraf hizmet çalışanındaki fetch işleyiciler, isteği kapsayan kapsamla foreignfetch etkinleştirilmiş bir üçüncü taraf hizmet çalışanı olsa bile web uygulaması tarafından yapılan tüm isteklere yanıt vermek için ilk fırsatı alır. 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 farklı olan 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şleyiciler varsa ancak bunlar, kaynaklar arası kaynağınız için istekleri işlerken event.respondWith() öğesini çağırmıyorsa istek otomatik olarak foreignfetch işleyicinize iletir:

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

Bir birinci taraf 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

Üçüncü taraf hizmetlerine istekte bulunan tüm müşteriler, halihazırda kendi hizmet çalışanını kullanmıyor olsalar bile, hizmetin yabancı bir getirme hizmet çalışanı dağıttığında avantaj elde edebilir. Destekleyen bir tarayıcı kullandıkları sürece, yabancı bir şekilde getirme hizmet çalışanının kullanılmasını sağlamak için müşterilerin yapması gereken belirli bir işlem yoktur. 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üşteriler yanıtı nerede arar?

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 kaynaklar arası istekler için)
  3. Tarayıcının HTTP önbelleği (yeni 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 bildirimler doğrultusunda 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. Ayrıca, önemli değişikliklerle ilgili bilgileri @chromiumdev Twitter hesabı üzerinden paylaşacağız.