在JavaScript中发送之前拦截XHR并更改请求头和URL

Intercept XHR and change request headers and url before send in JavaScript(在JavaScript中发送之前拦截XHR并更改请求头和URL)
本文介绍了在JavaScript中发送之前拦截XHR并更改请求头和URL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要截取所有正在发送的XHR请求,并在发送请求之前更改其URL和标头。
找到this类似问题,但没有答案。

我尝试挂钩XMLHttpRequest.prototype.open,但它只允许我访问响应:

(function () {
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function () {
        console.log(arguments); // prints method ("GET"), URL
        console.log(this); // prints response, responseText, responseURL, status, statusText, and onXXX handlers
        origOpen.apply(this, arguments);
    };
})();

还尝试了挂钩XMLHttpRequest.prototype.setRequestHeader,但它只允许我逐个访问正在设置的每个标头值,无法将其与请求的URL相关联:

(function () {
    var origSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
    XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
        console.log("header", header);
        console.log("value", value);
        origSetRequestHeader.apply(this, arguments);
    };
})();

我设法挂钩XMLHttpRequest.prototype.send来设置自定义标头,但是由于我想更改现有的标头键,所以它附加了我的新值,而不是替换现有的值。其他人也遇到了同样的问题:1,2:

(function () {
    var origSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function () {
        arguments[1] = myNewUrl; // arguments[1] holds the URL
        this.setRequestHeader('existingHeaderKey', newHeaderValue)
        origSend.apply(this, arguments);
    };
})();

如何完成此操作?

推荐答案

XMLHttpRequest(Xhr)接口公开的东西很少。所以您可以截获的内容是有限制的。

但是,我们可以将xhr对象包装在Proxy中并收集数据,直到调用Send为止。在发送请求之前,我们会一次性修改数据。

const OriginalXHR = XMLHttpRequest;

// wrap the XMLHttpRequest
XMLHttpRequest = function() {
  return new Proxy(new OriginalXHR(), {

    open(method, url, async, username = null, password = null) {
      lg('open');
      // collect URL and HTTP method
      this.modMethod = method;
      this.modUrl = url;

      this.open(...arguments);
    },

    setRequestHeader(name, value) {
      lg('set header');
      if (!this.modReqHeaders) {
        this.modReqHeaders = {};
      }
      // collect headers
      this.modReqHeaders[name] = value;

      // do NOT set headers here. Hold back!
      // this.setRequestHeader(name, value);
    },

    send(body = null) {
      lg('processing request...');
      // do the final processing
      // ...
      // don't forget to set headers
      for (const [name, value] of Object.entries(this.modReqHeaders)) {
        this.setRequestHeader(name, value);
      }

      lg('sending request =>' +
        '
		method: 	' + this.modMethod +
        '
		url:		' + this.modUrl +
        '
		headers:	' + JSON.stringify(this.modReqHeaders));
      this.send(body);
    },

    get(xhr, key) {
      if (!key in xhr) return undefined;

      let value = xhr[key];
      if (typeof value === "function") {
        // if wrapped, use the function in proxy
        value = this[key] || value;
        return (...args) => value.apply(xhr, args);
      } else {
        //return properties
        return value;
      }
    },

    set(xhr, key, value) {
      if (key in xhr) {
        xhr[key] = value;
      }
      return value;
    }
  });
}
console.warn('XMLHttpRequest has been patched!
 XMLHttpRequest: ', XMLHttpRequest);

let url = 'https://baconipsum.com/api/?type=all-meat&sentences=1&start-with-lorem=1';

function getData() {
  console.log('fetching lorem ipsum');
  let xhr = new XMLHttpRequest();
  xhr.responseType = 'json';

  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("demo").innerText = this.response[0];
    }
  };
  xhr.open("GET", url, true);
  xhr.setRequestHeader('Referer', 'www.google.com');
  xhr.setRequestHeader('Accept-Encoding', 'x-compress; x-zip')
  xhr.setRequestHeader('Accept-Language', 'de-US,en;q=0.5');
  xhr.send();
}

//fancy logging, looks good in dark mode
function lg(msg) {
  console.log('%c	 Proxy: ' + msg, 'background: #222; color: #bada55');
}
#demo {
  min-height: 100px;
  background-color: wheat;
}
<button onclick="getData()">Get data</button>
<div id="demo"></div>
<p>Note: look in the Developer Console for debug logs</p>

您可以根据需要将剩余的xhr方法或属性包装在代理处理程序中。
这可能比不上服务人员。但是服务人员有以下缺点:

服务工作器在工作器上下文中运行:因此它没有DOM访问权限,并且运行在与驱动您的应用程序的主JavaScript不同的线程上,因此它是非阻塞的。它被设计为完全异步;因此,诸如Synchronous XHR和Web Storage之类的API不能在服务工作者内部使用。

出于安全原因,服务工作人员仅通过HTTPS运行。修改了网络请求后,对中间人攻击完全开放将是非常糟糕的。在Firefox中,Service Worker API也是隐藏的,当用户处于私人浏览模式时无法使用。ref

这篇关于在JavaScript中发送之前拦截XHR并更改请求头和URL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)