一,XMLHttpRequest對象
IE5是最先引入XHR對象的瀏覽器,XHR對象是經過MSXML庫中的一個ActiveX對象實現的
使用MSXML庫中的XHR對象,編寫一個函數以下javascript
function ceateXHR(){ if(typeof argument.callee.activeXString != "String"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len; for(i=0;len=versions.length;i<len;i++){ try{ new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; }catch(ex){ //跳過 } } } return new ActiveXObject(argument.callee.activeXString); }
IE7+,Firefox,Opera,CHrome和safari中支持原生XHR對象,
var xhr = new XMLHttpRequest();
爲了兼容以上全部,使用下列函數php
function createXHR(){ if(typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); }else if(typeof ActiveXobject != "undefined"){ if(typeof argument.callee.activeXString != "String"){ var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i.len; for(i=0,len=versions.length;i<len;i++){ try{ new ActiveXObject(versions[i]); argument.callee.activeXString = versions[i]; break; }catch(ex){ //跳過 } } } return new ActiveXObject(argument.callee.activeXString); }else{ throw new Error("No XHR object available"); } }
建立對象,var xhr = createXHR();
1,XHR的用法
open(請求的類型(get或post),請求的URL,是否異步發送的布爾值),
要點:URL相對於執行代碼的當前頁面(可使用絕對路徑),open方法不會真的發送請求只是啓動一個請求以備發送
send(做爲請求主體發送的數據),發送特定的請求,能夠傳人null,調用函數後請求會被派送到服務器
若是請求時同步的,js代碼會等到服務器響應以後執行,響應的數據會自動填充XHR對象的屬性
responseText:做爲響應主體被返回的文本
responseXML:若是響應的內容類型是text/xml,application/xml,這個屬性會保存包含響應數據的XML DOM文檔
status:響應的http狀態
statusText:http狀態的說明
響應後,首先檢查status屬性,http狀態200表示成功,此時responseText屬性的內容就緒,內容類型正確的同時,responseXML也能夠訪問了
狀態304表示請求的資源沒有被修改,能夠直接使用瀏覽器中緩存的版本,
xhr.open("get","example.text",false);
xhr.send(null);
if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
alert(xhr.responseText);
}else{
alert("Request was unsuccessful:" + xhr.status);
}
通常狀況下使用異步請求,此時js會繼續執行沒必要等待服務器的響應,此時檢測XHR的readyState屬性,屬性值以下
0:未初始化,還沒有調用open方法
1:啓動,調用了open方法,未調用send方法
2,發送,調用了send方法,未接受到響應
3,接收,接收到部分響應數據
4,完成,接收到所有的響應數據,並且在客戶端使用了
只要readyState屬性的值發生變化就會觸發readystatechange事件,能夠利用這個事件檢測每次狀態後的readyState的值
必須在open()以前指定onreadystatechange事件處理程序才能保證跨瀏覽器兼容html
var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304{ alert(xhr.responseText); }else{ alert("Request was unsuccessful:" + xhr.status); } } }; xhr.open("get","example",true); xhr.send(null);
響應以前調用xhr.abort(),能夠取消異步請求,因爲內存緣由,不建議重用XHR對象
2,HTTP頭部信息
每一個http請求和響應都會帶有相應的頭部信息,XHR對象也提供了操做這兩種頭部信息的方式
發送XHR請求的同時,會發送下列頭部信息
Accept:瀏覽器可以處理的內容類型
Accept-Charset:瀏覽器可以顯示的字符集
Accept-Encoding:瀏覽器可以處理的壓縮編碼
Accept-Language:瀏覽器當前設置的語言
Connection:瀏覽器與服務器之間鏈接的類型
Cookie:當前頁面設置的任何cookie
Host:發送請求的頁面所在的域
Referer:發送請求的頁面的URL
User-Agent:瀏覽器的用戶代理字符串
setRequestHeader(頭部字段的名稱,頭部字段的值)方法能夠自定義設置頭部信息,在open和send方法以前調用此方法html5
var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){ alert(xhr.reponseText); }else{ alert("Request was unsuccessful :" + xhr.status); } } }; xhr.open("get","example.php",true); xhr.setRequestHeader("MyHeader","MyValue"); xhr.send(null);
調用XHR對象的getResponseHeader()方法,傳人頭部字段名稱,能夠返回相應的響應頭部信息
getAllResponseHeaders()方法,取得一個包含全部頭部信息的長字符串
var myheader = xhr.getResponseHeader("myHeader");
var allheader = xhr.getAllResponseHeader();
服務器端,能夠利用頭部信息向瀏覽器發送額外的,結構化的數據,沒有自定義的狀況下,getAllResponseHeader()方法會返回以下
Date:sun,14 Nov 2004 18:04:03 GMT
Server: Apache/1.3.29(Unix)
Vary:Accept
X-Powered-By:PHP/4.3.8
Connection:close
Content-Type:text/html;charset=iso-8859-1
3,GET請求
用於向服務器查詢某些信息,能夠將查詢字符串追加到URL末尾,
查詢字符串,每一個參數的名稱和值使用encodeURIComponent()進行編碼,並且全部名值對使用&分隔
xhr.open("get","example.php?name1=value&name2=value2",true);
使用下面函數向現有的URL末尾添加查詢字符串參數
function addURLParam(url,name,value){
url += (url.indexof("?") == -1 ? "?" : &);
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
使用下面函數來構建請求URL示例
var url = "example.php";
url = addURLParam(url,"name","Nicholas");
xhr.open("get",url,true);
4,POST請求
用於向服務器發送應該被保存的數據,
post請求將數據做爲請求的主體提交,請求的主體能夠包含很是多的數據,格式不限
發送post請求,要向send()方法中傳人某些數據,能夠是XML DOM文檔,可使字符串
XHR模仿表單提交,首先將Content-type頭部信息設置爲application/x-www-form-urlencoded,而後以適當的格式建立一個字符串,可使用serialize() 函數來建立字符串
function submitData(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readystate == 4){
if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){
alert(xhr.responseText);
}else{
alert("Request was unsuccessful:" + xhr.status);
}
}
};
xhr.open("post","postexample.php",true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
var form = document.getElementById("user-info");
xhr.send(serialize(form));
}
php文件經過$_POST取得提交的數據
<?php
header("Content-type:text/plain");
echo <<<EOF
Name:{$_POST["user-name"]}
Email:{$_POST['user-email']}
EOF;
?>
若是不設置Content-type頭部信息,那麼發送給服務器的數據不會出如今$_POST超級全局變量中,
要訪問一樣的數據,使用$HTTP_RAW_POST_DATA
二,XMLHttpRequest2級
1,FormData
爲了實現表單數據的序列化,FormData類型爲序列化表單以及建立與表單格式相同的數據
var data = new FormData();
data.append("name","Nicholas");
append()方法接收兩個參數,鍵值對,
經過向FormData構造函數中傳人表單元素,能夠用表單元素的數據預先向其中填入鍵值對
var data = new FormData(document.forms[0]);
建立了FormData實例,直接傳給XHR的send()方法java
var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful :" + xhr.status); } } }; xhr.open("post","postexample.php",true); var form = document.getElementById("user-info"); xhr.send(new FormData(form));
XHR對象可以識別傳人的數據類型是FormData的實例,並配置適當的頭部信息
2,超時設定
XHR對象的timeout屬性,表示請求在等待響應多少時間以後就終止,給timeout屬性設置一個值,在超過了這個值後,
就會調用ontimeout事件處理程序web
var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ try{ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful:" + xhr.status); } }catch(ex){ //假設由ontimeout事件處理程序處理 } } }; xhr.open("get","timeout.php",true); xhr.timoeout = 1000; xhr.ontimeout = function(){ alert("Request did not return in a second."); }; xhr.send(null);
3,overrideMimeType()方法
Firefox最先引入了overrideMimeType()方法,用於重寫XHR響應的MIME類型,能夠把響應當作XML而非純文原本處理
var xhr = createXHR();
xhr.open("get","text.php",true);
xhr.overrideMimeType("text/XML");
xhr.send(null);
三,進度事件
Progress Events規範定義了與客戶端服務器通訊有關的事件
loadstart:在接收到響應數據的第一個字節時觸發
progress:在接收響應期間持續不斷的觸發
error:在請求發生錯誤時觸發
abort:由於調用abort()方法而終止鏈接時觸發
load:在接收到完整的響應數據時觸發
loadend:在通訊完成,或者觸發了error,abort,load事件後觸發(還沒有被任何瀏覽器支持)
1,load事件
firefox引入了load事件,用於替代readystatechange事件
響應接收完畢後觸發load事件,沒有必要去檢測readyState屬性了,
onload會接收到event對象,target屬性就指向XHR對象,能夠訪問到XHR對象的全部方法和屬性
對於不支持的瀏覽器算法
var xhr = createXHR(); xhr.onload = function(){ if((xhr.status >=200 && xhr.status <= 300) || xhr.status == 304){ alert(xhr.responseText); }else{ alert("Request was unsuccessful:" + xhr.status); } }; xhr.open("get","alertEvents.php",true); xhr.send(null);
2,progress事件
事件在瀏覽器接收數據期間週期性觸發,
onprogress事件處理程序會接收到一個evnet對象,其target屬性是XHR對象
包含額外的三個屬性:lengthComputable(進度信息是否可用的布爾值),position(已經接收的字符數),totalSize(根據Content-Length響應頭部肯定的預期字節數)chrome
var xhr = createXHR(); xhr.onload = function(){ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){ alert(xhr.responsText); }else{ alert("Request was unsuccessful" + xhr.status); } }; xhr.onprogress = function(event){ var divStatus = document.getElementById("status"); if(event.lengthComputable){ divStatus.innerHTML = "Recevied " + event.position + "Of" + event.totalSize + "bytes"; } }; xhr.open("get","alertEvent.php",true); xhr.send(null);
四,跨源資源共享(CORS)
思想是使用自定義的http頭部,讓瀏覽器與服務器進行溝通,從而決定請求或響應的成功與否
請求的origin頭部,包含請求頁面的源信息(協議,域名,端口),
若是服務器認爲是可接受的,在Origin-Control-Allow-Origin頭部中回發相同的源信息,若是是公共資源,能夠回發*
1,IE對CORS的實現
IE8引入XDR類型,相似XHR對象,但能實現安全可靠的跨域通訊,區別以下
cookie不會隨請求發送,也不會隨響應返回
只能設置請求頭部信息中的Content-type字段
不能訪問響應頭部信息
只支持GET和POST請求
XDR使用方法,建立一個XDomainRequest的實例,調用open()方法,再調用send()方法,
open()方法只接收兩個參數,請求類型的UR,XDR請求都是異步的,
請求返回以後會觸發load事件,響應的數據保存在responseText屬性中
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.open("get","http://www.somewhere-else.com/page/");
xdr.send(null);
接收到響應後,只能訪問響應的原始文本,沒有辦法肯定響應的狀態代碼,只要響應有效就會會觸發load事件,
失敗就會觸發error事件,遺憾的是,除了錯誤自己外,沒有其餘信息可用,所以惟一可以肯定的就是請求未成功,
檢測錯誤能夠指定一個onerror事件處理程序
var xdr = new XDomainRequest();
xdr.onload = function(){alert(xdr.responseText)};
xdr.onerror = function(){alert("An error occurred.")};
xdr.open("get","http://www.somewhere-else.com/page/");
xdr.send(null);
返回請求前調用abrot()方法會終止請求
一樣支持timeout屬性和ontimeout事件
var xdr = new XDomainRequest();
xdr.onload = function(){alert(xdr.responseText);};
xdr.onerror = function(){alert("An Error Occurred");};
xdr.timeout = 1000;
xdr.ontimeout = function(){alert("Request took too long.");};
xdr.open("get","http://www.somewhere-else.com/page/");
xdr.send(null);
爲了支持POST請求,XDR對象提供了contenType屬性,用來表示發送數據的格式
var xdr = new XDomainRequest();
xdr.onload = function(){};
xdr.onerror = function(){};
xdr.open("post","http://www.somewhere-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");
2,其餘瀏覽器對CORS的實現
經過XMLHttpRequest對象實現對CORS的原生支持,請求另外一個域中的資源,使用標準的XHR對象並在open方法中傳人絕對的URLjson
var xhr = createXHR(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304){ alert("xhr.responseText"); }else{ alert("Request was unsuccessful:" + xhr.status); } } }; xhr.open("get","htt://www.somewhere-else.com/page/",true); xhr.send(null);
跨域XHR對象有限制,
不能使用setRequestHeader()設置自定義頭部
不能發送和接受cookie
調用getAllResponseHeader()方法總會返回空字符串
3,Preflighted Request
透明服務器驗證機制支持開發人員使用自定義的頭部,
使用下列高級選項來發送請求時,就會向服務器發送一個Preflight請求,使用OPTIONS方法,發送下列頭部
Origin:與簡單的請求相同
Access-Control-Request-Method:請求自身使用的方法
Access-Control-Request-Header:自定義的頭部信息,多個頭部用逗號分隔
Origin:http://www.nczonline.net
Access-Control-Request-Method:POST
Access-Control-Request-Header:NCZ
發送這個請求後,服務器能夠決定是否容許這種類型的請求,服務器經過在響應中發送以下頭部與瀏覽器進行溝通
Access-Control-Allow-Origin:與簡單請求相同
Access-Control-Allow-Method:容許的方法,多個方法以逗號分隔
Access-Control-Allow-Header:容許的頭部,多個頭部以逗號分隔
Access-Control-Max-Age:應該將這個Prefight請求緩存多長時間(秒)
Access-Control-Allow-Origin:http://www.nczonline.net
Access-Control-Allow-Method:POST,GET
Access-COntrol-Allow-Header:NZC
Access-Control-Max-Age:1728000
第一次發送這種請求時會多一個http請求
4,帶憑據的請求
默認狀況下跨源請求不會提供憑據(cookie,HTTP認證,客戶端SSL證實)
經過將withCredentials屬性設置爲true,能夠指定某個請求應該發送憑據
若是服務器端接收帶憑據的請求,會使用Access-Control-Allow-Credentials:true來響應
若是服務器的響應中沒有這個頭部,瀏覽器就不會把響應交給javascript,responseText中將是空字符串status的值爲0,調用onerror事件處理程序
5,跨瀏覽器的CORS跨域
function createCORSRequest(method,url){ var xhr = new XMLHttpRequest(); if("withCredentials" in xhr){ xhr.open(method,url,true); }else if(typeof XDomainRequest != "undefined"){ vxhr = new XDomainRequest(); xhr.open(method,url); }else{ xhr = null; } return xhr; } var request = createCORSRequest("get","http://www.somewhere-else.com/page/"); if(request){ request.onload = function(){ // }; request.send(); }
firefox,safari,chrome中的XMLHttpRequest對象和IE中XDomainRequest對象相似,共有屬性方法以下
abort(),用於中止正在進行的請求
onerror(),用於替代onreadystatechange()檢測錯誤
onload(),用於替代onreadystatechange()檢測成功
responseText(),用於取得響應內容
send(),用於發送請求
五,其餘跨域技術
1,圖像Ping
使用img標籤,動態的建立圖像,使用onload和onerror事件處理程序來肯定是否接收到了響應
圖形Ping是與服務器進行簡單,單向,的跨域通訊的一種方式
請求的數據經過查詢字符串形式發送的,響應的是任意內容,一般是像素圖或204響應
var img = new Image();
img.onload = img.onerror = function(){};
img.src = "http://www.example.com/test?name=Nicholas";
請求從設置src屬性那一刻開始,請求中發送了name參數
圖像Ping用於跟蹤用戶點擊頁面或動態廣告曝光次數,
兩個缺點,只能發送get請求,沒法訪問服務器的響應文本,只能單向通訊
2,JSONP
被包含在函數調用中的JSON,
callback({"name":"Nicholas"});
JSONP包含兩部分,回調函數和數據,回調函數是當響應到來時應該在頁面中調用的函數,函數名通常在請求中指定
數據就是傳人回調函數中的JSON數據,例如:http://freegeoip.net/json/?callback=handleResponse,指定的函數名爲handleResponse()
JSONP是經過動態script元素使用的,使用時能夠爲src屬性指定一個跨域URL
function handleResponse(response){
alert("you're at IP address" + resopnse.ip + ",which is in" + response.city + "," + response.region_name);
}
var script = document.createElement(script);
script.src="http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild);
能夠直接訪問響應文本,支持在瀏覽器與服務器之間雙向通訊,但不能保證安全,肯定JSONP請求是否失敗不是很容易
html5給script提供onerror事件處理程序,還沒有獲得任何任何瀏覽器支持,爲此常經過設置計時器檢測指定時間內是否收到了響應
3,Comet
Ajax是一種從頁面向服務器請求數據的技術,Comet是一種服務器向頁面推送數據的技術
Comet可以讓信息近乎實時的被推送到頁面上,很是適合處理體育比賽的分數和股票報價
兩種實現Comet的方式:長輪詢和流
長輪詢:頁面發起一個到服務器的請求,而後服務器一直保持鏈接打開,直到有數據可發送,
發送完數據以後,瀏覽器關閉鏈接,隨即又發起一個到服務器的新請求,這一過程在頁面打開期間持續不斷
長輪詢是服務器等待發送響應後發送數據
HTTP流:瀏覽器向服務器發送一個請求,服務器保持鏈接打開,而後週期性向瀏覽器發送數據
DOM瀏覽器中,經過監聽readystatechange事件及檢測readyState的值是否爲3,利用XHR對象實現流
function createStreamingClient(url,progress,finished){ var xhr = new XMLHttpRequest(),received = 0; xhr.open("get",url,true); xhr.onreadystatechange = function(){ var result; if(xhr.readyState == 3){ //只取得最新數據並整理計數器 result = xhr.responseText.substring(received); received += result.length; //調用progress回調函數 progress(result); }else if(xhr.readyState == 4){ finished(xhr.responseText); } }; xhr.send(null); return xhr; } var client = createStreamingClient("streaming.php",function(data){ alert("Received:" + data);},function(data){ alert("Done!"); });
4,服務器發送事件1)SSE API 建立到服務器的單向鏈接,服務器經過這個鏈接能夠發送任意數量的數據, 服務器響應的MIME類型必須是text/event-stream,支持長短輪詢,和http流 首先建立一個新的EventSource對象,並傳進一個入口點: var source = new EventSource("Myevent.php");url要與建立對象的頁面同域 EventSource實例有個readyState屬性,0表示正鏈接到服務器,1表示打開了鏈接,2表示關閉了鏈接 還有open事件,在創建鏈接時觸發,message事件,在服務器接收到新事件時觸發,error事件,在沒法創建鏈接時觸發 服務器發回的數據以字符串形式保存在event.data中 默認狀況下,EventSource對象會保持與服務器的活動鏈接,鏈接斷開會從新鏈接 強制斷開使用close()方法,source.close()2)事件流 服務器事件會經過一個持久的http響應發送,這個響應的MIME類型爲text/event-stream 響應的格式爲純文本,最簡單的狀況是每一個數據項前有data 對於多個連續的以data開頭的數據行,將做爲多段數據解析,每一個值之間有一個換行分隔符 只有包含data:的數據行後有空行,纔會觸發messge事件, 經過id:前綴能夠給特定的事件指定一個關聯的ID,位於data:行前面和後面皆可 設置了ID,EventSource對象會跟蹤上一次觸發的事件,鏈接斷開時,會向服務器發送一個包含名爲last-Event-ID的特殊HTTP頭部的請求,以便服務器知道 下一次觸發的事件5,Web Socket 在一個單獨的持久的鏈接上提供全雙工,雙向通訊 一個http請求發送到服務器已發起鏈接,取得服務器響應後,創建的鏈接使用http升級從http協議交換爲web Socket協議 ws://;wss:// 1)Web Socket API 首先實例一個WebSocket對象並傳人要鏈接的URL var socket = new WebSocket("ws://www.example.com/sever.php"); 必須給websocket構造函數傳人絕對的URL, 瀏覽器在實例化websocket對象後,立刻嘗試建立鏈接,readyState屬性 WebSocket.OPENING(0):正在創建鏈接 WebSocket.OPEN(1):已經創建鏈接 WebSocket.CLOSING(2):正在關閉鏈接 WebSocket.CLOSE(3):已經關閉鏈接 沒有readystatechange事件,readyState永遠是從0開始的 關閉WebSocket,使用close方法 2)發送和接收數據 send()方法 var socke = new WebSocket("ws://www.example.com/server.php"); socket.send("hello world"); websocket只能發送純文本數據,對於複雜的結構,將數據序列化爲JSON字符串,使用stringify()方法 當服務器向客戶端發來消息時,WebSocket對象會觸發message事件,這個message事件與其餘傳遞消息的協議相似, 也是把返回的數據保存在event.data屬性中 socket.onmessage = function(event){ var data = event.data; //處理數據 } 3)其餘事件 open:在成功創建鏈接時觸發 error:在發生錯誤時觸發,鏈接不能持續 close:在鏈接關閉時觸發 WebSocket對象不支持DOM2級事件監聽器,必須使用DOM0級語法分別定義每一個事件處理程序 var socket = new WebSocket("ws://www.example.com/server.php"); socket.onopen = function(){}; socket.onerror = function(){}; socket.onclose = function(){}; 只有close事件對象包含額外的三個屬性 wasClean:布爾值,表示鏈接是否已經明確的關閉 code:服務器返回的數據狀態碼 reason:字符串包含服務器發回的消息6,SSE與Web Socket SSE,支持單向的從服務器讀取數據 WebSocket,支持雙向通訊 SEE和XHR組合也能夠實現雙向通訊六,安全 確保XHR訪問的URl安全,通行的作法是驗證發送請求者是否有權限訪問相應的資源 要求以SSL鏈接來訪問能夠經過XHR請求的資源 要求每一次請求都附帶通過相應算法計算獲得的驗證碼 要求發送POST而不是GET請求(對CSRF攻擊沒有做用) 檢查來源URL以確保是否可信,(對CSRF攻擊沒有做用,來源記錄很容易僞造) 基於cookie信息進行驗證(對CSRF攻擊沒有做用,容易被僞造)