使用后台页面的跨域XMLHttpRequest

Cross-domain XMLHttpRequest using background pages

本文关键字:XMLHttpRequest 后台      更新时间:2023-09-26

在我的Chrome扩展中,我想让我的options.html页面与Google的OpenId API之类的东西进行通信。为了无缝地做到这一点,我在选项页面上有一个隐藏的iframe,它将弹出谷歌账户登录页面(遵循OpenId交互序列等(

我的问题是,我无法通过window.postMessage从选项页面与iframe通信(iframe的起源是我控制的,但与我的chrome扩展不同(。我想知道这个问题是否有一个快速的解决办法。

如果没有,我将使options.html包含一个包含页面布局和逻辑的iframe

您不必使用iframe。可以使用后台页面执行跨域XMLHttpRequest。自Chrome 13以来,可以从内容脚本中进行跨站点请求。但是,如果页面带有带有限制connect-src的内容安全策略标头,则请求仍然可能失败。

选择nexty方法而不是内容脚本的另一个原因是,对http网站的请求将导致混合内容警告(https://...显示了来自的不安全内容http://..."(。

将请求委派到后台页面的另一个原因是,当您希望从file://获取资源时,因为内容脚本无法从file:读取,除非它在file://方案的页面上运行。

注意
要启用跨源请求,必须使用清单文件中的permissions数组显式授予扩展名权限。

使用后台脚本的跨站点请求

内容脚本将通过消息API从后台请求功能。下面是一个发送和获取请求响应的非常简单的方法的示例。

chrome.runtime.sendMessage({
    method: 'POST',
    action: 'xhttp',
    url: 'http://www.stackoverflow.com/search',
    data: 'q=something'
}, function(responseText) {
    alert(responseText);
    /*Callback function to deal with the response*/
});

背景/事件页面:

/**
 * Possible parameters for request:
 *  action: "xhttp" for a cross-origin HTTP request
 *  method: Default "GET"
 *  url   : required, but not validated
 *  data  : data to send in a POST request
 *
 * The callback function is called upon completion of the request */
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
    if (request.action == "xhttp") {
        var xhttp = new XMLHttpRequest();
        var method = request.method ? request.method.toUpperCase() : 'GET';
        xhttp.onload = function() {
            callback(xhttp.responseText);
        };
        xhttp.onerror = function() {
            // Do whatever you want on error. Don't forget to invoke the
            // callback to clean up the communication port.
            callback();
        };
        xhttp.open(method, request.url, true);
        if (method == 'POST') {
            xhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }
        xhttp.send(request.data);
        return true; // prevents the callback from being called too early on return
    }
});

备注:消息传递API已被多次重命名。如果你的目标浏览器不是最新的Chrome版本,请查看这个答案。

为了完整起见,这里有一个清单文件来试用我的演示:

{
    "name": "X-domain test",
    "manifest_version": 2,
    "permissions": [
        "http://www.stackoverflow.com/search*"
    ],
    "content_scripts": {
        "js": ["contentscript.js"],
        "matches": ["http://www.example.com/*"]
    },
    "background": {
        "scripts": ["background.js"],
        "persistent": false
    }
}

我使用jquery实现了同样的东西——它简单得多,工作也很好。。

background.js

chrome.runtime.onMessage.addListener(function(request, sender, callback) {
  if (request.action == "xhttp") {
    $.ajax({
        type: request.method,
        url: request.url,
        data: request.data,
        success: function(responseText){
            callback(responseText);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            //if required, do some error handling
            callback();
        }
    });
    return true; // prevents the callback from being called too early on return
  }
});

contentscript.js

chrome.runtime.sendMessage({
        method: 'POST',
        action: 'xhttp',
        url: 'http://example-url.com/page.php',
        data: "key=value"
    }, function(reponseText) {
        alert(responseText);
    }); 

但请确保manifest.json文件具有所需的权限和jquery js文件

  "permissions": [
      "tabs", "activeTab", "http://example-url.com/*"
  ],
  "content_scripts": [ {
      "js": [ "jquery-3.1.0.min.js", "contentscript.js" ],
      "matches": [ "https://example-ssl-site.com/*" ]
  }],
  "background": {
      "scripts": [ "jquery-3.1.0.min.js", "background.js" ]
  }