BASTA! Blog

BASTA! | 19. - 23. September 2016, Mainz
6
Mai

Offlinefähige Browseranwendungen mit Service Worker

Manfred Steyer

Die bereits von einigen Browsern unterstützten Service Worker erlauben das Abfangen von HTTP-Anfragen. Auf diese Weise lassen sich verschiedene Caching-Strategien für offlinefähige Browseranwendungen umsetzen.

Schon seit längerer Zeit unterstützen alle gängigen Browser Offlineszenarien. Beispielsweise können Webanwendungen über ein so genanntes Cachemanifest alle für den Offlinebetrieb benötigten Dateien angeben. Der Browser lädt diese beim ersten Besuch der Anwendung herunter und hinterlegt sie im so genannten App-Cache.

Leider hat die Anwendung selbst nur wenig Kontrolle über die Nutzung dieses Cache. Beispielsweise lädt der Browser Dateien bevorzugt aus diesem Cache, auch wenn online neuere Versionen davon zur Verfügung stehen. Daneben aktualisiert er den App-Cache nur, wenn er eine Änderung am Cachemanifest entdeckt. Ein gezieltes Aktualisieren einzelner Cacheeinträge ist hingegen nicht möglich.

Service Workers bieten hierfür eine bessere Lösung: Sie haben feingranularen Zugriff auf mehrere frei definierbare Cacheregionen und können Dateien nach eigenem Gutdünken darin verwalten. Darüber hinaus können sie HTTP-Anfragen abfangen und dynamisch entscheiden, ob diese über einen Netzwerkzugriff oder aus dem Cache zu beantworten sind. Auf diese Weise lassen sich unterschiedliche Caching-Strategien implementieren. Das Offline Cookbook von Google [1] geht darauf umfangreich ein. Einen Überblick dazu bietet Tabelle 1. Als der vorliegende Text verfasst wurde, unterstützten Chrome, Firefox und Opera dieses aufstrebende Konzept. Seitens der Teams hinter Safari und Edge gab es Interessensbekundungen.

 

Strategie Beschreibung
Cache-only Ressourcen werden ausschließlich aus dem Cache geladen.
Network-only Ressourcen werden ausschließlich über das Netzwerk geladen.
Cache, falling back to network Cache wird bevorzugt. Falls die gewünschte Ressource dort nicht zu finden ist, wird über das Netzwerk geladen.
Cache and network race Ressource wird parallel aus dem Cache und übers Netzwerk angefordert. Die zuerst erhaltene Antwort kommt zum Einsatz.
Network falling back to cache Netzwerk wird bevorzugt. Falls die gewünschte Ressource nicht übers Netzwerk geladen werden kann, kommt der Cache zum Einsatz.
Cache then network Ressource wird aus dem Cache geladen und verwendet. Parallel dazu wird eine Netzwerkanfrage gestartet. Sobald die darauf folgende Antwort vorliegt, kommt diese zum Einsatz. Darüber hinaus wird damit der Cache aktualisiert.
Generic fallback Wenn die gewünschte Ressource nicht im Cache und/oder nicht übers Netzwerk gefunden wurde, kommt eine generische Antwort zum Einsatz. Dabei kann es sich um eine benutzerfreundliche Fehlermeldung handeln.

Tabelle 1: Ausgewählte Caching-Szenarien

 

 

Service Worker

Ein Service Worker ist zunächst lediglich ein Skript, das der Browser im Hintergrund ausführt und sich für bestimmte Events registriert. Sein Wirkungsbereich erstreckt sich über den Ordner, in dem sich die Skriptdatei befindet, und bezieht auch sämtliche direkte sowie indirekte Unterordner ein. Um einen Service Worker die Kontrolle über eine gesamte Webanwendung zu geben, ist er somit in deren Root zu platzieren.

Mit der Service Worker Toolbox [2] stellt Google eine solche Bibliothek mit einem High-Level-API zur Verfügung. Das Herzstück ist dabei ein Routing-Konzept zum Abfangen von Anfragen. Eine Route definiert sich dabei durch ein URL-Muster, ein HTTP-Verb und einen Handler. Das URL-Muster ist im Wesentlichen ein URL mit Platzhalter. Greift die Anwendung mit dem definierten Verb auf ein zum Muster passenden URL zu, bringt das Service Worker Toolkit den registrierten Handler zur Ausführung. Dieser kann unter Nutzung einer festgelegten Caching-Strategie auf die Anfrage reagieren. Für die meisten der in Tabelle 1 beschriebenen Caching-Strategien bietet das Toolkit auch schon eine fertige Implementierung, sodass sich der Entwickler mit der Umsetzung dieser wohlbekannten Verfahren nicht belasten muss.

Dank des Routings kann eine Anwendung auf einfache Weise Anfragen für unterschiedliche Bereiche mit unterschiedlichen Caching-Strategien begegnen. Somit kann sie zum Beispiel Zugriffe auf statische Dateien anders handhaben als Zugriffe auf Web-APIs. Während sich womöglich im ersten Fall die Strategie Cache, falling back to network anbietet, könnte im zweiten Fall das Gegenstück Network, falling back to cache die bessere Option sein.

Nutzung der Service Worker Toolbox

