Although I see similar questions regarding the subject, there are not the same. So, I have a pwa app that is basically a simple form that users must fill out. The service worker catch the resources and serves to the app, as usual. The strategy is 'cache first, then network'. All is ok, when is onLine, BUT in offLine mode, the cached resources are not used by the app, I mean, in spite that (you can see) in the cache are the resources(fetch requests) that the app needs, it anyway try to fetch to the web and obviously because there is offLine, the fetch fail and the app crash. So, the code lines ...
caches.match(e.request)
.then( res => {
if (res ){
return res;
}
...
is not working. My question....Why???.
I will appreciate you help/comments.
You should add some more context and code, to let others better understand your situation.
Do you serve data from the cache like the following example?
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
Related
I am learning about Service workers, as I have a usecase to create a fetch listener, that will pass back an arbitrary binary response.
I have no idea where to start. The examples I have seen online talk about making a server request, caching in the service worker, and passing it back. What I really want to do is just pass back my own response, not make a query to the server and cache it!
What I am looking for, as a start is, say something that will once the service worker is active, given the user enters in the browser, (or uses fetch api to get the following url)
http://myapp/helloworld
will show 'Helloworld' in the browser. The service worker will be something like the following. But I have not a clue how make it work.
self.addEventListener('fetch', event => {
// compare end of url with /helloworld
// if match, respond with 'helloword', otherwise fetch the response from the server
});
This is just going to be a very brief, broad overview of how I would tackle the problem.
First, I would follow a guide like this:
https://css-tricks.com/add-a-service-worker-to-your-site/
// Listen for request events
self.addEventListener('fetch', function (event) {
// Get the request
let request = event.request;
...
}
Then you'll use this bit of code as a guideline for what you want to do:
event.respondWith(
fetch(request).then(function (response) {
return response;
}).catch(function (error) {
return caches.match(request).then(function (response) {
return response;
});
})
);
With some modifications.
First, you'll want to check if it's a normal non-/helloworld type of request, and if it is, do something like this:
if (normalRequest) {
event.respondWith(
fetch(request).then(function (response) {
return response;
});
} else {
... TODO
}
And in the TODO section, you'll do your helloworld code - it's not clear to me what you want to do with that, so I can't really go into more detail. But this is the overall structure I would use.
I am looking into creating progressive web applications, and I was stumped on how the service worker functions. It is my understanding that the service worker is responsible for caching resources to be used in the event that the user loses connection--but I am unsure how it does this, and can't find any resources to quite answer the question.
The question is pretty broad and thus hard to answer. You were looking for a good resource, so here is one.
In general I think your understanding of service workers is more or less correct, but you're probably not registering the correct callbacks and maybe using the wrong APIs.
A service worker can be responsible for caching (among other things), but it in general just gives you the ability to intercept http calls in a callback: self.addEventListener('fetch', (event) => {}); (this line needs to go into the service worker file and self refers to the service worker's instance). You can then do whatever you want in that callback. One very common action is to use the Cache API to use an explicitly handled cache for data:
self.addEventListener('fetch', (event) => {
event.respondWith(
//this returns the response from any cache and populates the cache with name 'v1' with the request's response from the server on a cache miss
caches.match(event.request).then((resp) => {
return resp || fetch(event.request).then((response) => {
let responseClone = response.clone();
caches.open('v1').then((cache) => {
cache.put(event.request, responseClone);
});
return response;
}).catch(() => {
return caches.match('./sw-test/gallery/myLittleVader.jpg');
})
})
);
});
For all of this, the service worker needs to be registered first, though:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./path-to-service-worker.js')
.then((reg) => {
console.log('Registration succeeded.);
}).catch((error) => {
console.log('Registration failed with ' + error);
});
}
I have learned that the Service Worker functions by creating a local cache in the index file of my project. I then created a service worker file that stores a variable cache_name and cache_urls to store the title of the cache and the urls that I want the service worker to cache. I then bind an event listener to a function which will wait until the cache is opened and then add the urls from the cache_urls to the actual cache.
I know I am still missing some steps, but this has at least intialized a service worker in my file.
I'm building an PWA with limited offline capability, I'm using this code to save page content to dynamic cache every time user visits a new url:
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request)
.then(function(res) {
return caches.open('cache')
.then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
})
})
.catch(function(err) {
console.log( err );
return caches.match(event.request);
})
);
});
This works great, after a page is loaded all of it assets are cached and can be seen in offline mode.
But, I would also like to add the option to automatically cache some of the more important urls when the user comes back online.
I do that by putting the list of urls in the array, loop through it and send a fetch request to each url, so those pages can be cached without user visiting/revisiting the page.
Problem is that when I do that some of the assets on some pages are not cached, for example google map on one page, is there a way to simulate real visit to a page, that gets all of the assets from an url with fetch request?
Fetch code:
function fillDynamicCache(user_id = false) {
let urls = [
'/homepage',
'/someotherpage',
'/thirdpage',
'/...',
];
urls.map((url, id) => (
fetch(url)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
console.log( 'in fetch: ' + url );
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
));
}
self.addEventListener('message', (event) => {
// refresh cache when user comes back online
if (event.data == 'is_online') {
fillDynamicCache();
} else if (event.data == 'is_updated') {
self.skipWaiting();
Typically if you have important assets you want to provide the users, even when they are offline, you should consider an offline first strategy, meaning you prefetch those resources while the service worker is installing.
This way the matching requests will be served from the cache, improving the performance because you skip the relative network calls entirely.
In case the target resources tend to update/change frequently on the server, then you can opt for a stale while revalidate strategy (after the data is provided from the cache, the SW will update its value with a newer one from the network, if available) or even network first, fallback to cache, the latter if you want to provide always the latest values and provide cache data only if the network connection times out or is unavailable.
I wrote an article about service worker and caching strategies, in case you want to go deeper into the topic.
Is it possible to show an offline cache of my website when the server is down?
All the examples I can find regarding offline pages has to do with the client being offline. What I need is to show the user a cached version of my site if the server can't be reached.
I've read about the Cache Manifest in HMTL 5 but it's getting removed and it causes to many problems.
What can be done without using any other loadbalancing servers and such?
I recently learned that with Fetch API and service workers its dead simple:
First, you register the Service worker:
if (!navigator.serviceWorker) return;
navigator.serviceWorker.register('/sw.js')
Then configure it to cache whats needed:
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(staticCacheName).then(function(cache) {
return cache.addAll([
'/',
'js/main.js',
'css/main.css',
'imgs/icon.png',
]);
})
);
});
And use Fetch API to get cached peaces if no response from the call:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
if need to get cached version only if server is down, try something like:
self.addEventListener('fetch', function(event) {
event.respondWith(
return fetch(event.request).then(function(response) {
if (response.status !== 200) {
caches.match(event.request).then(function(response) {
return response;
}).catch(function(error) {
console.error('Fetching failed:', error);
throw error;
});
})
);
});
p.s. using Fetch API seems much nicer way than implementing old and nasty XMLHttpRequest.
I just started trying to use service workers to cache files from my web server. I'm using the exact code that google suggests HERE. I uploaded the service worker script to my sites root folder and have included a link to it in the pages I want the service worker to cache files on.
I was under the impression that the service worker only caches files that are in the urlsToCache variable, and the service worker would only work on pages where the service worker script is called (linked to).
I have several pages on my site that I don't want the service worker to do anything on. However, it seems that it's still being referenced somehow. The pages in question do not contain a link to my service worker script at all. I've noticed that each time I run an AJAX command using the POST method I receive the following error in my console:
Uncaught (in promise) TypeError: Request method 'POST' is unsupported
at service-worker.js:40
at anonymous
line 40 is this snippet of code: cache.put(event.request, responseToCache);
The url my AJAX call is pointing to does not contain a link to my service worker script either.
So my question is a two parter.
1.) Does anyone understand the error message I'm receiving and know how to fix it?
2.) Why is my service worker script running on pages that don't even link to the script in the first place?
Here is the full service worker script I'm using:
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'assets/scripts/script1.js',
'assets/scripts/script2.js',
'assets/scripts/script3.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
Once a service worker is installed it runs independently of your website, means all requests will go through the fetch-eventhandler, your service worker controls also the other pages.
Not only the urlsToCache are cached, the service worker also caches responses on the fly as soon as they were fetched in the fetch-eventhandler (line 38-41)
This also leads to the answer for your first question. Your code caches all responses independent of the http method, but as the error message says http POST response cannot be cached.