您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replaces global fetch() with GM_xmlhttpRequest for cross-origin and controlled fetch support
// ==UserScript== // @name Override fetch with GM_xmlhttpRequest. Chatgpt generated // @namespace Violentmonkey // @version 1.0 // @description Replaces global fetch() with GM_xmlhttpRequest for cross-origin and controlled fetch support // @match *://*/* // @grant GM_xmlhttpRequest // @license MIT2 // ==/UserScript== (function () { 'use strict'; // === fetchToGM: Wrapper around GM_xmlhttpRequest === function fetchToGM(request) { const req = request.clone(); const headers = Object.fromEntries(req.headers.entries()); const method = req.method.toUpperCase(); const serializeFormData = async (formData) => { return new Promise((resolve) => { const boundary = "----WebKitFormBoundary" + Math.random().toString(36).substr(2); let body = ""; for (const [key, value] of formData.entries()) { body += `--${boundary}\r\n`; if (value instanceof Blob) { body += `Content-Disposition: form-data; name="${key}"; filename="${value.name || 'file'}"\r\n`; body += `Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`; body += "[Binary data omitted]\r\n"; } else { body += `Content-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`; } } body += `--${boundary}--`; headers["Content-Type"] = `multipart/form-data; boundary=${boundary}`; resolve(body); }); }; const readBody = async () => { if (method === "GET" || method === "HEAD") return null; const contentType = headers["Content-Type"] || headers["content-type"] || ""; if (req.body instanceof FormData) { return serializeFormData(req.body); } if (req.body instanceof Blob) { return req.body.text(); } if (contentType.includes("application/json")) { return req.text(); } return req.text(); }; return new Promise((resolve, reject) => { readBody().then(data => { GM_xmlhttpRequest({ method, url: req.url, headers, data, anonymous: req.credentials === "omit", withCredentials: req.credentials === "include", onload: function (res) { let responseBody = res.responseText; let responseHeaders = res.responseHeaders; const response = new Response(responseBody, { status: res.status, statusText: res.statusText, headers: parseResponseHeaders(responseHeaders) }); resolve(response); }, onerror: () => reject(new TypeError("Network request failed")), ontimeout: () => reject(new TypeError("Network request timed out")), onabort: () => reject(new TypeError("Network request aborted")), }); }).catch(reject); }); } // === Helper to parse GM response headers into a Headers object === function parseResponseHeaders(headerStr) { const headers = new Headers(); if (!headerStr) return headers; const pairs = headerStr.trim().split(/[\r\n]+/); for (const line of pairs) { const parts = line.split(": "); const key = parts.shift(); const value = parts.join(": "); headers.append(key, value); } return headers; } // === 🧠 Override Global fetch() === window.fetch = function (input, init) { const request = input instanceof Request ? input : new Request(input, init); return fetchToGM(request); }; console.log("✅ window.fetch has been overridden by fetchToGM"); })();