由于webRequest API只能捕获请求但无法捕获响应,因此此处使用注入脚本(injected)的方式来实现。

一、测试页面

测试使用的Html代码如下,其中点击Ajax按钮将会以XMLHttpRequest方式发送Post请求,点击Fetch按钮将以fetch方式发送Get请求。

demo.html:

<!DOCTYPE HTML>
<html>
	<head>
		<title>demo</title>
		<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	</head>
	<body>
		<button id="ajax">Ajax</button>
		<button id="fetch">Fetch</button>
		<script>
			$(function(){
				$("#ajax").click(function(){
					$.post("http://httpbin.org/post", {'hello': 'world'}, function(data){
						console.log(data);
					});
				});
				$("#fetch").click(function(){
					fetch('https://jsonplaceholder.typicode.com/users/1', {'foo': 'bar'}).then(function(response){
						return response.json();
					}).then(function(json){
						console.log(json);
					});
				});
			});
			
		</script>
	</body>
</html>

二、Chrome插件

插件目录如下:

demo
|   demo.png
|   manifest.json
|
\---js
        demo.js
        injected.js

1、Mainifest文件

manifest.json:

{
	"name": "demo",
    "description" : "capture request and response",
    "version": "1.0",
    "manifest_version": 2,
	"permissions": [
        "http://*/*",
        "https://*/*",
		"tabs"
    ],
	"background": {
        "scripts": []
    },
	"content_scripts": [{
		"matches": ["https://*/*", "http://*/*"],
		"run_at": "document_start",
		"js": ["js/demo.js"]
    }],
    "browser_action": {
		"default_icon": "demo.png"
    },
	"web_accessible_resources": ["js/injected.js"]
}

其中内容脚本demo.js中将动态加载injected.js,由于安全原因,需要加载(注入)的js文件需要在web_accessible_resources中配置。

2、内容脚本

demo.js:

var s = document.createElement('script');
s.src = chrome.extension.getURL('js/injected.js');
s.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);

3、注入脚本

在注入脚本中将对原生的XMLHttpRequest及fetch对象做扩展来实现对请求和响应的捕获。

injected.js:

var datas = [];

(function(xhr) {

    var XHR = XMLHttpRequest.prototype;

    var open = XHR.open;
    var send = XHR.send;
    var setRequestHeader = XHR.setRequestHeader;

    XHR.open = function(method, url) {
        this._method = method;
        this._url = url;
        this._requestHeaders = {};
        this._startTime = (new Date()).toISOString();

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

    XHR.setRequestHeader = function(header, value) {
        this._requestHeaders[header] = value;
        return setRequestHeader.apply(this, arguments);
    };

    XHR.send = function(postData) {

        this.addEventListener('load', function() {
            var endTime = (new Date()).toISOString();

            var myUrl = this._url ? this._url.toLowerCase() : this._url;
            if(myUrl) {

				console.log('url: ',  myUrl);
				console.log('postData: ', postData);

                if ( this.responseType != 'blob' && this.responseText) {
                    try {
                        var text = this.responseText;                      
						console.log('response: ', text);                    
                    } catch(err) {
                        console.log("Error in responseType try catch");
                        console.log(err);
                    }
                }

            }
        });

        return send.apply(this, arguments);
    };

})(XMLHttpRequest);

var originalFetch = window.fetch;

window.fetch = function(url, options) {
	var fch = originalFetch(url, options);

	console.log('url: ', url);
	console.log('options: ', options);

	fch.then(function(data) {
		if (data.ok && data.status == 200) {
			return data.clone().json();
		}
	}).then(function(a){
		console.log('response: ', a);
	});
	return fch;
}

三、效果演示

点击Ajax按钮:

点击Fetch按钮:

参考资料: