XMLHttpRequest


  • 瀏覽器在XMLHttpRequest類上定義了他們的HTTP API
  • XMLHttpRequest類的每個實例表示了一個獨立的請求/相應對
  • HTTP請求包括:HTTP請求方法(post還是get),請求URL,一個可選的請求頭集合,一個可選的請求主體
  • HTTP響應包括:一個數字或者文字組策划過的狀態嗎(顯示請求的成功失敗),一個響應頭集合,響應主體
  • 步驟:
  1. 實例化XMLHttpRequest對象 :
var request=new XMLHttpRequest();

  2.指定請求

request.open("method","url");

第一個參數:指定HTTP方法或動作,“GET”適用於當URL完全猴子腚請求資源,當請求對服務器沒有任何副作用以及當服務器的響應是可緩存的。

                 “POST"常用語form表單,請求主體中包含額外數據(表單數據)且這些數據常存儲到服務器上的數據庫中(副作用)

第二個參數:url,如果使用絕對url,協議主機和端口常匹配所在文檔的對象內容:跨域的請求常常會報錯

  3.設置請求頭

request.setRequestHeader(“Content-Type”,"text/plain");

對相同請求頭調用多次setRequestHeader並不會覆蓋,而是賦予多次的值。

   /*以post方式發送數據給服務器*/    
function postMessage(msg){
        var request=new XMLHttpRequest();
        request.open("post","handlerPost");
        request.setRequestHeader('Content-Type','text/plain;charset=UTF-8');
        request.send(msg);
    }

一個HTTP響應:狀態嗎,響應頭集合,響應主體。

XMLHttpRequest對象的屬性和方法中可以獲得以上值:

1.status和statusText屬性以數字和純文本的格式趕回HTTP狀態碼。200:請求成功;404:URL不能匹配服務器上任何資源。

2.getResponseHeader()和getResponseHeaders()查詢響應頭

3.響應主體沖responseText屬性中得到文本形式,從responseXML得到Document形式

 

XMLHttpRequest對象通常異步使用:發送請求之后,send()方法立即返回,之后再返回響應。為了在響應准備就緒是得到通知,必須使用readystatechange事件。

XMLHttpRequest的readyState屬性指定了HTTP請求的狀態,4:響應完成,3:接收到響應主體,2:接受到頭信息。

    //postMessage("This is post message!");
    //發送一個HTTP    GET請求以獲得指定URL內容
    //當響應成功到達,驗證它是否是純文本
    //如果是,把它傳遞給指定回調函數
    function getText(url,callback){
        var request=new XMLHttpRequest();
        request.open("GET",url);
        request.onreadystatechange=function(){
            //如果請求完成,則它是成功的
            if(request.readyState===4 && request.status===200){
                var type=request.getResponseHeader("Content-Type");
                if(type.match(/^text/))  //確保相應是文本
                    callback(request.responseText);
            }
        }
    }

同步響應:

    //同步響應
    //發起同步的HTTP GET請求以獲得指定URL的內容
    //返回響應文本,或如果請求不成功或響應不是文本就報錯。
    function getTextSync(url){
        var request=new XMLHttpRequest();
        request.open("GET",url,false); //把fasle作為第三個參數傳遞給open(),那么send()方法將阻塞直到請求完成
        request.send(null);

        if(request.status!==200) throw new Error(request.statusText);

        var type=request.getResponseHeader('Content-Type');
        if(!type.match(/^text/)) throw new Error("Expected textual respomse;got:"+type);

        return request.responseText;
    }

響應解碼

 ///發送HTTP GET響應以獲取指定URL內心哦那個
    //當響應到達時候,把它解析后的XML Document對象,解析后的JSON對象或字符串形式傳遞給回調
    function get(url,callback){
        var request=new XMLHttpRequest();
        request.open('GET',url);
        request.onreadystatechange=function(){
            if(request.readyState===4 && request.status===200){
                var type=request.getResponseHeader("Content-Type");
                if(type.indexOf("xml") !== -1 && request.responseXML){ 
                    callback(request.responseXML); //Document對象響應
                }else if(type=== 'application/json'){
                    callback(JSON.parse(request.responseText)) //JSON響應
                }else {
                    callback(request.responseText);
                }
            }
        };
        request.send(null);
    }

