1. 程式人生 > >通用版父頁面跨域訪問iframe裡面的內容解決方案,jupyter+tornado跨域通訊解決方案

通用版父頁面跨域訪問iframe裡面的內容解決方案,jupyter+tornado跨域通訊解決方案

1、問題:獲取jupyter裡面的內容

現在已經將jupyter放進iframe裡面,現在的問題描述為:在父頁面獲取iframe裡面的內容,iframe裡面就是jupyter,這裡的父頁面是自己搭建的tornado伺服器開啟的頁面,網址為127.0.0.1:9000iframe裡面的jupyter用的網址是127.0.0.1:8888,所以涉及到了跨域請求。

1.1、通用版解決跨域問題(通用的不限於tornado),可以用於其他任何場景

父頁面獲取iframe裡面內容的解決方案如下:

實驗如下:開兩個tornado伺服器,一個埠9000,一個埠9001

現在在9000頁面要放一個iframe指向9001的一個頁面,再在9000父頁面列印9001iframe

裡面的東西是訪問不了的,提示跨域了,埠不一樣


目標父頁面127.0.0.1:9000訪問自己iframe src=127.0.0.1:9001裡面的內容

解決這個問題的方案的思路:

1、在127.0.0.1:9000頁面使用js程式碼動態增加一個iframesrc是與127.0.0.1:9001同級目錄下的一個頁面127.0.0.1:9001/execB.html

2、然後先去9001建立好這個頁面execB.html配置好url,保證能訪問到。

3、在127.0.0.1:9001/execB.html中,去做想做的js操作(如呼叫127.0.0.1:9001中的一個js函式,或者改變背景色等都在這個頁面進行)

原理解釋:9000頁面現在有兩個iframe,一個訪問9001,一個是動態增加的訪問9001/execB.html,9000載入完後返回9001的<body><html>….</html></body>標籤,此時,9001/execB.html也返回了,但是9001/execB.html裡面有操作9001的DOM的js程式碼如:parent.window.frames[“myframe9000”].document.getElementById(“test”).innerHTML,這樣就相當於9001/execB.html再操作9001裡面的DOM,就能夠操作成功

具體程式碼如下:

127.0.0.1:9000

html程式碼


js程式碼:

window.onload=function (ev) {
    function exec_iframe(){
    if(typeof(exec_obj)=='undefined'){
        exec_obj = document.createElement('iframe');
        exec_obj.name = 'tmp_frame';
        exec_obj.src = 'http://127.0.0.1:9001/execB';
        exec_obj.style.display = 'none';
        document.body.appendChild(exec_obj);//動態建立一個iframe
    }else{
        exec_obj.src = 'http://127.0.0.1:9001/execB?' + Math.random();
    }
  }

  exec_iframe()
}

127.0.0.1 :9001

html程式碼(目標id=notebook裡面的內容被獲取,被顯示,顏色更改):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
test9001
<div id="notebook">
    <p>這個頁面放到iframe裡面,目標獲取這裡的內容就算成功</p>
</div>
</body>
</html>

127.0.0.1:9001/execB.html(真正做取值的js頁面,被動態新增到9000頁面iframe)

