Błąd przechwytywania, jeśli nie można załadować elementu iframe src. Błąd: - „Odmowa wyświetlenia„ http://www.google.co.in/ ”w ramce…”

79

Używam Knockout.jsdo wiązania srcznacznika iframe (będzie to konfigurowalne w odniesieniu do użytkownika).

Teraz, jeśli użytkownik skonfigurował http://www.google.com (wiem, że nie załaduje się w iframe, dlatego używam go w scenariuszu -ve) i musi to być pokazane w ramce iFrame. ale wyrzuca błąd: -

Odmówił wyświetlenia „ http://www.google.co.in/ ” w ramce, ponieważ ustawił „X-Frame-Options” na „SAMEORIGIN”.

Mam następujący kod dla iframe: -

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

Chcę, żeby adres URL się nie załadował. Chcę wyświetlić wiadomość niestandardową . Zrzut ekranu konsoli, gdy pojawia się błąd FIDDLE TUTAJ

Teraz, jeśli użyję onload i onerror jako: -

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

Działa poprawnie, ładując w3schools.com, ale nie z google.com.

Po drugie: - jeśli zrobię to jako funkcja i spróbuję tak, jak robiłem to na moich skrzypcach, to nie działa.

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

Nie wiem, jak mam go uruchomić i wychwycić błąd.

Edytowano: - Widziałem, że chcę wywołać funkcję, jeśli iframe nie ładuje lub nie ładuje pytania w stackoverflow, ale pokazuje błąd dla witryn, które można załadować w iframe.

Przyjrzałem się również elementowi iframe Stackoverflow podczas zdarzenia ładowania. Dzięki!

Shubh
źródło

Odpowiedzi:

44

Nie będziesz w stanie tego zrobić po stronie klienta z powodu tej samej polityki pochodzenia ustawionej przez przeglądarki. Nie będziesz w stanie uzyskać wielu informacji z elementu iFrame poza podstawowymi właściwościami, takimi jak szerokość i wysokość.

Ponadto Google ustawia w swoim nagłówku odpowiedzi „ X-Frame-Options ” SAMEORIGIN.

Nawet jeśli wykonałeś połączenie AJAX z Google, nie będziesz w stanie sprawdzić odpowiedzi, ponieważ przeglądarka wymusza takie same zasady pochodzenia.

Tak więc jedyną opcją jest wysłanie żądania z serwera, aby sprawdzić, czy możesz wyświetlić witrynę w ramce iFrame.

Tak więc na Twoim serwerze… Twoja aplikacja internetowa wysyła żądanie do www.google.com, a następnie sprawdza odpowiedź, aby sprawdzić, czy zawiera argument nagłówka X-Frame-Options. Jeśli tak, to wiesz, że ramka IFrame będzie zawierać błąd.

Evan Larsen
źródło
Roblox używa iframesztuczki, aby wykryć, czy gra jest zainstalowana za <iframe src="roblox-player:foo">pomocą wykrywania niestandardowego modułu obsługi URI. Wykrycie tego iframebłędu powoduje wyświetlenie przycisku „Pobierz”. Jak wtedy to robią?
tresf
1
@QZ Support Nie mam pojęcia, o czym mówisz.
Evan Larsen
1
@EvanLarsen Wspominam o bardzo popularnej grze wideo, która z powodzeniem wykorzystuje <iframe>technikę do uruchomienia (i miałem nadzieję wykryć) moduł obsługi URI zarejestrowany w systemie operacyjnym. To darmowa gra, możesz ją zainstalować i przekonać się sam. Kiedy dołączasz do gry przez przeglądarkę, używa ona hacka iframe, aby uruchomić grę. Jeśli to się nie powiedzie, pokażą się przycisk „Pobierz”. W jakiś sposób wiedzą, czy identyfikator URI był obsługiwany prawidłowo. Moje wstępne przypuszczenie polegało na wyłapaniu iframebłędu, ale odpowiedzi (a także moje testy) wydają się sugerować, że nie jest to możliwe. Skąd wiedzą, że iframe zawiedzie?
tresf
To tylko przypuszczenie, ale założę się, że witryna po prostu wywołuje własną aplikację internetową na twoim lokalnym komputerze. Jeśli połączenie się nie powiedzie, wyświetli się przycisk pobierania. Po pobraniu i zainstalowaniu gry na komputerze witryna internetowa będzie mogła wywoływać lokalną, hostowaną przez siebie aplikację internetową. Przez samo hostowaną aplikację internetową mam na myśli tylko aplikację z punktami końcowymi http, którą może wywołać każda znana strona internetowa. (tj. localhost: 7845 / opengame / room / 2534 )
Evan Larsen
To localhost:7845całkiem założenie. Najpierw trafi na ostrzeżenia / błędy dotyczące mieszanej zawartości (roblox.com używa HTTPS), a następnie będzie potrzebował portów przełączania awaryjnego w przypadku konfliktów. Technika używana do uruchamiania oprogramowania to iframe. Można to zaobserwować, badając źródło strony. W jakiś sposób, gdy to nie uruchamia oprogramowania, są w stanie to wykryć. Można to zrobić za pomocą metody „telefon do domu” + limit czasu przy użyciu identyfikatora modułu śledzącego w przeglądarce. Wspominam o tym, ponieważ to iframewykrywanie awarii jest przydatne do uruchamiania powiązanych aplikacji, ale tylko wtedy, gdy awarię można wykryć.
tresf
15

Myślę, że możesz powiązać loadzdarzenie z iframe, zdarzenie jest uruchamiane, gdy zawartość iframe jest w pełni załadowana.

