1

I'm trying to setup PWA for my blazor application. I followed the instructions on: https://learn.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app?view=aspnetcore-6.0&tabs=visual-studio

But when I open the deployed website the following error occurs:

Failed to find a valid digest in the 'integrity' attribute for resource 'domain/manifest.json' with computed SHA-256 integrity 'uDWnAIEnaz9hFx7aEpJJVS1a+QB/W7fMELDfHWSOFkQ='. The resource has been blocked.
Unknown error occurred while trying to verify integrity.
service-worker.js:22 
    
   Uncaught (in promise) TypeError: Failed to fetch
at service-worker.js:22:54
at async onInstall (service-worker.js:22:5)

In the source file this happens here:

async function onInstall(event) {
    console.info('Service worker: Install');

    // Fetch and cache all matching items from the assets manifest
    const assetsRequests = self.assetsManifest.assets
        .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
        .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
        .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
    await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));

}

I think the error is happening since the entry in assetsRequests has a wrong hash and the resource is blocked. If I remove the file from the service-worker-assets.js, the service worker installs and the PWA can be used. But I think this is not a reliable solution.

This also happens sometimes for the appsettings.json. In the service-worker-assets.js I can find the following entry:

{
  "hash": "sha256-+Py0\/ezc+0k1sm\/aruGPrVhS1jOCTfPKMhOSS+bunek=",
  "url": "manifest.json"
},

So the hash does not seem to match. Where does the browser take the wrong hash from? How can I fix this so it does match?

Also it seems that the app is caching older files sometimes. Even when I do a "Reset Cache & Hard Reload" in Chrome the service-worker.js file is still an older version. Any idea how to fix this as well, since it might be related?

Edit: I was also checking this solution: https://stackoverflow.com/a/69935118/11385442. But in the mentioned blazor.boot.json I cannot find any reference to the manifest.json or the appsettings.json. Only Dlls are listed. So the problem only seems to relate to files not listed in blazor.boot.json.

Edit2: What I can see on the webserver is that the following files are published:

appsettings.json
appsettings.json.br
appsettings.json.gzip

So it seems like compressed version are added. Also the appsettings.json has a different size than the one in the solution. My guess is that somewhere in the build or release pipeline (Azure) the files are modified. But even when I copy the appsettings.json manually to the webserver the error still occurs. I was following Information provided here: https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-5.0 (Diagnosing integrity problems)

Snaketec
  • 471
  • 2
  • 14
  • 1
    I just fix a PWA cache Blazor issue in my own project. I follow this tuto: https://whuysentruit.medium.com/blazor-wasm-pwa-adding-a-new-update-available-notification-d9f65c4ad13 And in addition, I had to do a small modification: https://github.com/dotnet/aspnetcore/issues/39016#issuecomment-998604247 It seems working correctly now :) – Dylan El Bar Jul 21 '22 at 09:48
  • Do you know what was actually the fix or the difference to your first implementation? I tried {updateViaCache: 'none'} as well but now it's giving me the same error for the index.html file. – Snaketec Jul 21 '22 at 10:44
  • @DylanBarquilla I followed the same tutorial and made the small modification. Now I can test it locally. It seems to work only after I rebuild the solution when I made a change to a file. It looks like the hashs are not updated correctly somehow. – Snaketec Jul 21 '22 at 11:21
  • If you've followed the same tuto as me, you should have a file `sw-registrator.js`. Inside of it, I replace `navigator.serviceWorker.register('/service-worker.js')[...]` by `navigator.serviceWorker.register('/service-worker.js', { updateViaCache: 'none' })[...]`. I think it forces the serviceWorker to try to update via the server and never with the cache, wich seems good. Is it possible for to put somewhere all the sources, so i can check maybe? (at least index.html, sw-registrator.js and service-worker.js (2) ) – Dylan El Bar Jul 21 '22 at 11:41
  • I already did that, and the code is exactly the same. Also the sw-registrator is working, but the same integrity error occurs in onInstall (Since this is the same code as before). I guess it might have something to do how our build server is building and deploying the assemblies. Thanks for your help so far! – Snaketec Jul 21 '22 at 12:18

1 Answers1

0

My guess was right. The appsettings.json was modified probably due to the xml transformation in the azure pipeline. My current solution is to exclude integrity validation for such resources as described in the following answer: Error loading appsettings.Production.json due to digest integrity issue

Also I changed the "sw-registrator.js" mentioned in the original posts comments to work correctly, because it didn't load the new files into the cache:

function invokeServiceWorkerUpdateFlow(registration) {
    if (confirm("New version available, reload?") == true) {
        if (registration.waiting) {
            console.info(`Service worker registrator: Post skip_waiting...`);

            // let waiting Service Worker know it should became active
            registration.waiting.postMessage('SKIP_WAITING')
        }
    }
}

function checkServiceWorkerUpdate(registration) {
    setInterval(() => {
        console.info(`Service worker registrator: Checking for update... (scope: ${registration.scope})`);

        registration.update();
    }, 60 * 1000); // 60000ms -> check each minute
}

// check if the browser supports serviceWorker at all
if ('serviceWorker' in navigator) {
    // wait for the page to load
    window.addEventListener('load', async () => {
        // register the service worker from the file specified
        const registration = await navigator.serviceWorker.register('/service-worker.js');

        // ensure the case when the updatefound event was missed is also handled
        // by re-invoking the prompt when there's a waiting Service Worker
        if (registration.waiting) {
            invokeServiceWorkerUpdateFlow(registration);
        }

        // detect Service Worker update available and wait for it to become installed
        registration.addEventListener('updatefound', () => {
            if (registration.installing) {
                // wait until the new Service worker is actually installed (ready to take over)
                registration.installing.addEventListener('statechange', () => {
                    if (registration.waiting) {
                        // if there's an existing controller (previous Service Worker), show the prompt
                        if (navigator.serviceWorker.controller) {
                            invokeServiceWorkerUpdateFlow(registration);
                        } else {
                            // otherwise it's the first install, nothing to do
                            console.log('Service worker registrator: Initialized for the first time.')
                        }
                    }
                });
            }
        });

        checkServiceWorkerUpdate(registration);

        let refreshing = false;

        // detect controller change and refresh the page
        navigator.serviceWorker.addEventListener('controllerchange', () => {
            console.info(`Service worker registrator: Refreshing app... (refreshing: ${refreshing})`);

            if (!refreshing) {
                window.location.reload();
                refreshing = true
            }
        });
    });
}
else
{
    console.error(`Service worker registrator: This browser doesn't support service workers.`);
}

Also I had to add this in service-worker.js:

self.addEventListener('message', (event) => {
    console.info('Service worker: Message received');
    if (event.data === 'SKIP_WAITING') {
        // Cause the service worker to update
        self.skipWaiting();
    }
});

This code was mostly taken from https://whatwebcando.today/articles/handling-service-worker-updates/

Snaketec
  • 471
  • 2
  • 14