如果 iframe src 无法加载,则捕获错误。错误:-“拒绝在框架中显示 'http://www.google.co.in/'..”

Posted

技术标签:

【中文标题】如果 iframe src 无法加载,则捕获错误。错误:-“拒绝在框架中显示 \'http://www.google.co.in/\'..”【英文标题】:Catch error if iframe src fails to load . Error :-"Refused to display 'http://www.google.co.in/' in a frame.."如果 iframe src 无法加载,则捕获错误。错误:-“拒绝在框架中显示 'http://www.google.co.in/'..” 【发布时间】:2017-04-13 07:35:11 【问题描述】:

我正在使用 Knockout.js 绑定 iframe src 标记(这将针对用户进行配置)。

现在,如果用户配置了http://www.google.com(我知道它不会在 iframe 中加载,这就是我将它用于 -ve 场景的原因)并且必须在 IFrame 中显示。 但它会抛出错误:-

拒绝在框架中显示“http://www.google.co.in/”,因为它 将“X-Frame-Options”设置为“SAMEORIGIN”。

我有以下 Iframe 代码:-

<iframe class="iframe" id="iframe" data-bind="attr: src: externalAppUrl, height: iframeheight">
    <p>Hi, This website does not supports IFrame</p>
</iframe>

我想要的是,如果 URL 无法加载。我想显示自定义消息FIDDLE HERE

现在,如果我使用 onload 和 onerror 作为:-

<iframe id="browse" style="width:100%;height:100%" onload="alert('Done')" onerror="alert('Failed')"></iframe>

它可以正常加载 w3schools.com,但不能加载 google.com。

其次:- 如果我将它作为一个函数并尝试像我在小提琴中所做的那样,它不起作用。

<iframe id="browse" style="width:100%;height:100%" onload="load" onerror="error"></iframe>

我不知道我应该如何让它运行并捕获错误。

已编辑:- 我在 *** 中看到了 Want to call a function if iframe doesn't load or load's 问题,但它显示了可以在 iframe 中加载的网站的错误。

另外,我调查了*** iframe on load event 谢谢!!

【问题讨论】:

【参考方案1】:

由于浏览器设置了Same Origin Policy,您将无法从客户端执行此操作。除了宽度和高度等基本属性之外,您将无法从 iFrame 获得更多信息。

此外,google 在其响应标头中设置了 SAMEORIGIN 的“X-Frame-Options”。

即使您对 google 进行了 ajax 调用,您也无法检查响应,因为浏览器强制执行同源策略。

因此,唯一的选择是从您的服务器发出请求,看看您是否可以在 IFrame 中显示该网站。

因此,在您的服务器上.. 您的网络应用程序将向 www.google.com 发出请求,然后检查响应以查看它是否具有 X-Frame-Options 的标头参数。如果确实存在,那么您知道 IFrame 会出错。

【讨论】:

Roblox 使用 iframe 技巧通过检测自定义 URI 处理程序来检测是否使用 &lt;iframe src="roblox-player:foo"&gt; 安装了游戏。检测到此iframe 故障会触发“下载”按钮出现。那他们是怎么做到的呢? @QZ Support 我不知道你在说什么。 @EvanLarsen 我提到了一个非常流行的视频游戏,它成功地使用了&lt;iframe&gt; 技术来启动(我希望检测到)在操作系统中注册的 URI 处理程序。这是一款免费游戏,您可以安装它并自己查看。当您通过浏览器加入游戏时,它会使用 iframe hack 来启动游戏。如果失败,他们会显示“下载”按钮。不知何故,他们知道 URI 是否被正确处理。我最初的猜测是捕捉到iframe 错误,但答案(以及我的测试)似乎表明这是不可能的。他们如何知道 iframe 何时失败? 这只是一个猜测,但我敢打赌,该网站只是在您的本地计算机上调用一个自托管的 Web 应用程序。如果调用失败,则显示下载按钮。在您的计算机上下载并安装游戏后,该网站将能够调用本地自托管 Web 应用程序。通过自托管网络应用程序.. 我的意思只是一个带有 http 端点的应用程序,任何知道它的网站都可以调用它。 (即localhost:7845/opengame/room/2534) localhost:7845 完全是假设。首先,它会遇到混合内容警告/错误(roblox.com 使用 HTTPS),其次,它需要故障转移端口以防发生冲突。用于启动软件的技术是iframe。这可以通过检查页面源来观察。不知何故,当这无法启动软件时,他们能够检测到它。这可以通过使用浏览器跟踪器 ID 的“电话回家”+ 超时方法来完成。我提到这一点是因为这个iframe 故障检测对于启动关联的应用程序很有用,但前提是可以检测到故障。【参考方案2】:

