今天來(lái)記錄一下關(guān)于ajax跨域的一些問(wèn)題。以備不時(shí)之需。
跨域
同源策略限制
同源策略阻止從一個(gè)域上加載的腳本獲取或操作另一個(gè)域上的文檔屬性。也就是說(shuō),受到請(qǐng)求的 URL 的域必須與當(dāng)前 Web 頁(yè)面的域相同。這意味著瀏覽器隔離來(lái)自不同源的內(nèi)容,以防止它們之間的操作。
解決方式
通常來(lái)說(shuō),比較通用的有如下兩種方式,一種是從服務(wù)器端下手,另一種則是從客戶端的角度出發(fā)。二者各有利弊,具體要使用哪種方式還需要具體的分析。
- 服務(wù)器設(shè)置響應(yīng)頭
- 服務(wù)器代理
- 客戶端采用腳本回調(diào)機(jī)制。
方式一
Access-Control-Allow-Origin 關(guān)鍵字只有在服務(wù)器端進(jìn)行設(shè)置才
會(huì)生效。也就是說(shuō)即使再客戶端使用
xmlhttprequest.setHeaderREquest('xx','xx');
也不會(huì)有什么效果。
正常ajax請(qǐng)求
下面來(lái)模擬一下ajax非跨域請(qǐng)求的案例實(shí)現(xiàn)。
test1.html
!DOCTYPE html>
html lang="en">
head>
meta charset="UTF-8">
title>ajax 測(cè)試/title>
/head>
body>
input type="button" value="Test" onclick="crossDomainRequest()">
div id="content">/div>
script>
var xhr = new XMLHttpRequest();
var url = 'http://localhost/learn/ajax/test1.php';
function crossDomainRequest() {
document.getElementById('content').innerHTML = "font color='red'>loading.../font>";
// 延遲執(zhí)行
setTimeout(function () {
if (xhr) {
xhr.open('GEt', url, true);
xhr.onreadystatechange = handle_response;
xhr.send(null);
} else {
document.getElementById('content').innerText = "不能創(chuàng)建XMLHttpRequest對(duì)象";
}
}, 3000);
}
function handle_response() {
var container = document.getElementById('content');
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
container.innerHTML = xhr.responseText;
} else {
container.innerText = '不能跨域請(qǐng)求';
}
}
}
/script>
/body>
/html>
同級(jí)目錄下的test1.PHP內(nèi)容如下:
?php
echo "It Works.";
?>
跨域請(qǐng)求
剛才是HTML文件和php文件都在Apache的容器下,所以沒(méi)有出現(xiàn)跨域的情形,現(xiàn)在把HTML文件放到桌面上,這樣再次請(qǐng)求PHP數(shù)據(jù)的話,就營(yíng)造了這樣一個(gè)“跨域請(qǐng)求”了。
注意看瀏覽器的地址欄信息
再次進(jìn)行訪問(wèn),發(fā)現(xiàn)會(huì)出現(xiàn)下面的錯(cuò)誤信息。
針對(duì)這種情況,比較常見(jiàn)的一個(gè)操作就是設(shè)置Access-Control-Allow-Origin。
格式: Access-Control-Allow-Origin: domain.com/xx/yy.*
如果知道客戶端的域名或者請(qǐng)求的固定路徑,則最好是不使用通配符的方式,來(lái)進(jìn)一步保證安全性。如果不確定,那就是用*通配符好了。
后端開(kāi)發(fā)語(yǔ)言為PHP的時(shí)候可以再文件開(kāi)始處這么設(shè)置:
header("Access-Control-Allow-Origin: *");
如果是ASPX頁(yè)面的話,要這么設(shè)置(Java與之類似):
Response.AddHeader("Access-Control-Allow-Origin", "*");
這時(shí),再次來(lái)訪問(wèn)一下剛才的路徑。
服務(wù)器代理模式
這種方式應(yīng)該算是比較常用的,而且被廣泛采納的一個(gè)方式了。說(shuō)代理有點(diǎn)太過(guò)于書(shū)面化了,其實(shí)就是傳話兒的。來(lái)舉個(gè)小例子:
小明喜歡三班一個(gè)叫小紅的女孩兒,但是不好意思去要人家的QQ,微信號(hào)。然后就托和自己班的女生–小蘭。來(lái)幫自己去要。所以小蘭就相當(dāng)于一個(gè)代理。幫助小明獲取原本不能直接獲取的小紅的聯(lián)系方式。
下面來(lái)舉個(gè)例子說(shuō)明這個(gè)問(wèn)題。
直接的跨域請(qǐng)求
修改一下剛才的URL即可,讓ajax直接去請(qǐng)求其他網(wǎng)站的數(shù)據(jù)。
!DOCTYPE html>
html lang="en">
head>
meta charset="UTF-8">
title>ajax 測(cè)試/title>
/head>
body>
input type="button" value="Test" onclick="crossDomainRequest()">
div id="content">/div>
script>
var xhr = new XMLHttpRequest();
// var url = 'http://localhost/learn/ajax/test1.php';
var url = 'http://api.qingyunke.com/api.php?key=freeappid=0msg=%E5%93%92%E5%93%92';
function crossDomainRequest() {
document.getElementById('content').innerHTML = "font color='red'>loading.../font>";
// 延遲執(zhí)行
setTimeout(function () {
if (xhr) {
xhr.open('GEt', url, true);
xhr.onreadystatechange = handle_response;
xhr.send(null);
} else {
document.getElementById('content').innerText = "不能創(chuàng)建XMLHttpRequest對(duì)象";
}
}, 3000);
}
function handle_response() {
var container = document.getElementById('content');
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
container.innerHTML = xhr.responseText;
} else {
container.innerText = '不能跨域請(qǐng)求';
}
}
}
/script>
/body>
/html>
結(jié)果如下:
啟用代理模式
剛才的HTML頁(yè)面,咱們還是用自己的接口:
url = 'http://localhost/learn/ajax/test1.php';
具體如下:
!DOCTYPE html>
html lang="en">
head>
meta charset="UTF-8">
title>ajax 測(cè)試/title>
/head>
body>
input type="button" value="Test" onclick="crossDomainRequest()">
div id="content">/div>
script>
var xhr = new XMLHttpRequest();
var url = 'http://localhost/learn/ajax/test1.php';
// var url = 'http://api.qingyunke.com/api.php?key=freeappid=0msg=%E5%93%92%E5%93%92';
function crossDomainRequest() {
document.getElementById('content').innerHTML = "font color='red'>loading.../font>";
// 延遲執(zhí)行
setTimeout(function () {
if (xhr) {
xhr.open('GEt', url, true);
xhr.onreadystatechange = handle_response;
xhr.send(null);
} else {
document.getElementById('content').innerText = "不能創(chuàng)建XMLHttpRequest對(duì)象";
}
}, 3000);
}
function handle_response() {
var container = document.getElementById('content');
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 304) {
container.innerHTML = xhr.responseText;
} else {
container.innerText = '不能跨域請(qǐng)求';
}
}
}
/script>
/body>
/html>
然后對(duì)應(yīng)的test1.php應(yīng)該幫助我們實(shí)現(xiàn)數(shù)據(jù)請(qǐng)求這個(gè)過(guò)程,把“小紅的聯(lián)系方式”要到手,并返回給“小明”。
?php
$url = 'http://api.qingyunke.com/api.php?key=freeappid=0msg=hello%20world.';
$result = file_get_contents($url);
echo $result;
?>
下面看下代碼執(zhí)行的結(jié)果。
jsonp方式
JSONP(JSON with Padding) 靈感其實(shí)源于在HTML頁(yè)面中script標(biāo)簽內(nèi)容的加載,對(duì)于script的src屬性對(duì)應(yīng)的內(nèi)容,瀏覽器總是會(huì)對(duì)其進(jìn)行加載。于是:
克服該限制更理想方法是在 Web 頁(yè)面中插入動(dòng)態(tài)腳本元素,該頁(yè)面源指向其他域中的服務(wù) URL 并且在自身腳本中獲取數(shù)據(jù)。腳本加載時(shí)它開(kāi)始執(zhí)行。該方法是可行的,因?yàn)橥床呗圆蛔柚箘?dòng)態(tài)腳本插入,并且將腳本看作是從提供 Web 頁(yè)面的域上加載的。但如果該腳本嘗試從另一個(gè)域上加載文檔,就不會(huì)成功。
實(shí)現(xiàn)的思路就是:
在服務(wù)器端組裝出客戶端預(yù)置好的json數(shù)據(jù),通過(guò)回調(diào)的方式傳回給客戶端。
原生實(shí)現(xiàn)
!DOCTYPE html>
html lang="en">
head>
meta charset="UTF-8">
title>ajax 測(cè)試/title>
script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.0.js" type="text/javascript">/script>
/head>
body>
input type="text" name="talk" id="talk">
input type="button" value="Test" id="btn">
div id="content">/div>
script type="text/javascript">
function jsonpcallback(result) {
for(var i in result) {
alert(i+":"+result[i]);
}
}
var JSONP = document.createElement("script");
JSONP.type='text/javascript';
JSONP.src='http://localhost/learn/ajax/test1.php?callback=jsonpcallback';
document.getElementsByTagName('head')[0].appendChild(JSONP);
/script>
/body>
/html>
服務(wù)器端test1.php內(nèi)容如下:
?php
$arr = [1,2,3,4,5,6];
$result = json_encode($arr);
echo "jsonpcallback(".$result.")";
?>
需要注意的是最后組裝的返回值內(nèi)容。
來(lái)看下最終的代碼執(zhí)行效果。
JQuery方式實(shí)現(xiàn)
采用原生的JavaScript需要處理的事情還是蠻多的,下面為了簡(jiǎn)化操作,決定采用jQuery來(lái)代替一下。
!DOCTYPE html>
html lang="en">
head>
meta charset="UTF-8">
title>ajax 測(cè)試/title>
script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.0.js" type="text/javascript">/script>
/head>
body>
input type="text" name="talk" id="talk">
input type="button" value="Test" id="btn">
div id="content">/div>
script type="text/javascript">
function later_action(msg) {
var element = $("div>font color='green'>"+msg+"/font>br />/div>");
$("#content").append(element);
}
$("#btn").click(function(){
// alert($("#talk").val());
$.ajax({
url: 'http://localhost/learn/ajax/test1.php',
method: 'post',
dataType: 'jsonp',
data: {"talk": $("#talk").val()},
jsonp: 'callback',
success: function(callback){
console.log(callback.content);
later_action(callback.content);
},
error: function(err){
console.log(JSON.stringify(err));
},
});
});
/script>
/body>
/html>
相應(yīng)的,test1.php為了配合客戶端聊天的需求,也稍微做了點(diǎn)改變。
?php
$requestparam = isset($_GET['callback'])?$_GET['callback']:'callback';
// 青云志聊天機(jī)器人接口: http://api.qingyunke.com/api.php?key=freeappid=0msg=hello
// 接收來(lái)自客戶端的請(qǐng)求內(nèi)容
$talk = $_REQUEST['talk'];
$result = file_get_contents("http://api.qingyunke.com/api.php?key=freeappid=0msg=$talk");
// 拼接一些字符串
echo $requestparam . "($result)";
?>
最后來(lái)查看一下跨域的效果吧。
總結(jié)
至此,關(guān)于簡(jiǎn)單的ajax跨域問(wèn)題,就算是解決的差不多了。對(duì)我個(gè)人而言,對(duì)于這三種方式有一點(diǎn)點(diǎn)自己的看法。
- 服務(wù)器設(shè)置Access-Control-Allow-Origin的方式適合信用度高的小型應(yīng)用或者個(gè)人應(yīng)用。
- 代理模式則比較適合大型應(yīng)用的處理。但是需要一個(gè)統(tǒng)一的規(guī)范,這樣管理和維護(hù)起來(lái)都會(huì)比較方便。
- JSONP方式感覺(jué)還是比較雞肋的(有可能是我經(jīng)驗(yàn)還不足,沒(méi)認(rèn)識(shí)到這個(gè)方式的優(yōu)點(diǎn)吧(⊙﹏⊙)b)。自己玩玩知道有這么個(gè)東西好了。維護(hù)起來(lái)實(shí)在是優(yōu)點(diǎn)麻煩。
參考鏈接:
Ajax跨域請(qǐng)求: https://www.jb51.net/article/72703.htm
服務(wù)器端跨域設(shè)置: https://www.jb51.net/article/104442.htm
Ajax高級(jí)筆記: https://www.jb51.net/article/116878.htm
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:- 關(guān)于Ajax跨域問(wèn)題及解決方案詳析
- Ajax跨域問(wèn)題及解決方案(jsonp,cors)
- 解決前端跨域問(wèn)題方案匯總
- ajax請(qǐng)求前端跨域問(wèn)題原因及解決方案