JSONP淺析


DEMO : JSONP示例

為什么使用JSONP

JSONP和JSON是不一樣的。JSON(JavaScript Object Notation)是一種基於文本的數據交換方式,或者叫做數據描述格式。而JSONP(JSON with Padding)是一種方式或者說非強制性協議。它是為了解決某個難題而產生的一種技術方式。

為什么會用到JSONP呢?

我們平時在用ajax請求服務端數據時,一般是這么寫的:

$.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?

出什么問題了?被限制請求了!

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.

也就是請求來源與服務器不是同一個域名,不允許訪問。這就是瀏覽器同源策略

所謂"同源"指的是"三個相同":

協議相同
域名相同
端口相同

舉例來說,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:

<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數據,一般是這么寫的:

$data = array('name' => '52fhy', 'age' => '22');
echo json_encode($data);
exit;

這里需要改成:

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

一旦請求成功,服務端輸出了:

handler({name: "52fhy", age: "22"});

這時候瀏覽器就要響應了,找到handler()方法並執行,恰好,我們提前定義好了handler()方法。

這里為什么服務端沒有限制訪問呢?原因是我們通過動態添加了個:

<script src="http://demo.52fhy.com/jsonp/handJsonp.php?callback=handler"></script>

它的基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。

這種方法就是JSONP。

使用jQuery/Zepto

如果使用jQuery/Zepto,JSONP的方式將會和發送非跨域請求那樣簡單。
示例:

$.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里我們能接收到來自服務端的響應:

{name: "52fhy", age: "22"}

DEMO: Zepto jsonp demo

通過console控制台,可以看到請求信息:

Request URL:http://demo.52fhy.com/jsonp/handJsonp.php?_=1460899828609&callback=jsonp1
Request Method:GET
Status Code:200 OK

jQuery/Zepto為我們封裝好了回調函數,一般情況下不需要我們單獨去寫,如果你不想在success中處理,想單獨寫處理函數,那么可以通過設置這2個參數來實現:

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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM