Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hook all Fetch Api AJAX requests

How would you hook all AJAX requests that use the Fetch Api? Previously we could do something like this to hook all XMLHttpRequest:

(function() {
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
      console.log('request started!');
      this.addEventListener('load', function() {
          console.log('request completed!');
          console.log(this.readyState); //will always be 4 (ajax is completed successfully)
          console.log(this.responseText); //whatever the response was
      });
      origOpen.apply(this, arguments);
    };
  })();

Or better yet, if you wanted to add to the function above, how would you hook all Fetch Api and all XMLHttpRequest AJAX requests?

like image 403
colemerrick Avatar asked Jun 23 '17 19:06

colemerrick


2 Answers

It is easy to do due to nature of promises:

var taperFunction = response => response;
var originalFetch = fetch;
fetch = (input, init) => originalFetch(input, init)
    .then(response => new Promise((resolve) => {
        resolve(taperFunction(response));
    }));

and a bit more complex(explicit) example:

var originalFetch = fetch;
fetch = (input, init) => {
    debugger; // do what ever you want with request or reject it immediately
    return originalFetch(input, init).then(response => {
        // it is not important to create new Promise in ctor, we can await existing one, then wrap result into new one
        return new Promise((resolve) => {
            response.clone() // we can invoke `json()` only once, but clone do the trick
                .json()
                .then(json => {
                    debugger; // do what ever you want with response, even `resolve(new Response(body, options));`
                    resolve(response);
                });
        });
    });
};

like image 63
FoC Avatar answered Oct 22 '22 17:10

FoC


In fact, Fetch Api is supported by native browser and only has one interface: fetch. The constructor return one Promise, and you can't get the RequestResponse when you want to return your Promise to rewrite the fetch's constructor.

The following code doesn't work well.

(function() {
    var oldFectch = fetch;
    fetch.consotructor = function() {
        return new Promise(resolve, reject) {
            // your hook code
        };
    };
})();

So,Does it mean we can't hook all Fetch Api ? NO!

Firstly, thanks the window.fetch polyfill.

Then, let's do something(edit fetch.js) and rock.

(function(self) {
    'use strict';

    // comment or delete the following code block
    // if (self.fetch) {
    //    return
    // }

    var support = {
        searchParams: 'URLSearchParams' in self,
        iterable: 'Symbol' in self && 'iterator' in Symbol,
        // ...

Last, hook every thing as your better!

self.fetch = function(input, init) {
  return new Promise(function(resolve, reject) {
    var request = new Request(input, init)
    var xhr = new XMLHttpRequest()

    // Here we go!
    // Now we get the XMLHttpRequest Object
    // Do what you want!

    xhr.onload = function() {
      var options = {
        status: xhr.status,
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || '')
      }
      options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
      var body = 'response' in xhr ? xhr.response : xhr.responseText
      resolve(new Response(body, options))
    }
    // ...
like image 23
Rambo Avatar answered Oct 22 '22 16:10

Rambo