不用考慮特殊編碼的另一個相應類型application/javascript亦或text/javascript,如果你想使用XMLHttpRequest請求Javascript腳本,然后使用eval()的話,因為<script>能發起跨域請求(JSONP就是運用了這一點),但是XMLHttpRequest API則禁止。

 

web服務器也可使用二進制數據(例如,圖片文件)相應HTTP請求。

    //使用XMLHttpRequest下載Blob
    //以Blob的形式獲取URL指定的內容,並將其傳遞給指定的回調函數
    function getBlob(url,callback){
        var xhr=new XMLHttpRequest(); //創建一個新的XHR對象
        xhr.open("GET",url); //指定要獲取內容的URL
        xhr.responseType="blob"; //以BLOB形式
        xhr.onload=function(){ //onload比onstatechange更容易
            callback(xhr.response); //注意,這里是response不是.responseText
        };
        xhr.send(null);
    }

服務器響應的正常解碼是建立在服務器端發送的"Content-type"和MIME類型上的。

如果服務器端發送過的是XML文檔,但是設置“Content-type”錯誤,將導致XMLHttpRequest對象將使用錯誤的編碼來相應。

所以overrideMimeType方法將重寫服務端返回的MIME類型,注意該方法要在send()之前調用

    //無論服務器端設置的Content-type是什么類型,豆漿把它當作文本類型來處理
    request.overrideMimeType("text/plain;charset=utf-8");

表單編碼的請求

//將對象{find:"pizza",zipcode:01234,radius:"1km"}變成表單編碼的格式 find=pizza&zipcode=01234$radius=1km 的形式
    function encodeFormData(data){
        if(!data) return "";
        var pairs=[];
        for(var name in data){
            if(!data.hasOwnProperty(name)) continue; //跳過繼承方法
            if(typeof  data[name] === "function") continue;//跳過方法
            var value=data[name].toString();
            name=encodeURIComponent(name.replace("%20","+"));
            value=encodeURIComponent(value.replace("%20","+"));
            pairs.push(name+"="+value);
        }
        return pairs.join('&');
    }

HTTp POST請求包含一個請求主體,它包含客戶端傳遞給服務端的數據。表單數據編碼格式的正確MIME類型:

當POST方法提交這種順序的表單數據時,必須設置“Content-type”請求頭為這個值。

//使用表單數據發起一個HTTP POST請求
function postData(url,data,callback){
        var request=new XMLHttpRequest();
        request.open("POST",url);
        request.onreadystatechange=function(){
            if(request.readyState===4 && callback)
                callback(request);
        };
        request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        request.send(encodeFormData(data)); //發送表單編碼數據
    }

HTTP GET請求沒有請求主體:

 //使用表單編碼數據發起get請求
    function getData(url,data,callback){
        var request=new XMLHttpRequest();
        request.open("GET",url+"?"+encodeFormData(data));
        request.onreadystatechange=function(){
            if(request.readyState===4 && callback) callback(request);
        };
        request.send(null);
    }

JSON編碼請求

    //使用JSON編碼主題發起HTTP POST請求
    function postJSON(url,data,callback){
        var request=new XMLHttpRequest();
        request.open("POST",url);
        request.onreadystatechange=function(){
            if(request.readyState===4&&callback)
                callback(request);
        };
        request.setRequestHeader("Content-Type","application/json"); 
        request.send(JSON.stringify(data));
    }

HTTP進度事件

在XHR2規范中,XMLHttpRequest對象在請求的不同階段將觸發不同類型的事件,所以不再需要檢查readyState屬性

 

當調用send()事件時,觸發單個loadstart事件,當正在加載服務器的相應時,XMLHttpRequest對象會發生progress事件,通常每隔50毫秒左右,所以可以使用這些事件給用戶反饋請求進度。如果請求快速完成,它可能不會觸發progress事件。當事件完成時,觸發load事件。

 

