How to intercept esri-leaflet xhr request and then check CacheStorage API, if cache exist return cached feature layer data rather than send real xhr

67 views Asked by At

My web app uses esri-leaflet package, it loads geojson via xhr rather than fetch from ArcGIS server. I am trying to use the following code to intercept xhr requests, however, it doesn't work, and the browser doesn't render the cache data, I am not sure what's going on...

The code is running fine, save data to browser's cache storage, and then retrieve data successfully, but no matter how I configurate the xhr response, it seems browser never treat the fake xhr call success, and don't feed back the cached data.

const cache = await caches.open('my-cache');

(function() {
  var origOpen = XMLHttpRequest.prototype.open;
  var origSend = XMLHttpRequest.prototype.send;
  var targeturl, rs;
    
  XMLHttpRequest.prototype.send = async function() {
    var self = this;
    // Check for cached response before sending the request
    const cachedResponse = await caches.match(targeturl, {
      //ignoreSearch: true, //true will ignore querystring
      ignoreMethod: true,
      ignoreVary: true
    });

    if (cachedResponse) {
      // Use cached response
      rs = await cachedResponse.json();
      Object.defineProperty(self, 'status', {
            writable: true,
            configurable: true
        });
      Object.defineProperty(self, 'readyState', {
            writable: true,
            configurable: true
        });
      Object.defineProperty(self, 'responseText', {
            writable: true,
            configurable: true
        });
      Object.defineProperty(self, 'responseURL', {
            writable: true,
            configurable: true
        });
      Object.defineProperty(self, 'response', {
            writable: true,
            configurable: true
        });
      self.status = 200; // Set appropriate status code
      self.readyState = 4;
      self.responseText = JSON.stringify(rs); // Access cached response text
      self.response = JSON.stringify(rs);
      self.responseURL = targeturl;   
      self.dispatchEvent(new Event('loadstart'));
      self.dispatchEvent(new Event('load'));
      self.dispatchEvent(new Event('loadend'));
      
    } else {
      // Send the actual XHR request
      origSend.apply(self, arguments);
    }
  };

  XMLHttpRequest.prototype.open = function(method, url) {
    targeturl = url;  
    var self = this;

    // Corrected event listener logic
    self.addEventListener('loadstart', async (e) => {
      console.log('request started!');
      //try set response again as not working
      e.target.status = 200; // Set appropriate status code
      e.target.readyState = 4;
      e.target.responseText = JSON.stringify(rs); // Access cached response text
      console.log(e)
    });

    self.addEventListener('load', async (e) => {
      console.log('request completed!', e, rs);

      // Add response to cache if not already cached
      const cacheKeys = await cache.keys();
      const hit = cacheKeys.find((key) => key.url === url);
      if (!hit) {
        await cache.add(url); // Cache the actual response
      }
    });

    origOpen.apply(self, arguments);
  };
})();
1

There are 1 answers

0
lpfy On BEST ANSWER

After look at the esri-leaflet source code, I worked out the featureLayer render logic triggered by readystatechange event.

the above code all correct, just need to add

self.dispatchEvent(new Event('readystatechange'));