一、Ajax介紹
1、概述
AJAX 是一種在無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。AJAX(Asynchronous Javascript And XML)翻譯成中文就是“異步Javascript和XML”。即:使用Javascript語言與服務器進行異步交互,傳輸的數據為XML(當然,傳輸的數據不只是XML,比如還有JSON)。
- 同步交互:客戶端發出一個請求后,需要等待服務器響應結束后,才能發出第二個請求;
- 異步交互:客戶端發出一個請求后,無需等待服務器響應結束,就可以發出第二個請求。
2、使用場景
- 搜索引擎(谷歌、百度)在用戶輸入某個關鍵字后,會出來一串提示關鍵字;
- 注冊頁面,輸入信息后,如果信息有重復或缺失,會自動提示;
- 當我們只要修改網頁上的部分內容時,單獨局部刷新就可以做到;
3、優缺點
優點:
- AJAX使用Javascript技術向服務器發送異步請求;
- AJAX無須刷新整個頁面;
- 因為服務器響應內容不再是整個頁面,而是頁面中的局部,所以AJAX性能高;
缺點:
- AJAX並不適合所有場景,很多時候還是要使用同步交互;
- AJAX雖然提高了用戶體驗,但無形中向服務器發送的請求次數增多了,導致服務器壓力增大;
- 因為AJAX是在瀏覽器中使用Javascript技術完成的,所以還需要處理瀏覽器兼容性問題,當然,如果使用jQuery的話,則不用考慮這個問題;
二、通過JavaScript實現Ajax
1、過程和請求相關
使用Ajax的過程:
- 創建核心對象,根據不同瀏覽器版本新建核心對象(主要原因是瀏覽器兼容問題);
- 使用核心對象打開與服務器的連接(open方法);
- 發送請求(send方法)
- 注冊監聽,監聽服務器響應。(通過判斷核心對象的就緒程度和狀態碼)
XMLHTTPRequest關鍵知識點:
- open(請求方式, URL, 是否異步)
- send(請求體)
- onreadystatechange,指定監聽函數,它會在xmlHttp對象的狀態發生變化時被調用
- readyState,當前xmlHttp對象的狀態,其中4狀態表示服務器響應結束
- status:服務器響應的狀態碼,只有服務器響應結束時才有這個東東,200表示響應成功;
- responseText:獲取服務器的響應體,文本
- responseXML:獲取服務器的響應體,XML
2、通過JavaScript實現Ajax之無后台交互
<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/ajax/test1.txt",true);
xmlhttp.send();
}
</script>
</head>
<body>
<div id="myDiv"><h2>Let AJAX change this text</h2></div>
<button type="button" onclick="loadXMLDoc()">通過 AJAX 改變內容</button>
</body>
</html>
3、通過JavaScript實現Ajax之與后台交互
通過Django實現,其實唯一區別就是在open的時候,把URL修改成Django的一個URL即可,包括GET和POST方法的URL。
a、urls.py文件
urlpatterns = [
url(r'^ajax_get',views.ajax_get),
url(r'^ajax_post',views.ajax_post)
]
b、views.py文件
def ajax_get(req):
return render(req,'ajax_get.html')
def ajax_post(req):
if req.method=='POST':
print req.POST
else:
print req.GET
return HttpResponse('ajax_post')
c、ajax_get.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試ajax的GET方法</title>
<script>
function loadXMLDoc()
{
var xmlhttp; //定義局部變量xmlhttp
if (window.XMLHttpRequest) //XMLHttpRequet對象用於和服務器交互數據。
{
// IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執行代碼
xmlhttp=new XMLHttpRequest();
}
else{
// IE6, IE5 瀏覽器執行代碼
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//偵聽
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
//POST方法
xmlhttp.open("POST","./ajax_post",true);
//GET方法
//xmlhttp.open("GET","./user_info",true);
//設置頭部信息
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//POST method發送
xmlhttp.send("name=xuequn");
//GET method發送
//xmlhttp.send();
}
</script>
</head>
<body>
<div id="myDiv"><h2>使用 AJAX 修改該文本內容</h2></div>
<button type="button" onclick="loadXMLDoc()">修改</button>
</body>
</html>
d、測試結果
GET方法:
POST方法:
三、通過jQuery實現Ajax
1、兩個快捷API
<1>$.get(url, [data], [callback], [type])
<2>$.post(url, [data], [callback], [type])
url:請求的URL
data:發送的數據
callback:回調函數,前端發送數據給后端之后,后端返回數據給前端,前端接受到的數據
type:text|html|json|script
2、兩個不常用API
getJSON(url,data,callback)
<html>
<head>
<script type="text/javascript" src="/jquery/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("button").click(function(){
$.getJSON("/example/jquery/demo_ajax_json.js",function(result){
$.each(result, function(i, field){
$("p").append(field + " ");
});
});
});
});
</script>
</head>
<body>
<button>獲得 JSON 數據</button>
<p></p>
</body>
</html>
///example/jquery/demo_ajax_json.js
{
"firstName": "Bill",
"lastName": "Gates",
"age": 60
}
getScript( url ,callback ])
jQuery.getScript("http://dev.jquery.com/view/trunk/plugins/color/jquery.color.js",
function(){
$("#go").click(function(){
$(".block").animate( { backgroundColor: 'pink' }, 1000)
.animate( { backgroundColor: 'blue' }, 1000);
});
});
3、最常用的方法
$.ajax()
$(function(){
$('#send').click(function(){
$.ajax({
type: "GET",
url: "test.json",
data: {username:$("#username").val(), content:$("#content").val()},
dataType: "json",
success: function(data){
$('#resText').empty(); //清空resText里面的所有內容
var html = '';
$.each(data, function(commentIndex, comment){
html += '<div class="comment"><h6>' + comment['username']
+ ':</h6><p class="para"' + comment['content']
+ '</p></div>';
});
$('#resText').html(html);
}
});
});
});
ajax的參數
1.url:
要求為String類型的參數,(默認為當前頁地址)發送請求的地址。
2.type:
要求為String類型的參數,請求方式(post或get)默認為get。注意其他http請求方法,例如put和delete也可以使用,但僅部分瀏覽器支持。
3.timeout:
要求為Number類型的參數,設置請求超時時間(毫秒)。此設置將覆蓋$.ajaxSetup()方法的全局設置。
4.async:
要求為Boolean類型的參數,默認設置為true,所有請求均為異步請求。如果需要發送同步請求,請將此選項設置為false。注意,同步請求將鎖住瀏覽器,用戶其他操作必須等待請求完成才可以執行。
5.cache:
要求為Boolean類型的參數,默認為true(當dataType為script時,默認為false),設置為false將不會從瀏覽器緩存中加載請求信息。
6.data:
要求為Object或String類型的參數,發送到服務器的數據。如果已經不是字符串,將自動轉換為字符串格式。get請求中將附加在url后。防止這種自動轉換,可以查看processData選項。
對象必須為key/value格式,例如{foo1:"bar1",foo2:"bar2"}轉換為&foo1=bar1&foo2=bar2。如果是數組,JQuery將自動為不同值對應同一個名稱。例如{foo:["bar1","bar2"]}轉換為&foo=bar1&foo=bar2。
7.dataType:
要求為String類型的參數,預期服務器返回的數據類型。如果不指定,JQuery將自動根據http包mime信息返回responseXML或responseText,並作為回調函數參數傳遞。可用的類型如下:
xml:返回XML文檔,可用JQuery處理。
html:返回純文本HTML信息;包含的script標簽會在插入DOM時執行。
script:返回純文本JavaScript代碼。不會自動緩存結果。除非設置了cache參數。注意在遠程請求時(不在同一個域下),所有post請求都將轉為get請求。
json:返回JSON數據。
jsonp:JSONP格式。使用SONP形式調用函數時,例如myurl?callback=?,JQuery將自動替換后一個“?”為正確的函數名,以執行回調函數。
text:返回純文本字符串。
8.beforeSend:
要求為Function類型的參數,發送請求前可以修改XMLHttpRequest對象的函數,例如添加自定義HTTP頭。在beforeSend中如果返回false可以取消本次ajax請求。XMLHttpRequest對象是惟一的參數。
function(XMLHttpRequest){
this; //調用本次ajax請求時傳遞的options參數
}
9.complete:
要求為Function類型的參數,請求完成后調用的回調函數(請求成功或失敗時均調用)。參數:XMLHttpRequest對象和一個描述成功請求類型的字符串。
function(XMLHttpRequest, textStatus){
this; //調用本次ajax請求時傳遞的options參數
}
10.success:要求為Function類型的參數,請求成功后調用的回調函數,有兩個參數。
(1)由服務器返回,並根據dataType參數進行處理后的數據。
(2)描述狀態的字符串。
function(data, textStatus){
//data可能是xmlDoc、jsonObj、html、text等等
this; //調用本次ajax請求時傳遞的options參數
}
11.error:
要求為Function類型的參數,請求失敗時被調用的函數。該函數有3個參數,即XMLHttpRequest對象、錯誤信息、捕獲的錯誤對象(可選)。ajax事件函數如下:
function(XMLHttpRequest, textStatus, errorThrown){
//通常情況下textStatus和errorThrown只有其中一個包含信息
this; //調用本次ajax請求時傳遞的options參數
}
12.contentType:
要求為String類型的參數,當發送信息至服務器時,內容編碼類型默認為"application/x-www-form-urlencoded"。該默認值適合大多數應用場合。
13.dataFilter:
要求為Function類型的參數,給Ajax返回的原始數據進行預處理的函數。提供data和type兩個參數。data是Ajax返回的原始數據,
type是調用jQuery.ajax時提供的dataType參數。函數返回的值將由jQuery進一步處理。
function(data, type){
//返回處理后的數據
return data;
}
14.dataFilter:
要求為Function類型的參數,給Ajax返回的原始數據進行預處理的函數。提供data和type兩個參數。data是Ajax返回的原始數據,
type是調用jQuery.ajax時提供的dataType參數。函數返回的值將由jQuery進一步處理。
function(data, type){
//返回處理后的數據
return data;
}
15.global:
要求為Boolean類型的參數,默認為true。表示是否觸發全局ajax事件。設置為false將不會觸發全局ajax事件,ajaxStart或ajaxStop可用於控制各種ajax事件。
16.ifModified:
要求為Boolean類型的參數,默認為false。僅在服務器數據改變時獲取新數據。服務器數據改變判斷的依據是Last-Modified頭信息。默認值是false,即忽略頭信息。
17.jsonp:
要求為String類型的參數,在一個jsonp請求中重寫回調函數的名字。該值用來替代在"callback=?"這種GET或POST請求中URL參數里的"callback"部分,
例如{jsonp:'onJsonPLoad'}會導致將"onJsonPLoad=?"傳給服務器。
18.username:
要求為String類型的參數,用於響應HTTP訪問認證請求的用戶名。
19.password:
要求為String類型的參數,用於響應HTTP訪問認證請求的密碼。
20.processData:
要求為Boolean類型的參數,默認為true。默認情況下,發送的數據將被轉換為對象(從技術角度來講並非字符串)以配合默認內容類型"application/x-www-form-urlencoded"。
如果要發送DOM樹信息或者其他不希望轉換的信息,請設置為false。
21.scriptCharset:
要求為String類型的參數,只有當請求時dataType為"jsonp"或者"script",並且type是GET時才會用於強制修改字符集(charset)。通常在本地和遠程的內容編碼不同時使用。
4、Django中設定狀態碼
HttpResponse.status_code = 400 //手動設定返回的狀態碼為400
四、跨域問題
4.1 同源策略機制
瀏覽器有一個很重要的概念——同源策略(Same-Origin Policy)。所謂同源是指,域名,協議,端口相同。不同源的客戶端腳本(javascript、ActionScript)在沒明確授權的情況下,不能讀寫對方的資源。
簡單的來說,瀏覽器允許包含在頁面A的腳本訪問第二個頁面B的數據資源,這一切是建立在A和B頁面是同源的基礎上。
如果Web世界沒有同源策略,當你登錄淘寶賬號並打開另一個站點時,這個站點上的JavaScript可以跨域讀取你的任意賬號的數據,這樣整個Web世界就無隱私可言了。
4.2 問題出現
在同源策略下,比如我在URL01:http://localhost/user_manager/test_jsonp/上獲取https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse的數據時,就是屬於跨域了,此時會出現問題:
代碼如下:重點在open里面的URL,是屬於跨域的行為。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax</title>
</head>
<body>
<div id="mydiv">
<button id="btn">點擊</button>
</div>
</body>
<script type="text/javascript">
window.onload = function() {
var oBtn = document.getElementById('btn');
oBtn.onclick = function() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert( xhr.responseText );
}
};
xhr.open('get', 'https://api.douban.com/v2/book/search?q=javascript&count=1', true);
xhr.send();
};
};
</script>
</html>
上述程序運行時會報錯:
4.3 JSONP介紹
JSONP 是 JSON with padding(填充式 JSON 或參數式 JSON)的簡寫。
JSONP實現跨域請求的原理簡單的說,就是動態創建<script>
標簽,然后利用<script>
的src 不受同源策略約束來跨域獲取數據。
JSONP 由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數。回調函數的名字一般是在請求中指定的。而數據就是傳入回調函數中的 JSON 數據。
其實網上關於JSONP的講解有很多,大概總結如下:
1、一個眾所周知的問題,Ajax直接請求普通文件存在跨域無權限訪問的問題,甭管你是靜態頁面、動態網頁、web服務、WCF,只要是跨域請求,一律不准;
2、不過我們又發現,Web頁面上調用js文件時則不受是否跨域的影響(不僅如此,我們還發現凡是擁有"src"這個屬性的標簽都擁有跨域的能力,比如<script>、<img>、<iframe>);
3、於是可以判斷,當前階段如果想通過純web端(ActiveX控件、服務端代理、屬於未來的HTML5之Websocket等方式不算)跨域訪問數據就只有一種可能,那就是在遠程服務器上設法把數據裝進js格式的文件里,供客戶端調用和進一步處理;
4、恰巧我們已經知道有一種叫做JSON的純字符數據格式可以簡潔的描述復雜數據,更妙的是JSON還被js原生支持,所以在客戶端幾乎可以隨心所欲的處理這種格式的數據;
5、這樣子解決方案就呼之欲出了,web客戶端通過與調用腳本一模一樣的方式,來調用跨域服務器上動態生成的js格式文件(一般以JSON為后綴),顯而易見,服務器之所以要動態生成JSON文件,目的就在於把客戶端需要的數據裝入進去。
6、客戶端在對JSON文件調用成功之后,也就獲得了自己所需的數據,剩下的就是按照自己需求進行處理和展現了,這種獲取遠程數據的方式看起來非常像AJAX,但其實並不一樣。
7、為了便於客戶端使用數據,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。
如果對於callback參數如何使用還有些模糊的話,我們后面會有具體的實例來講解。
4.4 JavaScript實現跨域
1、我們知道,哪怕跨域js文件中的代碼(當然指符合web腳本安全策略的),web頁面也是可以無條件執行的,比如img中的src指定的圖片地址可以是網上任意圖片。
遠程服務器remoteserver.com根目錄下有個remote.js文件代碼如下:
alert('我是遠程文件');
本地服務器localserver.com下有個jsonp.html頁面代碼如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>
</body>
</html>
復制代碼
毫無疑問,頁面將會彈出一個提示窗體,顯示跨域調用成功。
2、現在我們在jsonp.html頁面定義一個函數,然后在遠程remote.js中傳入數據進行調用。
jsonp.html頁面代碼如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
var localHandler = function(data){
alert('我是本地函數,可以被跨域的remote.js文件調用,遠程js帶來的數據是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>
</body>
</html>
remote.js文件代碼如下:
localHandler({"result":"我是遠程js帶來的數據"});
運行之后查看結果,頁面成功彈出提示窗口,顯示本地函數被跨域的遠程js調用成功,並且還接收到了遠程js帶來的數據。很欣喜,跨域遠程獲取數據的目的基本實現了,但是又一個問題出現了,我怎么讓遠程js知道它應該調用的本地函數叫什么名字呢?畢竟是jsonp的服務者都要面對很多服務對象,而這些服務對象各自的本地函數都不相同啊?我們接着往下看。
3、聰明的開發者很容易想到,只要服務端提供的js腳本是動態生成的就行了唄,這樣調用者可以傳一個參數過去告訴服務端“我想要一段調用XXX函數的js代碼,請你返回給我”,於是服務器就可以按照客戶端的需求來生成js腳本並響應了。
這里其實就是跨域的api接口。
看jsonp.html頁面的代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
// 得到航班信息查詢結果后的回調函數
var handleResponse = function(data){
alert(data.total);
};
// 提供jsonp服務的url地址(不管是什么類型的地址,最終生成的返回值都是一段javascript代碼)
var url = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse";
// 創建script標簽,設置其屬性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script標簽加入head,此時調用開始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
</body>
</html>
這里的API接口是豆瓣的一個獲取圖書信息的接口,本質就是在指定位置添加了一個script標簽,且是通過src方式,然后通過callback參數獲取返回值,返回值可以直接使用,這樣就做到了跨域。
4.5 jQuery實現跨域
jQuery實現起來會更加簡單,因為它已經幫你包裝了一層,比如:指定url、數據類型,最重要的是,jQuery里面,直接可以通過success參數調用獲取的結果,當然,也可以使用error設定錯誤提示。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function(){
$.ajax({
type: "get",
async: false,
url: "https://api.douban.com/v2/book/search?q=javascript&count=1",
dataType: "jsonp",
jsonp: "callback",//傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(一般默認為:callback)
jsonpCallback:"flightHandler",//自定義的jsonp回調函數名稱,默認為jQuery自動生成的隨機函數名,也可以寫"?",jQuery會自動為你處理數據
success: function(json){
alert(json.total);
},
error: function(){
alert('fail');
}
});
});
</script>
</head>
<body>
</body>
</html>
4.6 jsonp的總結
1、ajax和jsonp這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個url,然后把服務器返回的數據進行處理,因此jquery和ext等框架都把jsonp作為ajax的一種形式進行了封裝;
2、但ajax和jsonp其實本質上是不同的東西。ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標簽來調用服務器提供的js腳本。
3、所以說,其實ajax與jsonp的區別不在於是否跨域,ajax通過服務端代理一樣可以實現跨域,jsonp本身也不排斥同域的數據的獲取。
4、還有就是,jsonp是一種方式或者說非強制性協議,如同ajax一樣,它也不一定非要用json格式來傳遞數據,如果你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。
總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變這一點!