一個完成的請求不一定是成功的請求,例如:load事件的處理程序應該檢車XMLHttpRequest對象的status狀態來確定收到的是“200 OK”而不是“400 Not Found”的HTTP響應。

 

HTTP請求無法完成有三種情況,對應三種事件

1.請求超時:timeout時間

2.請求中止:abort事件

3.阻止請求完成:比如太多重定向這樣的網絡錯誤:error事件

 

對於任何具體的請求,瀏覽器只會觸發load,abort,timeout和error事件中的一個,一旦這些事件觸發后,將觸發loadend事件。需要在send()之前進行事件監聽,下面是下載事件被觸發:

var req = new XMLHttpRequest();

req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);

req.open();

...

// progress on transfers from the server to the client (downloads)
function updateProgress(evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }
}

function transferComplete(evt) {
  alert("The transfer is complete.");
}

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  alert("The transfer has been canceled by the user.");
}

下面是上傳事件被觸發:

var req = new XMLHttpRequest();

req.upload.addEventListener("progress", updateProgress);
req.upload.addEventListener("load", transferComplete);
req.upload.addEventListener("error", transferFailed);
req.upload.addEventListener("abort", transferCanceled);

req.open();

判斷是否支持progress事件

    if("onprogress" in (new XMLHttpRequest())){
        //支持progress事件
    }

和progress事件相關聯的事件對象上有三個有用的屬性

1.loaded屬性是目前傳輸的字節數值

2.total屬性是自"Content-Length"頭傳輸的數據的整體長度(單位字節)

3.知道上面的長度則lengthComutable為true,反之亦然。

    request.onprogress=function(e){
        if(e.lengthComputable){
            progress.innerHTML=Math.round(100* e.loaded/ e.total)+"% Complete";
        }
    };

中止請求和超時

    ///實現超時
    //如果響應成功到達,傳入responseText給回調函數
    //如果響應在timeout毫秒內沒有到達,中止這個請求
    //瀏覽器可能在abort()后出發“readystatechange”
    //如果是部分請求結果到達,設置可能設置status屬性
    //所以需要設置一個標記,當部分且超時的響應到達時不會調用回調函數
    ///如果使用load事件就沒有這個風險
    function timedGetText(url,timeout,callback){
        var request=new XMLHttpRequest();
        var timedout=false;
        //啟動計時器,在timeout毫秒之后將終止請求
        var timer=setTimeout(function(){
            timedout=true;
            request.abort();
        },
        timeout);
        
        request.open("GET",url);
        request.onreadystatechange = function(){ //定義事件處理程序
            if(request.readyState !== 4) return; //忽略未完成的請求
            if(timedout) return;                 //忽略終止請求
            clearTimeout(timer);                //取消等待的超時
            if(request.status === 200)          //如果請求成功
                callback(request.responseText); //把response傳給回調
        };
        request.send(null);
    }

跨域請求

作為同源策略的一部分,XMLHttpRequest對象通常僅可以發起和文檔具有相同服務器的HTTP請求,這個限制關閉了安全漏洞,但是同時也阻止了大量適合使用的跨域請求。

CORS支持的跨域請求工作不需要做任何的事情,但是安全細節要了解

1.給open()方法傳遞的用戶名和密碼將絕對不會通過跨域請求發送

