- ABP Framework version: v8.0.1
- UI Type: Blazor WASM
- Database System: EF Core (SQL Server)
Hi,
I am developing a Blazor Wasm application with offline capabilities. When application switch to offline mode, I want to handle the API calls like fetch openid-configuration, application-configuration, and call the identity server etc .currently i am using below service-worker code but am getting 403 error after login. Have you ever encountered this issue, or is there any alternative solution for calling the Api?
self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [/\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/];
const offlineAssetsExclude = [/^service-worker\.js$/];
async function onInstall(event) {
console.info('Service worker: Install');
self.skipWaiting();
// 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 => createRequest(asset));
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}
function createRequest(asset) {
try {
return new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' });
} catch (error) {
console.error(`Error creating request for ${asset.url} with integrity ${asset.hash}:`, error);
return new Request(asset.url, { cache: 'no-cache' });
}
}
async function onActivate(event) {
console.info('Service worker: Activate');
// Delete unused caches
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys
.filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
.map(key => caches.delete(key)));
}
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
if (event.request.mode === 'navigate' &&
(event.request.url.includes('/connect/') ||
event.request.url.includes('/authentication/') ||
event.request.url.includes('well-known/') ||
event.request.url.includes('/api/abp/'))) {
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(event.request);
if (cachedResponse) {
return cachedResponse;
}
else {
const networkResponse = await fetch(event.request);
if (networkResponse.ok) {
cache.put(event.request, networkResponse.clone());
}
return networkResponse;
}
}
const shouldServeIndexHtml = event.request.mode === 'navigate';
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request).catch(error => console.log(`error on fetch ${error}`));
}
11 Answer(s)
-
0
hi
We don't have any experience with this case. : (
-
0
can you please confirm that do we need to do any changes in service worker, or its fine ?
-
0
hi
I will ask our colleagues.
-
0
-
0
-
0
-
0
-
0
Do you have any unresolved problems?
-
0
Hi, Could you please verify if the service worker code below is fine?
self.importScripts('./service-worker-assets.js'); self.addEventListener('install', event => event.waitUntil(onInstall(event))); self.addEventListener('activate', event => event.waitUntil(onActivate(event))); self.addEventListener('fetch', event => event.respondWith(onFetch(event))); const cacheNamePrefix = 'offline-cache-'; const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; const offlineAssetsInclude = [/\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/]; const offlineAssetsExclude = [/^service-worker\.js$/]; // Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'. const base = "/"; const baseUrl = new URL(base, self.origin); const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href); async function onInstall(event) { console.info('Service worker: Install'); await onActivate(event); self.skipWaiting(); // 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 => createRequest(asset)); assetsRequests.push(new Request('https://cdn.quilljs.com/1.3.7/quill.snow.css')); assetsRequests.push(new Request('https://cdn.quilljs.com/1.3.7/quill.js')); await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)).catch(error => console.log(`error on add to cache ${error}`)); } function createRequest(asset) { try { return new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }); } catch (error) { console.error(`Error creating request for ${asset.url} with integrity ${asset.hash}:`, error); return new Request(asset.url, { cache: 'no-cache' }); } } async function onActivate(event) { console.info('Service worker: Activate'); // Delete unused caches const cacheKeys = await caches.keys(); await Promise.all(cacheKeys .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) .map(key => caches.delete(key))); } async function onFetch(event) { let cachedResponse = null; if (event.request.method === 'GET') { if (((event.request.mode === 'cors') && (event.request.url.includes('/api/abp/application-') || event.request.url.includes('well-known/') || event.request.url.includes('hotreload') || event.request.url.includes('api-definition'))) || (event.request.mode === 'no-cors' && event.request.url.includes('profile-picture-file'))) { const cache = await caches.open(cacheName); if (!navigator.onLine) { cachedResponse = await cache.match(event.request); if (!cachedResponse && (event.request.url.includes('?v=') || event.request.url.includes('?_v='))) { cachedResponse = await cache.match(event.request.url.replace(/\?v=.*$/, '').replace(/\?_v=.*$/, '')); } } if (cachedResponse) { return cachedResponse; } else { const networkResponse = await fetch(event.request).catch(error => console.log(`error on fetch ${event.request.url} ${error}`)); if (networkResponse.ok) { cache.put(event.request, networkResponse.clone()); } if (event.request.url.includes('profile-picture-file')) { cache.put(event.request, networkResponse.clone()); } return networkResponse; } } // For all navigation requests, try to serve index.html from cache // If you need some URLs to be server-rendered, edit the following check to exclude those URLs const shouldServeIndexHtml = event.request.mode === 'navigate' && !event.request.url.includes('/Identity/') && !event.request.url.includes('/connect/') && !event.request.url.includes('/authentication/'); const request = shouldServeIndexHtml ? 'index.html' : event.request; const cache = await caches.open(cacheName); cachedResponse = await cache.match(request); if (!cachedResponse && (event.request.url.includes('?v=') || event.request.url.includes('?_v='))) { cachedResponse = await cache.match(event.request.url.replace(/\?v=.*$/, '').replace(/\?_v=.*$/, '')); } } return cachedResponse || fetch(event.request).catch(error => console.log(`error on fetch ${event.request.url} ${error}`)); }
-
0
I have resolved most of the issues, but putting some conditions in service worker.js file as that was not there in default file that was provided by ABP solution, can you please confirm the code that I have written so far, thanks
-
0
I have resolved most of the issues, but putting some conditions in service worker.js file as that was not there in default file that was provided by ABP solution, can you please confirm the code that I have written so far, thanks
I haven't tried your code yet, but it seems all right and covers most scenarios including showing profile images. You can customize this file to your needs without the need to worry.