Greasy Fork 支持简体中文。

请求替换器

2024/5/9 20:23:13

// ==UserScript==
// @name        请求替换器
// @namespace   Violentmonkey Scripts
// @match       *://*/*
// @grant       none
// @license     MIT
// @version     1.2
// @author      youngyy
// @description 2024/5/9 20:23:13
// ==/UserScript==
/**
 * 弹窗 显示替换操作
 */
let reqList = [];

function tableRow(table, position, key, value) {
    const tr = table.insertRow();
    tr.insertCell().innerText = position
    tr.insertCell().innerText = key
    const td2 = tr.insertCell();
    td2.innerText = value
    td2.contentEditable = true;

    let deleteTd = tr.insertCell();
    let deleteButton = document.createElement('button');
    deleteButton.textContent = 'Delete';
    deleteButton.onclick = function () {
        table.deleteRow(this.parentElement.parentElement.rowIndex);
    };
    deleteTd.appendChild(deleteButton);
}

function urlParam(uri, table) {
    const url = getUrlObj(uri);
    const params = new URLSearchParams(url.search);
    params.forEach((value, key) => tableRow(table, 'url', key, value))
}

function bodyParam(obj, table, parentKey) {
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            const fullKey = parentKey ? `${parentKey}.${key}` : key;
            if (!(typeof obj[key] === 'object' && obj[key] !== null)) {
                tableRow(table, 'body', fullKey, obj[key])
            } else {
                if (Array.isArray(obj[key])) {
                    // 使用forEach遍历数组
                    obj[key].forEach((item, index) => {
                        let arrKey = `${fullKey}[${index}]`;
                        if (typeof item === 'object' && item !== null) {
                            bodyParam(item, table, arrKey);
                        } else {
                            tableRow(table, 'body', arrKey, item)
                        }
                    });
                } else {
                    bodyParam(obj[key], table, fullKey);
                }
            }
        }
    }
}

function clickReqLi(item) {
    let tableContainer = document.getElementById("tableContainer");
    tableContainer.replaceChildren()

    let element = document.createElement("h4");
    // 使用URL构造函数来解析这个字符串
    const url = getUrlObj(item.url);
    element.id = "h4uri"
    element.innerText = `${url.origin}${url.pathname}`
    tableContainer.appendChild(element)

    const table = document.createElement('table');
    table.setAttribute('border', '1');
    table.setAttribute('width', '500px');
    table.style.cssText = ` text-align: center; `;
    // 创建表头
    const thead = table.createTHead();
    const headerRow = thead.insertRow();
    ['位置', '参数名', '值', '操作'].forEach(function (headerName) {
        var th = document.createElement('th');
        th.textContent = headerName;
        headerRow.appendChild(th);
    });

    urlParam(item.url, table)
    bodyParam(item.data, table)
    tableContainer.appendChild(table)
}

function hideDialog() {
    document.getElementById('dialogReqRepl').style.display = 'none'
}

function showDialog() {
    let dialog = document.getElementById('dialogReqRepl');
    if (dialog) {
        dialog.style.display = 'block';
    } else {
        const dialogHtmlStr = `
            <div id="dialogReqRepl" style="position: fixed; left: 0; top: 50%; width: 600px; height: 600px;overflow-y: scroll; background-color: #fff; border: 1px solid #ccc; padding: 10px; z-index: 9999;">
                <button id="closeDialogBtn" onclick="document.getElementById('tableContainer')?.replaceChildren();reqList=[];document.getElementById('reqList')?.replaceChildren()">清空</button>
                <button id="closeDialogBtn" onclick="document.getElementById('dialogReqRepl').style.display = 'none'">关闭</button>
                <ul id="reqList" style="width: 600px;height: 200px;overflow: scroll;white-space: nowrap; padding: 0;margin: 0"></ul>
                <div id="tableContainer"></div>
            </div>
        `;
        document.body.insertAdjacentHTML('beforeend', dialogHtmlStr);
        // 使div可拖拽
        document.getElementById('dialogReqRepl').addEventListener('mousedown', function (e) {
            const div = e.target;
            // 鼠标位置与div边界的偏移量
            const offsetX = e.clientX - div.offsetLeft;
            const offsetY = e.clientY - div.offsetTop;
            document.addEventListener('mousemove', mouseMoveHandler);
            document.addEventListener('mouseup', mouseUpHandler);

            function mouseMoveHandler(e) {
                div.style.left = (e.clientX - offsetX) + 'px';
                div.style.top = (e.clientY - offsetY) + 'px';
            }

            function mouseUpHandler() {
                document.removeEventListener('mousemove', mouseMoveHandler);
                document.removeEventListener('mouseup', mouseUpHandler);
            }
        })
    }
}

function showReqLog() {
    // 添加接口请求信息
    let str = "";
    reqList.forEach((item) => {
        str += `<li class="reqLi" style="border: 1px solid #ccc;padding: 5px;">${item.url}</li>`
    });
    let element = document.querySelector('#reqList');
    if (element) {
        element.innerHTML = str
        // 添加点击事件
        document.querySelectorAll('.reqLi').forEach((item, idx) => {
            item.addEventListener('click', () => {
                clickReqLi(reqList[idx])
            })
        })
    }
}

function getUrlObj(uri) {
    return new URL(uri, window.location.origin)
}

function hasTable() {
    return document.querySelector('#tableContainer table')
}

function isCurrUri(url) {
    if (hasTable()) {
        // 判断uri是否为设置的
        let element = document.querySelector('#h4uri');
        let urlObj = getUrlObj(element.innerText);
        let obj = getUrlObj(url);
        return obj.pathname === urlObj.pathname;
    }
}

function uriReplaceBody(dataStr, uris) {
    if (!isCurrUri(uris)) {
        return dataStr;
    }
    // 替换参数
    let parse = JSON.parse(dataStr);

    let listOf = document.querySelectorAll('#tableContainer table tr');
    Array.from(listOf).slice(1).forEach(item => {
        // 每行一个参数对
        let data = item.querySelectorAll("td");
        let s = data[1].innerText;
        parse[s] = data[2].innerText;
    })
    return JSON.stringify(parse);
}

function uriReplaceParam(url) {
    if (!isCurrUri(url)) {
        return url;
    }
    // 替换参数
    let listOf = document.querySelectorAll('#tableContainer table tr');
    const urls = getUrlObj(url);
    Array.from(listOf).slice(1).forEach(item => {
        // 每行一个参数对
        let data = item.querySelectorAll("td");
        let s = data[1].innerText;
        let text = data[2].innerText;
        urls.searchParams.set(s, text);
    })
    return urls.toString();
}

function mains() {
    document.addEventListener('keydown', (event) => {
        if (!(event.altKey && event.key === 'e')) {
            return;
        }
        // 隐藏或显示
        let elementById = document.getElementById('dialogReqRepl');
        if (elementById?.style.display === 'block') {
            hideDialog()
        } else {
            showDialog()
        }


        event.stopPropagation();
        event.preventDefault();
    });

    const originalSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function (data) {
        const self = this;
        // 获取reqList最后一项
        reqList[reqList.length - 1].data = JSON.parse(data);
        showReqLog()
        // 替换body
        data = uriReplaceBody(data, self._url)
        originalSend.call(self, data);
    };

    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url, async) {
        this._url = url;
        reqList.push({
            method: method,
            url: url,
        });
        url = uriReplaceParam(url);
        originalOpen.call(this, method, url, async);
    };
}

mains()