我认为你可以绑定iframe的load事件,当iframe内容完全加载时触发该事件。

同时你可以启动一个 setTimeout,如果 iFrame 被加载清除超时或者让超时触发。

代码:

var iframeError;

function change() 
    var url = $("#addr").val();
    $("#browse").attr("src", url);
    iframeError = setTimeout(error, 5000);


function load(e) 
    alert(e);


function error() 
    alert('error');


$(document).ready(function () 
    $('#browse').on('load', (function () 
        load('ok');
        clearTimeout(iframeError);
    ));

);

演示:http://jsfiddle.net/IrvinDominin/QXc6P/

第二个问题

这是因为你错过了内联函数调用中的括号;尝试改变这个:

<iframe id="browse" style="width:100%;height:100%" onload="load" onerror="error"></iframe>

进入这个:

<iframe id="browse" style="width:100%;height:100%" onload="load('Done func');" onerror="error('failed function');"></iframe>

演示:http://jsfiddle.net/IrvinDominin/ALBXR/4/

【讨论】:

嘿,看看小提琴 - 它从不抛出错误。 我在 Ubuntu 和 Chrome 上——无论我在文本框中输入什么 url,它都会显示“done”和“done func”:( @IrvinDomininakaEdward :两个小提琴链接都不起作用......它没有打开......你删除了小提琴吗? 减 1,因为作为上述状态的 cmets:当 iframe 加载失败时,小提琴永远不会抛出预期的错误消息。这是因为 iframe 的 load 事件即使没有加载内容也会触发。 这不应该是选择的答案。下面 Evan 的说法是正确的:浏览器安全性阻止了检测到这一点。【参考方案3】:

onload 总是会被触发,我喜欢这个问题使用 try catch 块。当你尝试获取 contentDocument 时它会抛出异常。

iframe.onload = function()
   var that = $(this)[0];
   try
        that.contentDocument;
   
   catch(err)
        //TODO 
   

【讨论】:

这是迄今为止我遇到的最简单的方法,而且它也确实有效! 这也不适用于跨域 - 无论哪种方式都会引发错误 这对我有用,适用于我自己域的 iframe。 X-Frame-Options:SAMEORIGIN 对我来说,即使 iframe 加载了内容,它也会抛出相同的异常。怎么知道内容没有加载? 我正要回答,用这种方式可以捕捉到那个错误!但你已经做到了!我正在以这种方式处理相同的问题(并检查用户是否被重定向到我域中的某个页面)!我在搜索“处理 iframe 中的错误”时遇到了这个问题,但我现在在 iframe 中遇到 503 错误!【参考方案4】:

这是对 Edens 答案的轻微修改 - 对我来说,在 chrome 中没有发现错误。尽管您仍然会在控制台中收到错误消息: “拒绝在框架中显示'https://www.google.ca/',因为它将'X-Frame-Options'设置为'sameorigin'。”至少这会捕获错误消息,然后您可以处理它。

 <iframe id="myframe" src="https://google.ca"></iframe>

 <script>
 myframe.onload = function()
 var that = document.getElementById('myframe');

 try
    (that.contentWindow||that.contentDocument).location.href;
 
 catch(err)
    //err:SecurityError: Blocked a frame with origin "http://*********" from accessing a cross-origin frame.
    console.log('err:'+err);


</script>

【讨论】:

这会导致that.contentDocument 出现“无法读取属性'location' of null”错误,并且许多工作站点在加载过程中仍然会在that.contentWindow 上抛出错误。【参考方案5】:

我用window.length 解决了这个问题。 但是使用此解决方案,您可以获取当前错误(X-Frame 或 404)。

iframe.onload = event => 
   const isLoaded = event.target.contentWindow.window.length // 0 or 1

MSDN

【讨论】:

【参考方案6】:

更新: contentWindow.name 现在将始终在跨域帧上引发错误。 似乎唯一的方法是做这个服务器端(现在)。 我编写了一个小型 cloudflare worker 来捕获远程 api 的标头,它可以在这里用来检查 X-Frame-Options。

在 iframe 中呈现之前要检查的示例代码:(jsfiddle: https://jsfiddle.net/2gud39aw/2/)

function checkUrlFrameOptions(apiurl)
  return fetch("https://header-inspector.repalash.workers.dev/?" + new URLSearchParams(
    'apiurl': apiurl,
    'headers': 'x-frame-options'
  ), 
    method: 'GET'
  ).then(r => r.json()).then(json => 
    let xFrameOp = (json.headers['x-frame-options'] || '').toLowerCase();
    // deny all requests
    if(xFrameOp==='deny') return false;
    // deny if different origin
    if(xFrameOp==='sameorigin' && json.origin !== location.origin) return false;
    return true;
  )


checkUrlFrameOptions("https://google.com").then((res)=>console.log("google.com can be loaded in iframe: ", res))
checkUrlFrameOptions("https://example.com").then((res)=>console.log("example.com can be loaded in iframe: ", res))

cloudflare worker 端点 (https://header-inspector.repalash.workers.dev) 仅用于测试,请勿在生产中使用它。代码可在:https://gist.github.com/repalash/b1e778dbe3ac2e7149831c530a6535f9 并且可以直接部署为cloudflare worker

老答案

这是一个简单的解决方案,在 Chrome 和 Safari 上进行了测试。

    const iframe = document.createElement('iframe')
    iframe.onload = function() 
        try 
            iframe.contentWindow.name
         catch (e) 
            if (e.message.includes('cross-origin')) console.warn(e.message);
            else console.error(e.message);
        
    

    iframe.src = "https://google.com";

jsFiddle 演示:https://jsfiddle.net/k5e1mg3t/5/

【讨论】:

有没有人能让这个工作?我没有看到 iframe 有 contentWindow.name 属性?【参考方案7】:

我遇到了类似的问题。我在不使用 onload 处理程序的情况下解决了它。我正在处理 AngularJs 项目,所以我使用了 $interval 和 $ timeout。你也可以使用 setTimeout 和 setInterval。代码如下:

 var stopPolling;
 var doIframePolling;
 $scope.showIframe = true;
 doIframePolling = $interval(function () 
    if(document.getElementById('UrlIframe') && document.getElementById('UrlIframe').contentDocument.head && document.getElementById('UrlIframe').contentDocument.head.innerhtml != '')
        $interval.cancel(doIframePolling);
        doIframePolling = undefined;
        $timeout.cancel(stopPolling);
        stopPolling = undefined;
        $scope.showIframe = true;
    
,400);

stopPolling = $timeout(function () 
        $interval.cancel(doIframePolling);
        doIframePolling = undefined;
        $timeout.cancel(stopPolling);
        stopPolling = undefined;
        $scope.showIframe = false;     
,5000);

 $scope.$on("$destroy",function() 
        $timeout.cancel(stopPolling);
        $interval.cancel(doIframePolling);
 );

每 0.4 秒持续检查 iFrame 文档的头部。我有东西。CORS 没有停止加载,因为 CORS 错误显示空白页。 如果 5 秒后没有出现任何错误(Cors 政策)等。 显示合适的消息。谢谢。希望能解决你的问题。

【讨论】:

Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "..." from accessing a cross-origin frame. - 你确定它可以跨域工作吗?【参考方案8】:

我也有同样的问题,当 iframe src 加载错误时,chrome 无法触发 onerror。

但就我而言, 我只需要发出一个跨域的http请求, 所以我使用脚本 src/onload/onerror 而不是 iframe。 它运作良好。

【讨论】:

【参考方案9】:

如已接受的答案https://***.com/a/18665488/4038790 中所述,您需要通过服务器进行检查。

因为没有可靠的方法在浏览器中检查这一点,我建议您自己构建一个快速服务器端点,您可以使用它来检查是否有任何 url 可通过 iframe 加载。一旦您的服务器启动并运行,只需向其发送 AJAX 请求以检查任何 url,方法是在查询字符串中提供 url(或您的服务器所需的任何内容)中的 url。这是 NodeJs 中的服务器代码:

const express = require('express')
const app = express()

app.get('/checkCanLoadIframeUrl', (req, res) => 
  const request = require('request')
  const Q = require('q')

  return Q.Promise((resolve) => 
    const url = decodeURIComponent(req.query.url)

    const deafultTimeout = setTimeout(() => 
      // Default to false if no response after 10 seconds
      resolve(false)
    , 10000)

    request(
        url,
        jar: true /** Maintain cookies through redirects */
      )
      .on('response', (remoteRes) => 
        const opts = (remoteRes.headers['x-frame-options'] || '').toLowerCase()
        resolve(!opts || (opts !== 'deny' && opts !== 'sameorigin'))
        clearTimeout(deafultTimeout)
      )
      .on('error', function() 
        resolve(false)
        clearTimeout(deafultTimeout)
      )
  ).then((result) => 
    return res.status(200).json(!!result)
  )
)

app.listen(process.env.PORT || 3100)

【讨论】:

以上是关于如果 iframe src 无法加载,则捕获错误。错误:-“拒绝在框架中显示 'http://www.google.co.in/'..”的主要内容,如果未能解决你的问题,请参考以下文章

检测 iframe 加载错误

如果 iframe 无法在 src 加载资源,如何显示替代图像?使用 AngularJs?

sencha 日历错误未捕获类型错误:无法读取页面加载时未定义的属性“indexOf”

无法阻止加载iframe

上传问题

检测 iframe src 是不是可显示