W tym samym czasie możesz uruchomić funkcję setTimeout, jeśli element iFrame jest załadowany, wyczyść limit czasu lub pozwól, aby limit został uruchomiony.

Kod:

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

});

Demo: http://jsfiddle.net/IrvinDominin/QXc6P/

Drugi problem

Dzieje się tak, ponieważ pomijasz pareny w wywołaniu funkcji wbudowanej; spróbuj to zmienić:

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

zaangażowany w to:

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

Demo: http://jsfiddle.net/IrvinDominin/ALBXR/4/

Irvin Dominin
źródło
51
Hej, tylko spojrzałem na skrzypce - nigdy nie zgłasza błędu.
vvohra87
12
Jestem na Ubuntu i Chrome - bez względu na to, jaki adres URL umieściłem w polu tekstowym, jest napisane „done” i „done func” :(
vvohra87
3
@IrvinDomininakaEdward: oba łącza do skrzypiec nie działają ... nie otwierają się ... czy usunąłeś skrzypce?
Hitesh,
10
minus 1, ponieważ jak w komentarzach powyżej stwierdzono: skrzypce nigdy nie wyrzucają oczekiwanego komunikatu o błędzie, gdy element iframe nie ładuje się. Dzieje się tak, ponieważ zdarzenie load elementu iframe jest uruchamiane nawet wtedy, gdy nie ładuje treści.
CodeToad,
3
To nie powinna być wybrana odpowiedź. Ten, który przedstawił poniżej Evan, jest poprawny: zabezpieczenia przeglądarki uniemożliwiają wykrycie tego.
Scott Smith,
9

Onload zawsze będzie wyzwalany, rozwiązałem ten problem używając bloku try catch, który wyrzuci wyjątek, gdy spróbujesz pobrać contentDocument.

iframe.onload = function(){
   var that = $(this)[0];
   try{
        that.contentDocument;
   }
   catch(err){
        //TODO 
   }
}
H.Eden
źródło
1
To najprostszy sposób, z jakim do tej pory się spotkałem i faktycznie też działa!
Simon Jackson,
15
To również nie działa w przypadku wielu domen - w obu przypadkach pojawi się błąd
Luoruize,
To zadziałało dla mnie, dla elementu iframe mojej własnej domeny. Opcje X-Frame: SAMEORIGIN
Ryan
dla mnie zgłasza ten sam wyjątek nawet wtedy, gdy element iframe załadował zawartość. skąd wiesz, czy zawartość nie jest załadowana?
leeCoder
Już miałem odpowiedzieć, że w ten sposób można wyłapać ten błąd! Ale już to zrobiłeś! W ten sposób rozwiązuję dokładnie ten sam problem (a także sprawdzam, czy użytkownik został przekierowany na określoną stronę w mojej domenie)! Natknąłem się na to pytanie, szukając „obsługi błędów w elemencie iframe”, ale teraz mam do czynienia z błędem 503 w elemencie iframe!
Sampgun
8

To drobna modyfikacja odpowiedzi Edensa - która u mnie w chrome nie złapała błędu. Mimo że w konsoli nadal pojawia się błąd: „Odmowa wyświetlenia„ https://www.google.ca/ ”w ramce, ponieważ ustawiono„ X-Frame-Options ”na„ sameorigin ”.” Przynajmniej to złapie komunikat o błędzie i wtedy możesz sobie z tym poradzić.

 <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>
user2677034
źródło
Powoduje to błąd „nie można odczytać właściwości 'location' o wartości null” dla, that.contentDocumenta wiele działających witryn nadal będzie zgłaszać błąd that.contentWindowpodczas procesu ładowania.
Opuszczony
1

Miałem podobny problem. Rozwiązałem to bez użycia modułu obsługującego onload, pracowałem nad projektem AngularJs, więc użyłem $ interwał i $ timeout. Możesz także użyć setTimeout i setInterval. Oto kod:

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

Co 0,4 sekundy sprawdzaj nagłówek dokumentu iFrame. Coś jest obecne Ładowanie nie zostało zatrzymane przez CORS, ponieważ błąd CORS pokazuje pustą stronę. Jeśli nic nie jest obecne po 5 sekundach, wystąpił błąd (polityka Cors) itp. Pokaż odpowiednią wiadomość. Dzięki. Mam nadzieję, że to rozwiązuje twój problem.

viresh
źródło
2
Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "..." from accessing a cross-origin frame.- czy na pewno działa w wielu domenach?
Mars Robertson
0

Mam ten sam problem, Chrome nie może odpalić przy błędzie w przypadku błędu ładowania iframe src.

ale w moim przypadku potrzebuję tylko żądania HTTP między domenami, więc używam skryptu src / onload / onerror zamiast iframe. działa dobrze.

Engin Morse
źródło
0

Rozwiązałem to z window.length. Ale dzięki temu rozwiązaniu możesz wziąć bieżący błąd (X-Frame lub 404).

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

MSDN

Qeemerc
źródło
0

Jak wyjaśniono w zaakceptowanej odpowiedzi, https://stackoverflow.com/a/18665488/4038790 , musisz sprawdzić za pośrednictwem serwera.

Ponieważ nie ma niezawodnego sposobu sprawdzenia tego w przeglądarce, proponuję zbudować sobie szybki punkt końcowy serwera, którego możesz użyć do sprawdzenia, czy jakikolwiek adres URL jest możliwy do załadowania przez iframe. Gdy serwer jest już uruchomiony, po prostu wyślij do niego żądanie AJAX, aby sprawdzić dowolny adres URL, podając adres URL w ciągu zapytania jako url(lub cokolwiek sobie życzy serwer). Oto kod serwera w 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)

lwdthe1
źródło