JSONP和JSON是不一樣的。JSON(JAVAScript Object Notation)是一種基于文本的數據交換方式,或者叫做數據描述格式。而JSONP(JSON with Padding)是一種方式或者說非強制性協議。它是為了解決某個難題而產生的一種技術方式。
為什么會用到JSONP呢?
我們平時在用ajax請求服務端數據時,一般是這么寫的:
Copy $.ajax({ type: "get", url: "getData.php", dataType: "json", success: function (data, textStatus, jqXHR) { console.log(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert('fail'); } });
這是一段很普通的基于jQuery的AJAX請求,不會有什么問題。注意到:url里是getData.php,說明這個文件url是基于當前服務器的,例如可能是localhost,也就是前端發出的請求來源是localhost,后端肯定也是localhost。他們倆是在同一個域名下。當然,平時我們也不會特別注意。
這時候,假如這個url變成其它服務器上的地址,例如:'http://apis.juhe.cn/mobile/get?phone=13429667914&key=',我們再把請求發送出去,會發現出問題了。大家可以手動寫個示例看看。
DEMO: 為什么使用jsonp?
出什么問題了?被限制請求了!
Copy XMLHttpRequest cannot load http://apis.juhe.cn/mobile/get?phone=13429667914&key=. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://demo.52fhy.com' is therefore not allowed access.
也就是請求來源與服務器不是同一個域名,不允許訪問。這就是瀏覽器同源策略:
Copy 所謂"同源"指的是"三個相同": 協議相同 域名相同 端口相同 舉例來說,http://www.example.com/dir/page.html這個網址,協議是http://,域名是www.example.com,端口是80(默認端口可以省略)。它的同源情況如下。 http://www.example.com/dir2/other.html:同源 http://example.com/dir/other.html:不同源(域名不同) http://v2.www.example.com/dir/other.html:不同源(域名不同) http://www.example.com:81/dir/other.html:不同源(端口不同)
同源政策規定,AJAX請求只能發給同源的網址,否則就報錯。
那么,這時候該怎么辦呢?我就是想通過js請求對方服務器上的資源!
除了架設服務器代理(瀏覽器請求同源服務器,再由后者請求外部服務),有三種方法規避這個限制。
- JSONP
- WebSocket
- CORS
本文只對JSONP作介紹。
如何使用JSONP
首先前端這邊代碼得改改,假設先不用jQuery:
Copy <script type="text/JavaScript"> //跨域發送HTTP請求,從服務端獲取數據,callback指定回調函數名稱 var url = 'http://demo.52fhy.com/jsonp/handJsonp.php?callback=handler'; function getHello() { var script = document.createElement('script'); script.setAttribute('src', url); document.querySelector("head").AppendChild(script); } // 處理函數 function handler(data) { console.log(data); // our code here... } </script> <input type="button" value="發送跨域HTTP請求,獲取數據" onclick="getHello()" />
后端服務器也要改改。
例如,在PHP語言中,后端服務器在API里返回JSON數據,一般是這么寫的:
Copy $data = array('name' => '52fhy', 'age' => '22'); echo json_encode($data); exit;
這里需要改成:
Copy $data = array('name' => '52fhy', 'age' => '22'); handJsonp($data); //處理jsonp function handJsonp($data){ $callback = $_GET['callback'] ? : 'callback'; //默認使用callback echo sprintf("%s(%s)", $callback, json_encode($data)); exit; }
一旦請求成功,服務端輸出了:
Copy handler({name: "52fhy", age: "22"});
這時候瀏覽器就要響應了,找到handler()方法并執行,恰好,我們提前定義好了handler()方法。
這里為什么服務端沒有限制訪問呢?原因是我們通過動態添加了個:
Copy <script src="http://demo.52fhy.com/jsonp/handJsonp.php?callback=handler"></script>
它的基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。
這種方法就是JSONP。
使用jQuery/Zepto
如果使用jQuery/Zepto,JSONP的方式將會和發送非跨域請求那樣簡單。
示例:
Copy $.ajax({ type: "get", async: false, url: "http://demo.52fhy.com/jsonp/handJsonp.php", dataType: "jsonp", success: function (data, textStatus, jqXHR) { console.log(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert('fail'); console.log(XMLHttpRequest, textStatus, errorThrown); } });
只需要將dataType設置成jsonp就可以進行跨域請求了。在success里我們能接收到來自服務端的響應:
Copy {name: "52fhy", age: "22"}
DEMO: Zepto jsonp demo
通過console控制臺,可以看到請求信息:
Copy Request URL:http://demo.52fhy.com/jsonp/handJsonp.php?_=1460899828609&callback=jsonp1 Request Method:GET Status Code:200 OK
jQuery/Zepto為我們封裝好了回調函數,一般情況下不需要我們單獨去寫,如果你不想在success中處理,想單獨寫處理函數,那么可以通過設置這2個參數來實現:
Copy jsonp: "callback",//傳遞給服務端的回調函數名稱參數,如果不設置此項,則默認是"callback" jsonpCallback: "handler",//傳遞給服務端的回調函數名稱,如果不設置此項,jQuery默認是形如"jQuery18308539637457579374_1460898291266"的由jQuery自動生成的函數名稱,Zepto默認是`jsonp1`
如果是zepto,可以在success回調里面使用console.log(jsonp1),發現jsonp1()方法是存在的。
需要注意的是:
1.JSONP雖然看起來很像一般的ajax請求,但其原理不同,JSONP是通過<script>標簽的動態加載來實現的跨域請求,而一般的ajax請求是通過XMLHttpRequest對象進行;
2.JSONP不是一種標準協議,其安全性和穩定性都不如 W3C 推薦的 CORS;
3.JSONP不支持POST請求,即使把請求類型設置為post,其本質上仍然是一個get請求。
參考
1、瀏覽器同源政策及其規避方法 - 阮一峰的網絡日志
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
2、跨域資源共享 CORS 詳解 - 阮一峰的網絡日志
http://www.ruanyifeng.com/blog/2016/04/cors.html
3、說說JSON和JSONP,也許你會豁然開朗_知識庫_博客園
http://kb.cnblogs.com/page/139725/
4、跨域解決方案二:使用JSONP實現跨域 - choon - 博客園
http://www.cnblogs.com/choon/p/5393682.html
5、JSON-P: Safer cross-domain Ajax with JSON-P/JSONP
http://json-p.org/
本文首發于公眾號"飛鴻影的博客(fhyblog)",歡迎關注。博客地址:https://52fhy.cnblogs.com
(本文完)