1
votes

I have implemented service worker in my web app and attempted to cache all html, css, js, and images file. My hosting service is firebase, after a successful deployment I tested if the service will work and the files will be cached, unfortunately the following error will do occur.

Uncaught (in promise) TypeError: Request failed

service-worker.js

let cacheName = 'my-tools-v1';
let filesToCache = [
    '/',
    '/css/app.css',
    '/css/diffview.css',
    '/css/json-lint.css',
    '/css/materialize.css',
    '/images/favicon.png',
    '/images/icons/apple-touch-icon.png',
    '/images/icons/apple-touch-icon-57x57.png',
    '/images/icons/apple-touch-icon-72x72.png',
    '/images/icons/apple-touch-icon-76x76.png',
    '/images/icons/apple-touch-icon-114x114.png',
    '/images/icons/apple-touch-icon-120x120.png',
    '/images/icons/apple-touch-icon-144x144.png',
    '/images/icons/apple-touch-icon-152x152.png',
    '/images/icons/apple-touch-icon-180x180.png',
    '/images/icons/icon-72x72.png',
    '/images/icons/icon-96x96.png',
    '/images/icons/icon-128x128.png',
    '/images/icons/icon-144x144.png',
    '/images/icons/icon-152x152.png',
    '/images/icons/icon-192x192.png',
    '/images/icons/icon-384x384.png',
    '/images/icons/icon-512x512.png',
    '/js/index.js',
    '/js/css-lint.js',
    '/js/difflib.js',
    '/js/diffview.js',
    '/js/ipsum-generator.js',
    '/js/json2.js',
    '/js/json-lint.js',
    '/js/jsonlint.js',
    '/js/lorem-ipsum.js',
    '/js/materialize.js',
    '/js/visual-difference.js',
    '/bower_components/codemirror/lib/codemirror.js',
    '/bower_components/codemirror/mode/javascript/javascript.js',
    '/bower_components/codemirror/mode/css/css.js',
    '/bower_components/codemirror/addon/edit/matchbrackets.js',
    '/bower_components/codemirror/addon/comment/continuecomment.js',
    '/bower_components/codemirror/addon/comment/comment.js',
    '/bower_components/ft-csslint/dist/csslint.js',
    '/bower_components/jquery/dist/jquery.slim.min.js',
    '/bower_components/kanye-ipsum/dist/jquery.kanye-ipsum.min.js',
    '/bower_components/codemirror/lib/codemirror.css',
    '/index.html',
    '/css-lint.html',
    '/ipsum-generator.html',
    '/json-lint.html',
    '/visual-difference.html',
    '/notfound.html',
    '/404.html'
];

self.addEventListener('install', (e) => {
    console.log('[ServiceWorker] Install');

    e.waitUntil(
        caches.open(cacheName).then((cache) => {
            console.log('[ServiceWorker] Caching app shell');
            return cache.addAll(filesToCache);
        })
    );
});

self.addEventListener('activate', (e) => {
    console.log('[ServiceWorker] Activate');

    e.waitUntil(
        caches.keys().then(function(keyList) {
            return Promise.all(keyList.map((key) => {
                if (key !== cacheName) {
                    console.log('[ServiceWorker] Removing old cache', key);
                    return caches.delete(key);
                }
            }));
        })
    );
});

self.addEventListener('fetch', (e) => {
    console.log('[ServiceWorker] Fetch', e.request.url);

    e.respondWith(
        caches.match(e.request).then((response) => {
            return response || fetch(e.request);
        })
    );
});

Then all files were not cached because of this. But if I remove the 404.html or rename it to other name the service worker will work fine and all files will be cached. It is also weird that in my local server the service worker works and caches 404.html but it fails in firebase.

Why 404.html causes uncaught error during service worker caching? How do I resolve this?

2

2 Answers

0
votes

Cache.addAll() is an all or nothing API. If any response is not in the 200 HTTP status code range, nothing will be cached.

cache.addAll will reject if any of the resources fail to cache. This means the service worker will only install if all of the resources in cache.addAll have been cached.

Firebase returns 404 Not Found for the /404.html file.

An approach to resolve this is to have a file /notfound.html like you have and then return that in fetch when needed.

0
votes

You cannot add 404 error html page into cache, if it returns 404 https status (as it should).

But still you can create 404 error page with service worker and use it for sites not in cache even if website is in offline mode.

Using catch() after fetch() request and create whole new Response

minimalistic example:

// self is ServiceWorkerGlobalScope
self.addEventListener( 'fetch', function ( /** @type {FetchEvent} */ event )
{

    //const caches = ( event.target.caches );

    event.respondWith(
        caches.match( event.request ).then( ( /** @type {Response | undefined} */ response ) =>
        {

            if ( response ) {
                return response;
            }

            return fetch( event.request.clone() ).then( ( /** @type {Response} */ response ) =>
            {

                //…

                return response;
            }
            ).catch( () =>
            {
                return new Response( '<h1>404 - Not found</h1><p>more HTML here …</p>', {
                    status: 404,
                    statusText: 'Not found',
                    headers: new Headers( {
                        'Content-Type': 'text/html'
                    } )
                } );

            } );

        }
        )
    );

} );