js程式碼(去操作同一個域名下的127.0.0.1:9001的內容獲取id=notebook裡面的內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>
<script src="http://lib.baomitu.com/jquery/3.3.1/jquery.js"></script>
<script>
   //jquery操作方式,變個顏色
   // $('#notebook', window.parent.myframe.document).css("backgroundColor","red")

   //js的操作方式,變個顏色
   parent.window.frames["myframe"].document.getElementById("notebook").style.backgroundColor = "green";

   var temp = parent.window.frames["myframe"].document.getElementById("notebook").innerHRML;//取值
   console.log(temp)
</script>

至此,跨域訪問成功~

2、目標:自己的tornado伺服器的一個父頁面(127.0.0.1:9000/test)要取得iframe裡面(127.0.0.1:8888/notebooks/test2.ipynb)的jupyter伺服器的值。

發現其實jupyter自身還有一層保護,通過閱讀文件發現,還要去設定jupyter被引用的域名是誰。


(同時可以閱讀原始碼:notebook/base/handlers.py 62行開始定義了content_security_policy函式的操作,有相同描述,下文有解釋。)

如果不新增上面的這串程式碼,在自己寫的父頁面裡寫

<iframe id="myframe" name="myframe" src="http://127.0.0.1:8888/notebooks/test2.ipynb" frameborder="1" style="" height="400" width=“720"></iframe>

將會導致下圖1這裡都打不開,將會顯示圖2這種東西,就是self指向是不對的,不允許其他的頁面的iframe指向自己


1


2

那麼添加了下面這串之後一切顯示正常。但是現在的需求是獲取iframe的值,然後好在tornado服務端存入資料庫,好跟蹤記錄學生的學習情況。


使用1種所說的解決跨域的方案。在jupyter目錄下建立execB.html,讓這個execB.html去操作jupyter裡面的DOM獲取裡面的元素。本來是可以實現的。結果jupyter內部的content_security_policy定義的策略,讓execB.html也成了允許範圍之外的人

notebook/base/handlers.py裡面說的很清楚,如下圖:


就是說,如果settings定義了headers就用它的,那我們現在是定義了的(就是上面讓定義的在這裡:~.jupyter/jupyter_notebook_config.py,如過不定義自己的tornado伺服器的頁面裡的iframe顯示不了,但是這裡必須顯示出來,不然沒意義)但是現在定義了,因為execB.html也是一個動態建立的iframe,它又顯示不了。現在這種跨域取值的方式因為jupyter自身安全保護的機制取不了值。只有換一種思路~~~~

3、jupyter取值通過ajax jsonp跨域將資料傳給tornado伺服器

我現在想在jupyter(隨意開啟一個jupyter notebook http://127.0.0.1:8888/notebooks/test2.ipynb)中獲取了值(學生通過jupyter列印的返回結果)之後,通過更改jupyter原始碼,在原始碼中新增ajax方式,在jupyter中將獲取的結果傳給我自己的tornado服務端,然後就可以美滋滋的將學生操作的結果值存資料庫了。

需求:將jupyter notebook中列印的結果傳到自己的tornado服務端,在tornado服務端寫sql語句將jupyter notebook前端頁面傳遞過來的資料儲存,方便顯示到其他地方(二次開發jupyter notebook,將其結果作為一種使用jupyter的學生的操作反饋顯示在網站後臺)

3.1、jupyter頁面採用修改原始碼的方式

在jupyter原始碼根目錄下找到notebook/templates/notebook.html新增如下程式碼
window.onload=function () {
    //var temp_result = $("#notebook  div[class ~='selected'] .output_wrapper .output .output_text pre").text())
   var temp_result = $("#notebook .output_wrapper .output .output_text pre").text()
   console.log(temp_result)
}

3.2、新增ajax程式碼使用jsonp的方式做跨域請求

notebook/templates/notebook.html中新增ajax請求的程式碼

<script>
window.onload=function () {
    //temp.push($("#notebook  div[class ~='selected'] .output_wrapper .output .output_text pre").text())
   var temp_result = $("#notebook .output_wrapper .output .output_text pre").text()
   //console.log(temp_result)
    
   $.ajax({
       url: 'http://127.0.0.1:9000/ajaxjupyter',//http://127.0.0.1:9000/ajaxjupyter?callbackfun=jsonpCallback&temp_result=temp_result
       type: 'get',
       dataType: 'jsonp',
       jsonp:"callbackfun",//傳遞給請求處理程式或頁面的,用以獲得jsonp回撥函式名的引數名(預設為:callback)
       jsonpCallback:"jsonpCallback",//自定義的jsonp回撥函式名稱,預設為jQuery自動生成的隨機函式名,如果定義了就success:function(){}就失效
       data: {
           temp_result:temp_result
       },
   });
}
 function jsonpCallback(data) {
           console.log(data)
       }
</script>

3.3、tornado服務端需要做接收和處理返回

class AjaxjupyterHandler(HomeHandler):
    def get(self):
        temp_result = self.get_argument("temp_result", "")
        callbackfun = self.get_argument("callbackfun", "") #接受callback的名字callbackfun=jsonpCallback

        res = dict()
        res["status"] = 0
        res["info"] = "新增成功"
        res["temp_result"] = temp_result

        self.set_header("content-type", "application/json")
        data = json.dumps(res)

相關推薦

no