Tampermonkey 解決呼叫 API CORS 問題(呼叫 API 示範)

在 Tampermonkey 的使用者腳本中發出 API 請求很容易會出現 CORS 錯誤:
    
translate.google.com.tw/:1 Access to XMLHttpRequest at 'http://localhost:3001/api' from origin 'https://translate.google.com.tw' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    

原因是因為伺服器沒有明確允許,所以被瀏覽器阻擋。不過這不是今天要探討的重點,在 Tampermonkey 有個內建函式可以很輕鬆的繞過這個限制,輕鬆的呼叫 API,就是使用 GM_xmlhttpRequest

發送 API 示範:
    
const url = 'http://localhost:3001/api';

const data = {
    a: 1,
    b: 2,
}

GM_xmlhttpRequest({
    method: "POST", // GET, HEAD, POST
    url: url,
    headers: {"Content-Type": "application/json"},
    data: JSON.stringify(data),
    timeout: 10000, // 毫秒
});
    

只是一發送就會出現錯誤:
    
userscript.html?name=New-Userscript.user.js&id=1d838e34-c86d-4cb8-9d8e-dd77722f9eff:25 Uncaught (in promise) ReferenceError: GM_xmlhttpRequest is not defined
    at userscript.html?name=New-Userscript.user.js&id=1d838e34-c86d-4cb8-9d8e-dd77722f9eff:25:5
    at Object.<anonymous> (userscript.html?name=New-Userscript.user.js&id=1d838e34-c86d-4cb8-9d8e-dd77722f9eff:32:3)
    at Gt (<anonymous>:9:89)
    at userscript.html?name=New-Userscript.user.js&id=1d838e34-c86d-4cb8-9d8e-dd77722f9eff:1:90
    at window.__f__lq8euq2z.kyn (userscript.html?name=New-Userscript.user.js&id=1d838e34-c86d-4cb8-9d8e-dd77722f9eff:1:318)
    at Gt (<anonymous>:9:89)
    at o (<anonymous>:78:23)
    at <anonymous>:80:411
    at f (<anonymous>:74:452)
    

GM_xmlhttpRequest is not defined?所以沒有這個函式可以使用?!

後來把整個文件看完,才發現原來在 Tampermonkey 中要使用 GM_, unsafeWindow, window 等功能強大的函式需要事先宣告權限,才可以使用:
    
// ==UserScript==
// @name         API Test
// @namespace    http://ruyut.com/
// @version      2023-12-16
// @description  發送 API 示範
// @author       Ruyut
// @match        https://translate.google.com.tw/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        GM_xmlhttpRequest
// @connect      localhost
// ==/UserScript==

(function () {
    'use strict';

    const data = {
        a: 1,
        b: 2,
    }

    const url = 'http://localhost:3001/api';

    GM_xmlhttpRequest({
        method: "POST", // GET, HEAD, POST
        url: url,
        headers: {"Content-Type": "application/json"},
        data: JSON.stringify(data),
        timeout: 10000, // 毫秒
    });

})();
    

並且在 UserScript 區塊中還要使用 @connect 定義允許的網域,不然會出現以下訊息,使用者需要手動點擊同意:

定義的時候可以定義多個允許的網域,也可以直接使用 * 表示全部允許
    
// @connect      localhost
// @connect      example.com
// @connect      *
    

該如何取得 API 的回應呢?直接看下面的完整範例:
    
// ==UserScript==
// @name         API Test
// @namespace    http://ruyut.com/
// @version      2023-12-16
// @description  發送 API 示範
// @author       Ruyut
// @match        https://translate.google.com.tw/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        GM_xmlhttpRequest
// @connect      localhost
// ==/UserScript==

(function () {
    'use strict';

    const url = 'http://localhost:3001/api';
    const data = {
        a: 1,
        b: 2,
    }

    GM_xmlhttpRequest({
        method: "POST", // GET, HEAD, POST
        url: url,
        headers: {"Content-Type": "application/json"},
        data: JSON.stringify(data),
        timeout: 10000, // 毫秒
        onload: function (response) { // 取得回應
            console.log(`status:`, response.status); // 200
            console.log(`result:`, response.responseText); // 回應 Body
        },
    });

})();
    



參考資料:
Tampermonkey - Documentation

留言