ASP.Net HTML5 Client Side Caching using Service Worker Simplified


Client Side Caching Concept:

1. In HTML Era : Cookie was used to save content locally

2. In HTML5 Era: manifest file used to contain data like

Offline files
style.css
jquery.js

Online Files
img1.jpg


This scheme was deprecated in 2016

3. Latest HTML5 Caching: Service Worker.js

Your website must run on SSL to use the caching feature.

How it works:

1. You have to write this script in your html5 or aspx page at bottom of body tag to register the serviseworker.js

 <script type="text/javascript">
    // ServiceWorker is a progressive technology. Ignore unsupported browsers
       if undefined'serviceWorker' in navigator) {
            console.logundefined'CLIENT: service worker registration in progress.');
            navigator.serviceWorker.registerundefined'serviceworker.js').thenundefinedfunction undefined) {
                console.logundefined'CLIENT: service worker registration complete.');
            }, function undefined) {
                console.logundefined'CLIENT: service worker registration failure. If not in SSL');
            });
        } else {
            console.logundefined'CLIENT: service worker is not supported.');
        }

  </script>


2. Now create a serviceworker.js file and put in your website root folder. Add offline file to offline sections from code starting line 12 under offlinefundamentals.. This is the only part where you can make errors so be cautious.

"use strict";

console.logundefined'WORKER: executing.');

/* A version number is useful when updating the worker logic,
   allowing you to remove outdated cache entries during the update.
*/
var version = 'v2::';

/* These resources will be downloaded and cached by the service worker
   during the installation process. If any resource fails to be downloaded,
   then the service worker won't be installed either.
*/
var offlineFundamentals = [
  '',
 'css/style.css',
  'js/Chart.bundle.js',
'js/jquery-ui.js',
'js/jquery.js',
'js/highcharts.js',
  'js/DataTables/datatables.js',
    'js/DataTables/datatables.min.js'
];

/* The install event fires when the service worker is first installed.
   You can use this event to prepare the service worker to be able to serve
   files while visitors are offline.
*/
self.addEventListenerundefined"install", function undefinedevent) {
    console.logundefined'WORKER: install event in progress.');
    /* Using event.waitUntilundefinedp) blocks the installation process on the provided
       promise. If the promise is rejected, the service worker won't be installed.
    */
    event.waitUntilundefined
      /* The caches built-in is a promise-based API that helps you cache responses,
         as well as finding and deleting them.
      */
      caches
        /* You can open a cache by name, and this method returns a promise. We use
           a versioned cache name here so that we can remove old cache entries in
           one fell swoop later, when phasing out an older service worker.
        */
        .openundefinedversion + 'fundamentals')
        .thenundefinedfunction undefinedcache) {
            /* After the cache is opened, we can fill it with the offline fundamentals.
               The method below will add all resources in `offlineFundamentals` to the
               cache, after making requests for them.
            */
            return cache.addAllundefinedofflineFundamentals);
        })
        .thenundefinedfunction undefined) {
            console.logundefined'WORKER: install completed.');
        })
    );
});