2.跨域請求不會包含其他的任何用戶證書:cookie和HTTP身份驗證令牌(token)通常不會作為請求內容發送

 1    /*
 2     *
 3     * linkdetails.js
 4     *
 5     * 這個常見的javascript模塊查詢有href屬性但是沒有title屬性的所有<a>元素
 6     * 並給他們注冊onmouseover事件處理程序
 7     * 這個時間處理程序使用XMLHttpRequest HEAD請求取得鏈接資源的詳細信息
 8     * 然后把這寫詳細信息設置成鏈接的title屬性
 9     * 這樣他們會在工具提示中顯示
10     * */
11 whenReady(function(){
12     //是否有機會使用跨域請求?
13     var supportsCORS= (new XMLHttpRequest()).withCredentials !== undefined;
14 
15     //遍歷文檔中的所有鏈接
16     var links=document.getElementsByTagName('a');
17     for(var i=0;i<links.length;i++){
18         var link=links[i];
19         if(!link.href) continue; //跳過沒有超鏈接的錨點
20         if(link.title) continue; //跳過已經有工具提示的鏈接
21         //如果這是一個跨域鏈接
22         if(link.host !== location.host || link.protocol !== location.protocol){
23             link.title="站外鏈接"; //假設我們不能得到任何信息
24             if(!supportsCORS) continue; //如果沒有CORS支持就退出
25            //否則,我們能了解這個鏈接的更多信息
26             //所以繼續前行,注冊事件處理程序,於是我們可以嘗試
27         }
28         //注冊事件處理程序,當鼠標懸停時下載鏈接詳細信息
29         if(link.addEventListener)
30             link.addEventListener("mouseover",mouseHandler,false);
31         else
32             link.attachEvent("onmouseover",mouseHandler);
33     }
34 
35     function mouseHandler(e){
36         var link = e.target || e.srcElement; //<a>元素
37         var url=link.href;                  //鏈接url
38 
39         var req=new XMLHttpRequest();       //新請求
40 
41         req.open("HEAD",url);               //僅僅詢問頭信息
42         req.onreadystatechange=function(){
43             if(req.readyState !== 4) return; //忽略未完成的請求
44             if(req.status === 200){  //如果成功
45                 var type=req.getResponseHeader("Content-Type"); //獲取鏈接的詳細信息
46                 var size=req.getResponseHeader("Content-Length");
47                 var date=req.getResponseHeader("Last-Modified");
48 
49                 //在工具提示中顯示詳細信息
50                 link.title="類型:"+type+"\n"+
51                         "大小:"+size+"\n"+"時間:"+date;
52             }else{
53                 //如果請求失敗,且鏈接沒有“站外鏈接”的工具提示,那么顯示這個錯誤
54                 //if(!link.title)
55                     link.title="不能獲取詳情:\n"+
56                             req.status+" statusText"+req.statusText;
57             }
58         };
59         req.send(null);
60 
61         //移除處理程序,僅想要一次獲取這些頭信息
62         if(link.removeEventListener)
63             link.removeEventListener("mouseover",mouseHandler,false);
64         else
65             link.detachEvent("onmouseover",mouseHandler);
66     }
67 });
View Code

出現的bug

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.


XMLHttpRequest2 進行跨域訪問時需要服務器許可,不是任何域都接受跨域請求的。先來看一下從 Yahoo YQL 域返回的響應頭(Response Header ):注意:

1 HTTP/1.1 200 OK
2 Set-Cookie: AO="o=1&s=1&dnt=1"; Version=1; Domain=yahoo.com; Max-Age=630720000; Expires=Sat, 18-Jun-2033 10:07:41 GMT; Path=/
3 Access-Control-Allow-Origin: *
4 Cache-Control: public, max-age=899
5 Content-Type: text/xml;charset=utf-8
6 Content-Encoding: gzip
7 Vary: Accept-Encoding 8 Date: Sun, 23 Jun 2013 10:07:40 GMT

注意里面有一條標識 Access-Control-Allow-Origin:* ,這就表示允許跨域訪問,所以可以正常訪問該域,而對於其他沒有該標識的域就會出現禁止訪問提示。

 

解決方法:

1.在服務器端設置response頭,php是<?php header('Access-Control-Allow-Origin: *'); ?>,當然會讓你的服務器網站變得很危險

2.在chrom瀏覽器中設置

  1)安裝Chrom插件(你得先翻了牆( ̄▽ ̄)"):點擊這里

  2)在windows中window+r打開運行,輸入

chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security

還要注意的一點是:chrome不支持本地運行Access-Control-Allow-Origin,所以為了可以讓chrome瀏覽器在頭部發送Access-Control-Allow-Origin,你必須結合你的hosts文件將localhost指定成其他域名:127.0.0.1 localhost yourdomain.com

JSONP

function foo(data)
{
    // do stuff with JSON
}

var script = document.createElement('script');
script.src = '//example.com/path/to/jsonp?callback=foo'

document.getElementsByTagName('head')[0].appendChild(script);

 


免責聲明!

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



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