11

I have a website which I don't want to make people create accounts. It is a news feed with each news article categorized. I want to allow people to tag the categories they are interested in so that next time they go to the site it only shows news for the categories that are tagged.

I'm saving the tags in an indexedDB which I understand is available in a service worker.

Hence in my service worker I want to "intercept" requests to www.my-url.com, check the indexDB for what categories this person is interested in, and add some headers like 'x-my-customer-header': 'technology,physics,sports' so that my server can respond with a dynamic html of those categories only.

However I'm struggling to get the service worker to properly cache my root response. In my serviceworker.js, I console log every event.request for the onFetch handler. There are no requests that are related to my root url. I'm testing right now on my localhost, but I only see fetch requests to css & js files.

Here is my onFetch:

function onFetch(event) {
  console.log('onFetch',event.request.url);
  event.request.headers["X-my-custom-header"] = "technology,sports";
  event.respondWith(
    // try to return untouched request from network first
    fetch(event.request).catch(function() {
      // if it fails, try to return request from the cache
      caches.match(event.request).then(function(response) {
        if (response) {
          return response;
        }
        // if not found in cache, return default offline content for navigate requests
        if (event.request.mode === 'navigate' ||
          (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
          return caches.match('/offline.html');
        }
      })
    })
  );
}

I'm using rails so there is no index.html that exists to be cached, when a user hits my url, the page is dynamically served from my news#controller.

I'm actually using the gem serviceworker-rails

What am I doing wrong? How can I have my service worker save a root file and intercept the request to add headers? Is this even possible?

anthumchris
  • 8,245
  • 2
  • 28
  • 53
Terence Chow
  • 10,755
  • 24
  • 78
  • 141
  • but do you see the other requests in chrome dev tools? – Stef Chäser Mar 28 '18 at 05:08
  • @StefChäser yes. I think my issue is that rails dynamically produces localhost so there is no `index.html`? Not entirely sure... – Terence Chow Mar 28 '18 at 05:32
  • By the way, I think it is not a good idea to handle categories in the service worker, since it is application logic. (what if the browser doesn't support sw) And be aware that caches.match(event.request) does not compare the headers, so if you have a cached request for "technology,sports" it will also match if the user changes it to "technology,beer". Better handle the categories with url params. – Stef Chäser Mar 28 '18 at 05:35
  • Did you check if the service worker runs in the correct scope: https://stackoverflow.com/questions/35780397/understanding-service-worker-scope – Stef Chäser Mar 28 '18 at 05:42
  • @StefChäser yes I am operating on the root scope. – Terence Chow Mar 28 '18 at 05:44

1 Answers1

18

Credit here goes to Jeff Posnick for his answer on constructing a new Request. You'll need to respond with a fetch that creates a new Request to which you can add headers:

self.addEventListener('fetch', event => {
  event.respondWith(customHeaderRequestFetch(event))
})

function customHeaderRequestFetch(event) {
  // decide for yourself which values you provide to mode and credentials

  // Copy existing headers
  const headers = new Headers(event.request.headers);

  // Set a new header
  headers.set('x-my-custom-header', 'The Most Amazing Header Ever');
  // Delete a header
  headers.delete('x-request');

  const newRequest = new Request(event.request, {
    mode: 'cors',
    credentials: 'omit',
    headers: headers
  })
  return fetch(newRequest)
}
some
  • 48,070
  • 14
  • 77
  • 93
anthumchris
  • 8,245
  • 2
  • 28
  • 53
  • One caveat. This will strip out the original header. Jeff Posnick's solution is to make a new header based off the old one, and appending on the new header key. Which does work to preserve the old header's content. – dethstrobe Apr 07 '21 at 21:58
  • 1
    @dethstrobe You can do `headers: { ...evt.request.headers, 'x-my-custom-header': 'The Most Amazing Header Ever' }` to keep and overwrite the original requests headers. – Sadra M. Jun 27 '22 at 09:07
  • it's been a while since I've had to deal with this problem, but i'm almost sure you can't just use the spread operator on the headers as the browser's security hides those details so they will not copy. – dethstrobe Jun 28 '22 at 12:54
  • @dethstrobe I noticed that too. By using a new `Headers`-object, it is easy to copy existing headers, and modify the headers object. I wrote an answer with an example. – some Dec 21 '22 at 15:57
  • 1
    @AnthumChris I edited your answer and added how to copy the existing headers, and deleted my own answer. – some Dec 22 '22 at 20:09