1. 程式人生 > >跨域&JSONP

跨域&JSONP

參數 tps css 發送 端口 訪問 效果 cor end

1. 同源&跨域

1.1 什麽是同源?什麽是跨域?

  • 同源:協議頭、域名、端口完全一致就叫同源

  • 跨域:協議頭、域名、端口有一個不一樣就叫跨域

  • 判斷是否同源,如:http://www.example.com/detail.html 與以下地址對比

對比地址是否同源原因
http://api.example.com/detail.html 不同源 域名不同
https://www.example.com/detail.html 不同源 協議不同
http://www.example.com:8080/detail.html 不同源 端口不同
http://api.example.com:8080/detail.html
不同源 域名、端口不同
https://api.example.com/detail.html 不同源 協議、域名不同
https://www.example.com:8080/detail.html 不同源 端口、協議不同
http://www.example.com/other.html 同源 只是目錄不同

1.2 跨域的影響

  • 註:根據同源和跨域的意思,跨域就是指不同源
  • 如果要在 localhost/test.html 這個網站上訪問 http://api.douban.com/v2/movie/top250 這個接口,那麽瀏覽器會出現以下錯誤:

技術分享圖片

  • 原因:瀏覽器本身從安全角度考慮,不支持跨域訪問

2. JSONP

2.1 如何突破跨域請求限制

2.1.1 使用img標簽(拿不到響應體)

我們知道:img 標簽,有個 href 屬性,這個屬性其實也是向服務器發送請求,我們看看這種方式是否可以跨越訪問,代碼如下:

<img href="http://api.douban.com/v2/movie/top250" alt="">

此時可以看到能正確收到響應,但拿不到響應體(返回的JSON數據),如圖:

技術分享圖片

2.1.2 使用link標簽(拿不到響應體)

link 標簽,有個 src 屬性,我們以前是用來外聯css樣式,其實它也是相當於發送請求

 <link rel="stylesheet"
src="http://api.douban.com/v2/movie/top250"

看瀏覽器響應報文,可以發現link標簽也可以跨域請求,但是一樣,沒辦法拿到響應體

2.1.3 使用script標簽(曲線拿到響應體)

代碼如下:

1 <script>
2     function test(json) {
3         console.log(我被調用了);
4         console.log(json);
5     }
6 </script>
7 <script src="http://api.douban.com/v2/movie/top250?callback=test"></script>

結果如下圖

技術分享圖片

說明:我們可以看到,我們預先定義了一個函數叫test,再然後在src裏加了一個參數callback=test,可以發現,當請求完成,會自動調用test這個函數,並且把響應體(JSON數據)當做參數傳遞過來

2.2 使用script標簽拿到JSON數據

2.2.1 script導入外部文件作用

  • 使用script導入外部文件時,默認會把文件內容當JS代碼執行,例:

  • 新建 1.txt 文件,文件內容如下:

alert(‘你好‘);
  • 新建 index.html 文件,核心代碼如下:
<script src="1.txt"></script>

結果:

技術分享圖片

結論:script導入文件會默認把文件內容當JS代碼執行,跟文件格式無關

2.2.2 script標簽曲線拿到響應體原理

  • 新建 index.html 頁面,代碼如下
1 <script>
2     //聲明一個success函數
3     function success(obj){
4         console.log(obj);
5     }
6 </script>
7 <!-- script導入文件 -->
8 <script src="http://www.demo.com/top250.php"></script>
  • 新建 top250.php 頁面,代碼如下
echo "success(‘hello‘)";

結果

技術分享圖片

說明:請求的top250文件裏,服務器最終返回的是 success(‘hello‘); 這個當JS代碼執行時,是調用函數並傳遞參數hello的語法,因此可以看到success函數被調用,並且打印了參數hello

圖解:

技術分享圖片

★★ 思考:如果在上例中,把success函數的參數換成json對象,會怎樣?

  • 修改 top250.php 代碼,如下:
1     $data = array(‘name‘ => ‘jack‘,‘age‘=>16,‘gender‘=>‘true‘ );
2 
3     $json = json_encode($data);
4 
5     echo "success({$json})"; 
  • 再次訪問網頁可以發現,打印的參數就變成了json對象

技術分享圖片

結論:script標簽拿到JSON數據的原理就是在服務器端寫調用瀏覽器端函數的方法 ,然後把JSON數據當參數傳遞過去

★★ 思考:現在服務端代碼這麽寫,如果要解決跨域問題,是不是每個瀏覽器端都要準備一個名叫success的函數?能讓瀏覽器端自己指定嗎?怎麽指定?

  • 請求數據時加參數,修改 index.html
1 <script>
2     //聲明函數
3     function myFunc(obj){
4         console.log(obj);
5     }
6 </script>
7 
8 <!-- 請求數據,並加入callback參數,值為myFunc -->
9 <script src="http://www.demo.com/top250.php?callback=myFunc"></script>
  • 此時,除了請求 top250.php 外,還傳遞了個 callback 的參數過去,修改 top250.php,拿到這個參數
 1 <?php
 2 
 3     //拿到傳遞過來的參數,即函數名
 4     $funcName = $_GET[‘callback‘];
 5 
 6     //準備數據
 7     $data = array(‘name‘ => ‘jack‘,‘age‘=>16,‘gender‘=>true );
 8 
 9     //轉化為JSON
10     $json = json_encode($data);
11 
12     //傳過來的是什麽函數名,就調用什麽函數
13     echo "$funcName($json)"; 
14 ?>

結果:此時就完成了指定函數名讓服務器調用傳參的效果了。

這就是之前跨域拿數據的原理,即服務器端調用瀏覽器端的函數,把參數傳遞過來即可

2.3 JSONP概念總結

  • JSONP:

    • JSON with Padding 是一種借助於script標簽發送跨域請求的技巧方案。

  • 它不是一套新技術,只是聰明的程序員想出的一套方案

  • 能不能用這套方案,要看服務器端代碼怎麽寫,服務器端如果寫了調用函數的代碼,那麽就能支持JSONP方案

  • 以後服務器的接口會有接口文檔進行說明是否支持JSONP

  • 建議:不要隨意使用別人的接口,特別是別人寫的支持JSONP的接口

    例:https://developers.douban.com/wiki/?title=api_v2

3. CORS

  • Cross Origin Resource Share,跨域資源共享

  • 直接從服務器端解決ajax無法跨域訪問的問題(可以不要用jsonp了)

// 允許遠端訪問
header(‘Access-Control-Allow-Origin: *‘);

這種方案無需客戶端作出任何變化(客戶端不用改代碼),只是在服務端添加一個 Access-Control-Allow-Origin 的響應頭,表示這個資源是否允許指定域請求(*代表任何網站都可以訪問)。

跨域&JSONP