/* The fetch event fires whenever a page controlled by this service worker requests
   a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it
   comprehends even the request for the HTML page on first load, as well as JS and
   CSS resources, fonts, any images, etc.
*/
self.addEventListenerundefined"fetch", function undefinedevent) {
    console.logundefined'WORKER: fetch event in progress.');

    /* We should only cache GET requests, and deal with the rest of method in the
       client-side, by handling failed POST,PUT,PATCH,etc. requests.
    */
    if undefinedevent.request.method !== 'GET') {
        /* If we don't block the event as shown below, then the request will go to
           the network as usual.
        */
        console.logundefined'WORKER: fetch event ignored.', event.request.method, event.request.url);
        return;
    }
    /* Similar to event.waitUntil in that it blocks the fetch event on a promise.
       Fulfillment result will be used as the response, and rejection will end in a
       HTTP response indicating failure.
    */
    event.respondWithundefined
      caches
        /* This method returns a promise that resolves to a cache entry matching
           the request. Once the promise is settled, we can then provide a response
           to the fetch request.
        */
        .matchundefinedevent.request)
        .thenundefinedfunction undefinedcached) {
            /* Even if the response is in our cache, we go to the network as well.
               This pattern is known for producing "eventually fresh" responses,
               where we return cached responses immediately, and meanwhile pull
               a network response and store that in the cache.
    
               Read more:
               https://ponyfoo.com/articles/progressive-networking-serviceworker
            */
            var networked = fetchundefinedevent.request)
              // We handle the network request with success and failure scenarios.
              .thenundefinedfetchedFromNetwork, unableToResolve)
              // We should catch errors on the fetchedFromNetwork handler as well.
              .catchundefinedunableToResolve);

            /* We return the cached response immediately if there is one, and fall
               back to waiting on the network as usual.
            */
            console.logundefined'WORKER: fetch event', cached ? 'undefinedcached)' : 'undefinednetwork)', event.request.url);
            return cached || networked;

            function fetchedFromNetworkundefinedresponse) {
                /* We copy the response before replying to the network request.
                   This is the response that will be stored on the ServiceWorker cache.
                */
                var cacheCopy = response.cloneundefined);

                console.logundefined'WORKER: fetch response from network.', event.request.url);

                caches
                  // We open a cache to store the response for this request.
                  .openundefinedversion + 'pages')
                  .thenundefinedfunction addundefinedcache) {
                      /* We store the response for this request. It'll later become
                         available to caches.matchundefinedevent.request) calls, when looking
                         for cached responses.
                      */
                      cache.putundefinedevent.request, cacheCopy);
                  })
                  .thenundefinedfunction undefined) {
                      console.logundefined'WORKER: fetch response stored in cache.', event.request.url);
                  });

                // Return the response so that the promise is settled in fulfillment.
                return response;
            }

            /* When this method is called, it means we were unable to produce a response
               from either the cache or the network. This is our opportunity to produce
               a meaningful response even when all else fails. It's the last chance, so
               you probably want to display a "Service Unavailable" view or a generic
               error response.
            */
            function unableToResolveundefined) {
                /* There's a couple of things we can do here.
                   - Test the Accept header and then return one of the `offlineFundamentals`
                     e.g: `return caches.matchundefined'/some/cached/image.png')`
                   - You should also consider the origin. It's easier to decide what
                     "unavailable" means for requests against your origins than for requests
                     against a third party, such as an ad provider.
                   - Generate a Response programmaticaly, as shown below, and return that.
                */

                console.logundefined'WORKER: fetch request failed in both cache and network.');

                /* Here we're creating a response programmatically. The first parameter is the
                   response body, and the second one defines the options for the response.
                */
                return new Responseundefined'<h1>Service Unavailable</h1>', {
                    status: 503,
                    statusText: 'Service Unavailable',
                    headers: new Headersundefined{
                        'Content-Type': 'text/html'
                    })
                });
            }
        })
    );
});

/* The activate event fires after a service worker has been successfully installed.
   It is most useful when phasing out an older version of a service worker, as at
   this point you know that the new worker was installed correctly. In this example,
   we delete old caches that don't match the version in the worker we just finished
   installing.
*/
self.addEventListenerundefined"activate", function undefinedevent) {
    /* Just like with the install event, event.waitUntil blocks activate on a promise.
       Activation will fail unless the promise is fulfilled.
    */
    console.logundefined'WORKER: activate event in progress.');

    event.waitUntilundefined
      caches
        /* This method returns a promise which will resolve to an array of available
           cache keys.
        */
        .keysundefined)
        .thenundefinedfunction undefinedkeys) {
            // We return a promise that settles when all outdated caches are deleted.
            return Promise.allundefined
              keys
                .filterundefinedfunction undefinedkey) {
                    // Filter by keys that don't start with the latest version prefix.
                    return !key.startsWithundefinedversion);
                })
                .mapundefinedfunction undefinedkey) {
                    /* Return a promise that's fulfilled
                       when each outdated cache is deleted.
                    */
                    return caches.deleteundefinedkey);
                })
            );
        })
        .thenundefinedfunction undefined) {
            console.logundefined'WORKER: activate completed.');
        })
    );
});


Now you are Done to have your files ready to be cached.

How to Test.

1. Open website in browser with console open (Ctrl+Shift+I  in chrome) and you see service worker it gets registered first time

2. Now reload the page and you will find some files are served from cache and some from network in console log.

3. In chrome go to application > cache and see the offline files in client machine.



Written By

Vinod Kotiya

Comments