XMLHttpRequest entre dominios usando páginas de fondo

5 minutos de lectura

XMLHttpRequest entre dominios usando páginas de fondo
yuzeh

En mi extensión de Chrome, quiero tener mi options.html página comunicarse con algo como la API OpenId de Google. Para hacer esto sin problemas, tengo un oculto iframe en la página de opciones que abrirá la página de inicio de sesión de las cuentas de Google (siguiendo la secuencia de interacción de OpenId, etc.).

Mi problema es que no puedo comunicarme desde la página de opciones al iframe (el origen de la iframe es algo que controlo, pero no es lo mismo que mi extensión de Chrome) a través de window.postMessage. Me preguntaba si hay una solución rápida a este problema.

Si no hay, voy a hacer options.html contener un iframe que alberga el diseño y la lógica de la página.

XMLHttpRequest entre dominios usando páginas de fondo
robar w

No tienes que meterte con iframes. Es posible realizar XMLHttpRequests entre dominios, utilizando páginas de fondo. Desde Chrome 13, las solicitudes entre sitios se pueden realizar desde el script de contenido. Sin embargo, las solicitudes aún pueden fallar si la página se sirve con un encabezado de Política de seguridad de contenido con una restricción connect-src.

Otra razón para elegir el método nexy en lugar de las secuencias de comandos de contenido es que las solicitudes a los sitios http generarán una advertencia de contenido mixto (“La página en https://… muestra contenido no seguro de http://…”).

Otro motivo más para delegar la solicitud a la página de fondo es cuando desea obtener un recurso del file://, porque un script de contenido no puede leer desde file:, a menos que se esté ejecutando en una página en el file:// esquema.

Nota

Para habilitar las solicitudes de origen cruzado, debe otorgar permisos explícitamente a su extensión mediante el permissions matriz en su archivo de manifiesto.

Solicitud de sitios cruzados utilizando un script de fondo.

El script de contenido solicitaría la funcionalidad desde el fondo a través del mensajería API. Aquí hay un ejemplo de una forma muy simple de enviar y obtener la respuesta de una solicitud.

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*/
});

Fondo / evento página:

/**
 * 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
    }
});

Observación: las API de mensajería se han renombrado varias veces. Si su navegador de destino no es la última versión de Chrome, consulte esta respuesta.

Para completar, aquí hay un manifiesto archivo para probar mi demo:

{
    "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
    }
}

  • Gracias por esto. Fue útil, pero es posible que desee actualizarlo ya que su página de eventos en segundo plano tiene un par de problemas. xhttp.open() debe colocarse antes del POST setRequestHeader o se producirá un error. Además, no hay razón para establecer la longitud del contenido, ya que simplemente se rechazará.

    – ElJefe

    11 ago.

  • ¿Alguna sugerencia sobre cómo agregar un tiempo de espera/reintento a esto que funcionaría con la naturaleza asíncrona, que podría tener varias consultas ejecutándose a la vez?

    – ElJefe

    19 sep.


  • @JeffG ¿Podría ser un poco más específico?

    – Rob W.

    19 sep.

  • A veces, XMLHttpRequest puede bloquearse porque no hay un tiempo de espera establecido. Así que estaba buscando una manera de establecer un tiempo de espera y luego volver a intentarlo hasta 3 veces. Acabo de encontrar esto: stackoverflow.com/questions/1523686/timeout-xmlhttprequest que podría ayudar, pero no estoy seguro de determinar la cantidad de intentos dentro de cada solicitud, ya que no puede ser una variable externa debido a la naturaleza asíncrona.

    – ElJefe

    19 sep.


  • @JeffG Si llamas chrome.runtime.sendMessage desde la ventana emergente y luego inspeccione la página de fondo (haciendo que la ventana emergente se cierre) antes de recibir la respuesta HTTP, luego sendResponse de hecho fallará ya que el contexto emergente se ha cerrado.

    – Rob W.

    04 mar. 17 en 15:21

Implementé lo mismo usando jquery, es mucho más simple y también funcionó muy bien.

fondo.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);
    }); 

Pero asegúrese de que el archivo manifest.json tenga los permisos requeridos y el archivo 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" ]
  }

.

¿Ha sido útil esta solución?