Ein Beispiel für einen Service Worker, der auf der Toolbox basiert, findet sich in Listing 1. Die gesamten Quellcodedateien stellt der Autor unter [3] zur Verfügung. Das Beispiel importiert zunächst die Bibliothek sw-toolbox.js und versetzt daraufhin die Toolbox in den Debug-Modus. Dies führt zu einer Protokollierung sämtlicher durchgeführter Schritte in der Entwicklerkonsole und ist gerade für das Testen empfehlenswert. Anschließend weist das Beispiel die Toolbox an, im Zuge der Installation des Service Workers einige Dateien in den Cache zu laden. Caches von älteren Versionen desselben Service Workers entfernt es bei der darauffolgenden Aktivierung automatisch.

Danach erfolgt das Einrichten der Routen: Die Methode toolbox.router.get definiert eine Route für GET-Aufrufe, die an einem beliebigen URL der Herkunft http://www.angular.at gerichtet sind. Dabei handelt es sich um das vom Beispiel genutzte Web-API. Als Handler kommt hierfür die Methode toolbox.networkFirst, die die Strategie Network, falling back to cache implementiert.

Zusätzlich hinterlegt das Beispiel in der Eigenschaft toolbox.router.default einen Standard-Handler. Dieser kommt per Definition zur Ausführung, wenn keine der definierten Routen zu einer GET-Anfrage passt. Im betrachteten Fall handelt es sich dabei um die Funktion toolbox.cacheFirst, welche die Strategie Cache, falling back to network implementiert und sämtliche aus dem Netzwerk bezogenen Dateien für weitere Zugriffe im Cache hinterlegt.

Damit der Browser den Service Worker so schnell wie möglich aktiviert, legt das Beispiel am Ende noch Handler für die Ereignisse install und activate fest.

 

importScripts(‚/node_modules/sw-toolbox/sw-toolbox.js‘);

toolbox.options.debug = true;

toolbox.precache([

‚/‘,

‚/app/flugsuchen.component.css‘,

‚/app/flugsuchen.component.html‘,

‚/app/flugsuchen.component.js‘,

‚/app/navbar.component.html‘,

‚/app/navbar.component.js‘,

‚/app/main.js‘

]);

toolbox.router.get(‚/(.*)‘, toolbox.networkFirst,
{origin: ‚http://www.angular.at‘});

toolbox.router.default = toolbox.cacheFirst;

var context: any = self;

context.addEventListener(‚install‘,
event => event.waitUntil(context.skipWaiting()));

context.addEventListener(‚activate‘,
event => event.waitUntil(context.clients.claim()));

 

Handler beim Routing durch swtoolkit

Bei den von den Routen genutzten Handlern handelt es sich um Funktionen, die als ersten Parameter die jeweilige HTTP-Anfrage entgegennehmen und eine dazu passende HTTP-Antwort retournieren. Neben der Anfrage nehmen sie auch noch ein Objekt mit URL-Parametern sowie ein Options-Objekt entgegen. Bei Letzterem handelt es sich um das beim Einrichten der Route hinterlegte Options-Objekt, das unter anderem die Origin festlegt.

Das nachfolgende Beispiel demonstriert dies. Es implementiert die Caching-Strategie Generic Fallback. Dazu definiert es einen URL mit einem Platzhalter imgname. Der zur Laufzeit dafür eingesetzte Wert erscheint innerhalb der gleichnamigen Eigenschaft im an den Handler übergebenen zweiten Parameter mit dem Namen values. Der Handler versucht, die Anfrage mit der Funktion cacheFirst zu beantworten. Funktioniert dies nicht und befindet sich die Anwendung im Offlinebetrieb, leitet der Handler auf die sich im Cache befindliche Datei notfound.png um.

 

declare var Request: any;

toolbox.router.get(‚/img/:imgname‘, (request, values, options) => {

return toolbox.cacheFirst(request)

.catch(() => toolbox.cacheOnly(
new Request(„/img/notfound.png“)));

});

 

Neben den hier betrachteten Methoden bietet das Toolkit noch einige weitere. Beispielsweise wartet es mit zusätzlichen Cachestrategien, aber auch mit Methoden für weitere HTTP-Verben auf. Es kann auch Cacheeinträge nach einer bestimmten Zeitspanne oder beim Erreichen eines Quotas entfernen. Informationen dazu bietet die Dokumentation im GitHub-Repository der Toolbox [2].

Auch zum Registrieren von Service Worker bietet die Toolbox mit dem Skript companion.js eine einfache Möglichkeit:

 

<script src=“node_modules/sw-toolbox/companion.js“
data-service-worker=“sw-with-toolbox.js“></script>

Die Datei companion.js über einen Skript-Tag zu laden und das gewünschte Service-Worker-Skript ist lediglich über das Attribut data-service-worker desselben Skript-Tags zu referenzieren.

 

[1] https://jakearchibald.com/2014/offline-cookbook/#generic-fallback

[2] https://github.com/GoogleChrome/sw-toolbox

[3] https://github.com/manfredsteyer/progressive-with-sw-toolbox-demo

 

Manfred Steyer (www.softwarearchitekt.at) ist Trainer und Berater mit Fokus auf Webtechnologien und Services. In seinem aktuellen Buch „AngularJS: Moderne Webanwendungen und Single Page Applications mit JavaScript“ behandelt er die vielen Seiten des populären JavaScript-Frameworks aus der Feder von Google.

 

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud, Azure, Serverless
Cloud-basierte & Native Apps