JavaScript使用jsonp實現跨域

  爲何要把ajax跨域寫一下呢,由於ajax跨域並非想跨就能跨的。由於爲了安全,ajax是不容許跨域的。javascript

  舉個例子,你有一個賣水果的網站,你的ajax請求另外一個網站提供的圖片,正常的時候,圖片是一個蘋果,可是那個網站被黑了,圖片全被改成全是大便的同名文件,那麼你請求回來的圖片一顯示,那場景,無限壯觀。php

  通常狀況下,只有本身的網站的內容纔是可信的,其餘外來的資源並非徹底可信的。好比平時開發中在script標籤src中引入CDN的jquery文件,若是某一天,未通知你,CDN上的jquery文件被惡意篡改了,那你可能就涼了。html

 

什麼是域

  域由三部分組成java

  一、協議(http、https、ftp....)jquery

  二、IP或者域名ajax

  三、端口json

  上面這三個部分,要是有一部分不一樣,就會出現跨域。後端

 

經常使用的跨域手段  

  Flash:使用flash插件完成,不多用,我沒用過。跨域

  本地代理:既然ajax請求其餘域名下的文件會失敗,可是後端的程序請求其餘域名的文件就不會存在跨域這個問題了吧,你能夠在後端寫一個程序,專門用來接收本地ajax對其餘域資源的請求,而後代替ajax去請求資源,而且將其餘域返回給後端的響應  再 返回給ajax,這樣ajax就能夠獲取到其餘域的資源了。雖然能達到目的,可是也請複雜的,不多采用。瀏覽器

  接受請求端設置http頭部信息。

  Jsonp:常用,下面重點寫。

  

經過設置頭部信息實現跨域

  以php爲例,在接受方的程序中的header添加一項便可:

//接收全部跨域請求
header("Access-Control-Allow-Origin:*");

//接收來自某個ip的請求
header("Access-Control-Allow-Origin:192.168.1.2");

//接收來自某個域名的請求
header("Access-Control-Allow-Origin:www.demo.com");

  

jsonp

   說jsonp以前,先說一下我們平時使用的一些標籤,好比script、img,他們都一個共同點———src屬性。經過src屬性,能夠將其餘資源包含進來,其中,src並不會有跨域的問題。

  好比下面這段代碼:

<script src="1.txt"></script>

  1.txt的內容以下:

var a=10;

  那麼在接下來的JavaScript代碼就能夠訪問從1.txt中獲取到的變量a了,此時的a是全局變量。其實就等價於:

<script>	
	var a=10;
</script>

  

   也許你已經猜到了,咱們能夠將src的值,也就是連接的地址改爲其餘域的資源名稱,好比

<script src="https://www.baidu.com"></script>

  這樣的話,我們就能將baidu首頁包含進來。

  可是,有一個很嚴峻的問題:你怎麼使用獲取的內容?

  雖然已經將內容百度的首頁內容引入了,至關於下面這樣:

<script>
	這是百度首頁的代碼
</script>

  你怎麼使用內容,很難頭疼的。由於並無像var a = 10那樣爲內容保存到變量中呀。

  因而聰明的你,確定想到了,既然包含的文件全是內容,並無賦值給一個變量,因此咱們沒法使用。那麼,咱們可讓他在返回資源的時候 就 先將內容項賦值給一個變量 而後再一併返回。

  既然是咱們要請求其餘域的資源,那麼其餘域確定會提供這個功能的,不會說只是單純的提供數據,若是隻是單純的提供數據,那麼讓他們加一下嘛,也就幾分鐘的事。

  好比說下面這樣:

<script src="demo.php"></script>
<script>
	console.log(a);
</script>

  demo.php內容以下:

<?php 
	$v = 10;
	echo "var a = " . $v;
 ?>

  而後JavaScript就會正常的運行。輸出10。

  上面簡單的示例,只是返回一個簡單的字符串,只不過該字符串是能夠再瀏覽器中執行的JavaScript語句。可是也有個問題,返回的JavaScript語句中的變量是全局的,對不?   因此咱們在想辦法,怎麼讓返回的內容不變成全局的呢?

  能夠這麼作:

<script>
	function Work(data){
		console.log(data);
	}
</script>
<script src="demo.php"></script>

  demo.php:

<?php 
	$v = 10;
	echo "Work(". $v .")";
 ?>

  能夠看到,只是在請求其餘域的資源以前先定義一個函數,該函數有參數。而在其餘域被請求的文件中,返回一個字符串,字符串內容前一部分就是以前定義的JavaScript函數(這裏是Work),以及將要傳回的數據做爲參數,內嵌到其中。

  其實如今用的技術就是jsonp,不知不覺吧。這就是jsonp中p(padding填充的含義),而jsonp中的json就是平時的json,合起來的理解就是:填充的內容時json格式的數據,填充到一個js的函數名中,組合成字符串返回。

  這裏有幾個注意點,

  一、實現定義函數。

  二、被請求資源中會返回的字符串中包含前面定義的函數。

 

  還有一個問題:咱們只是指定了src的連接,即請求誰,可是,咱們沒有指定何時指定。開發中,咱們更重要的是在須要的時候在發起請求,即 按需加載。

  注意這裏不要在一個script標籤中指定一個id,而後經過獲取這個script以後,動態修改src值,來發起請求,這是錯誤的,由於一旦加載過的代碼是不會再次執行的,除非你刷新頁面。

  正確的姿式是:建立一個dom元素節點(script),而後設置src以後,append到body中便可。

  好比下面這個用法:

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script>
		function Work(data){
			alert(data);
		}
	</script>
</head>
<body>
	<button id="btn">request</button>
</body>
<script>
	window.onload = function(){
		var btn = document.getElementById("btn");
		btn.onclick = function(){
			var script = document.createElement("script");
			script.src = "demo.php";
			document.body.append(script);
		}
	}
</script>
</html>

  demo.php

<?php 
	$v = 10;
	echo "Work(". $v .")";
 ?>

  運行時,一旦點擊click按鈕,就會建立一個script標籤,加載src的內容,而後執行返回的數據。

 

  還沒完,上面的function Work()中是將得到的響應數據alert出來,但如今咱們須要將相應數據console.log打印出來,咋辦?找後端的RD再建立一個Work2(),返回的時候,將Work改成Work2  ???

  若是你這樣作,多半會被人家打死的。

  其實你是能夠將你要調用的function傳遞給後端,而後後端在處理完數據以後,返回你所傳遞的函數名,向下面這樣:

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script>
		function WorkAlert(data){
			alert(data);
		}
		function WorkConsoleLog(data){
			console.log(data);
		}
	</script>
</head>
<body>
	<button id="btn1">alert</button>
	<button id="btn2">console.log</button>
</body>
<script>
	window.onload = function(){
		var btn1 = document.getElementById("btn1");
		var btn2 = document.getElementById("btn2");
		btn1.onclick = function(){
			var script = document.createElement("script");
			script.src = "demo.php?callback=WorkAlert";  //重點
			document.body.append(script);
		}
		btn2.onclick = function(){
			var script = document.createElement("script");
			script.src = "demo.php?callback=WorkConsoleLog";  //重點
			document.body.append(script);
		}

	}
</script>
</html>

  demo.php

<?php 
	//默認callback爲WorkAlert
	$callback = isset($_GET['callback']) ? $_GET['callback'] : "WorkAlert";

	$v = 10;
	echo $callback . "(" . $v . ")";
 ?>

  

  下面是jsonp的一個使用示例,參考360的so.com,在輸入框輸入文字時,下拉框提示

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>仿360搜索</title>
	<style>
		body{margin:0; padding:0;}
		#container{margin:0 auto;height:100%; width:800px; text-align:center}
		#div{margin:0 auto; margin-top:30%;}
		#keyword{width:400px; height:40px; border-radius:5px; font-size:25px;}
		#search{width:70px; height:40px; border-radius:10px;border:1px solid grey; line-height:40px; background: #19b955;}
		li {list-style: none; border:1px solid grey; height:40px;border-radius:5px; text-align:left;}
	</style>
</head>
<body>
	<div id="container">
		<div id="div">
			<div>
				<input type="text" name="keyword" id="keyword"> 
				<input type="submit" value="搜索" id="search">
			</div>
			<ol id="opt"></ol>
		</div>
	</div>
</body>
	<script>
		function showData(data){
			var res = data.result;
			var length = res.length;
			var Html = "";
			for(var i = 0; i < length; i++){
				Html += "<li>" + res[i].word + "</li>";
			}
			var opt = document.getElementById("opt");
			opt.innerHTML = Html;
		}
		var keyword = document.getElementById("keyword");
		keyword.oninput = function(){
			var v = this.value;
			var script = document.createElement("script");
			script.src = "https://sug.so.360.cn/suggest?callback=showData&encodein=utf-8&encodeout=utf-8&format=json&fields=word&word=" + v;
			document.body.append(script);
		}
	</script>
</html>

  

 

各類跨域的比較

  使用代理的話,沒啥說的,能夠,沒有什麼問題。

  jsonp:由於jsonp在傳輸數據的時候,都是將callback以及傳輸的數據連在URL上,而URL是有長度限制的,因此jsonp適不適合傳輸大量數據,好比文件之類的請求。

  服務器端設置header的Access-Control-Allow-Origin就適合傳輸大量數據的狀況。

相關文章
相關標籤/搜索