JavaScript學(xué)習(xí)筆記整理(14):AJAX

2018-06-19 13:52 更新
      超文本傳輸協(xié)議(HyperText Transfer Protocol,HTTP)是用于從WWW服務(wù)器傳輸超文本到本地瀏覽器的傳輸協(xié)議(transport)。它可以使瀏覽器更加高效,使網(wǎng)絡(luò)傳輸減少。

Ajax(Asynchronous JavaScript and XML)描述了一種主要使用腳本操縱HTTP的Web應(yīng)用架構(gòu)。Ajax的主要特點(diǎn)是使用腳本操縱HTTP和Web服務(wù)器進(jìn)行數(shù)據(jù)交換,不會導(dǎo)致頁面重載。

一、XMLHttpRequest對象
所有瀏覽器都支持XMLHttpRequest對象,它定義了用腳本操縱HTTP的API。除了常用的GET請求,這個(gè)API還包含實(shí)現(xiàn)POST請求的能力,同時(shí)它能用文本或Document對象的形式返回服務(wù)器的響應(yīng)。

瀏覽器在XMLHttpRequest類上定義了它們的HTTP API,這個(gè)類的每個(gè)實(shí)例都表示一個(gè)獨(dú)立的請求/響應(yīng)對,并且這個(gè)對象的屬性和方法允許指定請求細(xì)節(jié)和提取響應(yīng)數(shù)據(jù)。

具體來說,AJAX包括以下幾個(gè)步驟:
  1. 創(chuàng)建AJAX對象(實(shí)例化XMLHttpRequest對象)
  2. 發(fā)起HTTP請求
  3. 接收服務(wù)器傳回的數(shù)據(jù)
  4. 更新網(wǎng)頁數(shù)據(jù)

1.1實(shí)例化
實(shí)例化XMLHttpRequest對象:

var xhr = new XMLHttpRequest();

注意:你可以重用已存在的XMLHttpRequest,但這將會終止之前通過該對象掛起的任何請求。
由于IE7之前的版本不支持非標(biāo)準(zhǔn)的XMLHttpRequest()構(gòu)造函數(shù),不過能如下模擬:

if(window.XMLHttpRequest === undefined){

  window.XMLHttpRequest = function(){

    try{

      //如果可用,則使用ActiveX對象的最新版本

      return new ActiveXObject('Msxml2.XMLHTTP.6.0');

    }catch(e1){

      try{

        //否則,回退到較舊的版本

        return new ActiveXObject('Msxml2.XMLHTTP.3.0');

      }catch(e2){

        throw new Error('XMLHttpRequest is not supported');

      }

    }

  }

}

一個(gè)HTTP請求由4部分組成:
  • HTTP請求方法或動作(verb)
  • 正在請求的URL
  • 一個(gè)可選的請求頭集合,其中可能包括身份驗(yàn)證信息
  • 一個(gè)可選的請求主體

服務(wù)器返回的HTTP響應(yīng)包含3部分:
  • 一個(gè)數(shù)字和文字組成的狀態(tài)碼,用來顯示請求的成功和失敗
  • 一個(gè)響應(yīng)頭集合
  • 響應(yīng)主體

注意:使用XMLHttpRequest時(shí),必須把文件放到Web服務(wù)器上。因?yàn)锳JAX只能向同源網(wǎng)址(協(xié)議、域名、端口都相同)發(fā)出HTTP請求,如果發(fā)出跨源請求,就會報(bào)錯

1.2 指定請求
創(chuàng)建XMLHttpRequest對象后,就可以調(diào)用XMLHttpRequest對象的open()方法去指定這個(gè)請求的兩個(gè)必需部分:方法和URL。

xhr.open('GET','example.php');  

上面代碼向指定的服務(wù)器網(wǎng)址,發(fā)出GET請求。
open()的第一個(gè)參數(shù)指定HTTP方法或動作(常用的是“GET”和“POST”),這個(gè)字符串不區(qū)分大小寫,但通常使用大寫字母來匹配HTTP協(xié)議。“DELETE”、“HEAD”、“OPTONS”、“PUT”也可以作為open()方法的第1個(gè)參數(shù)。
第二個(gè)參數(shù)是URL(跨域的請求通常會報(bào)錯),它是請求的主題。

如果有請求頭,我們可以設(shè)置它。例如,POST請求需要“Content-Type”頭指定請求主題的MIME類型。

xhr.setRequestHeader('Content-Type','text/plain');

注意:如果對相同的頭調(diào)用setRequestHeader()多次,新值不會取代之前指定的值,相反,HTTP請求將包含這個(gè)頭的多個(gè)副本或這個(gè)頭將指定多個(gè)值。

使用XMLHttpRequest發(fā)起HTTP請求的最后一步是指定可選的請求主體并向服務(wù)器發(fā)送它。

xhr.send(null);

GET請求沒有主體,所以應(yīng)該傳遞null或者省略這個(gè)參數(shù)。POST請求通常擁有主體,同時(shí)它應(yīng)該匹配使用setRequestHeader()指定的“Content-Type”頭。

HTTP請求的各部分有指定順序:請求方法和URL首先到達(dá),然后是請求頭,最后是請求主體。XMLHttpRequest實(shí)現(xiàn)通常直到調(diào)用send()方法才開始啟動網(wǎng)絡(luò)。

1.3 取得響應(yīng)
一個(gè)完整的HTTP響應(yīng)由狀態(tài)嗎、響應(yīng)頭集合和響應(yīng)主體組成。這些都可以通過XMLHttpRequest對象的屬性和方法使用:
  • statusstatusText屬性以數(shù)字和文本的形式返回HTTP狀態(tài)碼。這些屬性保存標(biāo)準(zhǔn)的HTTP值。像200和“OK”表示成功請求,404和“Not Found”表示URL不能匹配服務(wù)器上的任何資源。
  • 使用getResponseHeader()getAllResponseHeaders()能查詢/獲取響應(yīng)頭。XMLHttpRequest會自動處理cookie:它會從getAllResponseHeaders()頭返回集合中過濾cookie頭,而如果給getResponseHeader()傳遞“Set-Cookie”和“Set-Cookie2”,則返回null。
  • 響應(yīng)主體可以從responseText屬性得到文本形式的,從responseXML屬性中得到Document形式的。

為了在響應(yīng)準(zhǔn)備就緒時(shí)得到通知,我們必須監(jiān)聽XMLHttpRequest對象上的readystatechange事件。
每當(dāng)發(fā)生狀態(tài)變化的時(shí)候,readyState屬性的值就會發(fā)生改變。這個(gè)值每一次變化,都會觸發(fā)readyStateChange事件。

xhr.onreadystatechange=function(){}

1.3.1 同步響應(yīng)
XMLHttpRequest默認(rèn)是異步的,當(dāng)然,如果有需要,我們也可以設(shè)置成同步(較少用):

xhr.open('GET','example.php',false);

上面的代碼中,通過傳遞第三個(gè)參數(shù)為false實(shí)現(xiàn)同步。要注意的是:一旦設(shè)置為同步,那么send()方法將阻塞直到請求完成。

1.3.2 響應(yīng)解碼
如果服務(wù)器發(fā)送諸如對象或數(shù)組這樣的結(jié)構(gòu)化數(shù)據(jù)作為響應(yīng),它應(yīng)該傳輸JSON編碼的字符串?dāng)?shù)據(jù)。
下面是一個(gè)解析不同類型的響應(yīng)值的方法:

function get(url,callback){

  var xhr = new XMLHttpRequest();  //創(chuàng)建新請求

  xhr.open('GET',url);

  xhr.onreadystatechange=function(){

    //如果請求完成且成功

    if(xhr.readyState === 4 && xhr.status === 200){

      //獲得響應(yīng)的類型

      var type = xhr.getResponseHeader('Content-type');

      if(type.indexOf('xml') !== -1 && xhr.responseXML){

        callback(xhr.responseXML);  //Document對象響應(yīng)

      }else if(type === 'application/json'){

        callback(JSON.parse(xhr.responseText));  //JSON響應(yīng)

      }else{

        callback(xhr.responseText);  //字符串響應(yīng)

      }

    }

  };

  xhr.send(null); //立即發(fā)送請求

}


1.4 編碼請求主體
HTTP POST請求包括一個(gè)請求主體,它包含客戶端傳遞給服務(wù)器的數(shù)據(jù)。

1.4.1 表單編碼的請求
默認(rèn)情況下,HTML表單通過POST方法發(fā)送給服務(wù)器,而編碼后的表單數(shù)據(jù)則用做請求主體。例如:

user=TG&age=18;

表單數(shù)據(jù)編碼格式有一個(gè)正式的MIME類型:

application/x-www-form-urlencoded

當(dāng)使用POST方法提交這種順序的表單數(shù)據(jù)時(shí),必須設(shè)置“Content-Type”請求頭為這個(gè)值。

xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

注意:當(dāng)使用form時(shí),這不是必需值,因?yàn)檫@是默認(rèn)方法。

當(dāng)我們發(fā)送給服務(wù)器的是一個(gè)JavaScript對象時(shí),我們也可以采取這種類型的編碼。
下面的函數(shù)encodeFormData()就是將對象轉(zhuǎn)換為表單編碼。

/*

* {

*    name:'TG',

*    age:18

* }

*/


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('&');

}

1.4.2 JSON編碼的請求
使用JSON編碼主體來發(fā)起HTTP POST請求

xhr.setRequestHeader('Content-Type','application/json');

xhr.send(JSON.stringify(data));


1.5.XMLHttpRequest實(shí)例的屬性

(1)readyState屬性
readyState是一個(gè)整數(shù),它指定了HTTP請求的狀態(tài)。它可能的值如下:

0,對應(yīng)常量UNSENT,表示XMLHttpRequest實(shí)例已經(jīng)生成,但是open()方法還沒有被調(diào)用。 

1,對應(yīng)常量OPENED,表示open()已調(diào)用,但send()方法還沒有被調(diào)用,仍然可以使用setRequestHeader(),設(shè)定HTTP請求的頭信息。

2,對應(yīng)常量HEADERS_RECEIVED,表示send()方法已經(jīng)執(zhí)行,并且頭信息和狀態(tài)碼已經(jīng)收到。

3,對應(yīng)常量LOADING,表示正在接收服務(wù)器傳來的主體(body)部分的數(shù)據(jù),如果responseType屬性是text或者空字符串,responseText就會包含已經(jīng)收到的部分信息。

4,對應(yīng)常量DONE,表示服務(wù)器數(shù)據(jù)已經(jīng)完全接收,或者本次接收已經(jīng)失敗了。

在IE8之前并沒有定義這些值,但使用硬編碼值4來表示XMLHttpRequest.DONE。
(2)status
status屬性為只讀屬性,表示本次請求所得到的HTTP狀態(tài)碼,它是一個(gè)整數(shù)。一般來說,如果通信成功的話,這個(gè)狀態(tài)碼是200。

200, OK,訪問正常  

301, Moved Permanently,永久移動  

302, Move temporarily,暫時(shí)移動  

304, Not Modified,未修改  

307, Temporary Redirect,暫時(shí)重定向  

401, Unauthorized,未授權(quán)  

403, Forbidden,禁止訪問  

404, Not Found,未發(fā)現(xiàn)指定網(wǎng)址  

500, Internal Server Error,服務(wù)器發(fā)生錯誤

例子:

if(xhr.readyState === 4){

  //請求完成

  if(xhr.status === 200){

     //請求成功

  }

}

(3)statusText

statusText屬性為只讀屬性,返回一個(gè)字符串,表示服務(wù)器發(fā)送的狀態(tài)提示。不同于status屬性,該屬性包含整個(gè)狀態(tài)信息,比如”200 OK“。

(4)timeout

timeout屬性等于一個(gè)整數(shù),表示多少毫秒后,如果請求仍然沒有得到結(jié)果,就會自動終止。如果該屬性等于0,就表示沒有時(shí)間限制。

對應(yīng)超時(shí),還有個(gè)監(jiān)聽函數(shù):

xhr.ontimeout = function(){

  //請求超時(shí)

}

(5)response

response屬性為只讀,返回接收到的數(shù)據(jù)體(即body部分)。它的類型可以是ArrayBuffer、Blob、Document、JSON對象、或者一個(gè)字符串,這由XMLHttpRequest.responseType屬性的值決定。 

如果本次請求沒有成功或者數(shù)據(jù)不完整,該屬性就會等于null。

(6)responseType

responseType屬性用來指定服務(wù)器返回?cái)?shù)據(jù)(xhr.response)的類型。

”“:字符串(默認(rèn)值)  

“arraybuffer”:ArrayBuffer對象  

“blob”:Blob對象  

“document”:Document對象  

“json”:JSON對象  

“text”:字符串

text類型適合大多數(shù)情況,而且直接處理文本也比較方便,document類型適合返回XML文檔的情況,blob類型適合讀取二進(jìn)制數(shù)據(jù),比如圖片文件。

(7)responseText

responseText屬性返回從服務(wù)器接收到的字符串,該屬性為只讀。如果本次請求沒有成功或者數(shù)據(jù)不完整,該屬性就會等于null。 

如果服務(wù)器返回的數(shù)據(jù)格式是JSON,就可以使用responseText屬性。

var data = xhr.responseText;

data = JSON.parse(data);

(8)responseXML

responseXML屬性返回從服務(wù)器接收到的Document對象,該屬性為只讀。如果本次請求沒有成功,或者數(shù)據(jù)不完整,或者不能被解析為XML或HTML,該屬性等于null。 

返回的數(shù)據(jù)會被直接解析為DOM對象。

(9)事件監(jiān)聽接口

XMLHttpRequest第一版,只能對onreadystatechange這一個(gè)事件指定回調(diào)函數(shù)。該事件對所有情況作出響應(yīng)。 XMLHttpRequest第二版允許對更多的事件指定回調(diào)函數(shù)。

onloadstart 請求發(fā)出  

onprogress 正在發(fā)送和加載數(shù)據(jù)  

onabort 請求被中止,比如用戶調(diào)用了abort()方法  

onerror 請求失敗  

onload 請求成功完成  

ontimeout 用戶指定的時(shí)限到期,請求還未完成  

onloadend 請求完成,不管成果或失敗


1.6.XMLHttpRequest實(shí)例的方法


(1)abort()

abort方法用來終止已經(jīng)發(fā)出的HTTP請求。


(2)getAllResponseHeaders()

getAllResponseHeaders方法返回服務(wù)器發(fā)來的所有HTTP頭信息。格式為字符串,每個(gè)頭信息之間使用CRLF分隔,如果沒有受到服務(wù)器回應(yīng),該屬性返回null。


(3)getResponseHeader()

getResponseHeader方法返回HTTP頭信息指定字段的值,如果還沒有收到服務(wù)器回應(yīng)或者指定字段不存在,則該屬性為null。

如果有多個(gè)字段同名,則它們的值會被連接為一個(gè)字符串,每個(gè)字段之間使用“逗號+空格”分隔。


(4)open()

XMLHttpRequest對象的open方法用于指定發(fā)送HTTP請求的參數(shù),它的使用格式如下,一共可以接受五個(gè)參數(shù)。

void open( string method, string url, optional boolean async, optional string user, optional string password);

參數(shù)說明:

method:表示HTTP動詞,比如“GET”、“POST”、“PUT”和“DELETE”。  

url: 表示請求發(fā)送的網(wǎng)址。  

async: 格式為布爾值,默認(rèn)為true,表示請求是否為異步。如果設(shè)為false,則send()方法只有等到收到服務(wù)器返回的結(jié)果,才會有返回值。  

user:表示用于認(rèn)證的用戶名,默認(rèn)為空字符串。  

password:表示用于認(rèn)證的密碼,默認(rèn)為空字符串。

(5)send()

send方法用于實(shí)際發(fā)出HTTP請求。如果不帶參數(shù),就表示HTTP請求只包含頭信息,也就是只有一個(gè)URL,典型例子就是GET請求;如果帶有參數(shù),就表示除了頭信息,還帶有包含具體數(shù)據(jù)的信息體,典型例子就是POST請求。


(6)setRequestHeader()

setRequestHeader方法用于設(shè)置HTTP頭信息。該方法必須在open()之后、send()之前調(diào)用。如果該方法多次調(diào)用,設(shè)定同一個(gè)字段,則每一次調(diào)用的值會被合并成一個(gè)單一的值發(fā)送。

xhr.setRequestHeader('Content-Type', 'application/json');

xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);

xhr.send(JSON.stringify(data));

在上面代碼中,首先設(shè)置頭信息Content-Type,表示發(fā)送JSON格式的數(shù)據(jù);然后設(shè)置Content-Length,表示數(shù)據(jù)長度;最后發(fā)送JSON數(shù)據(jù)。

(6)overrideMimeType()

該方法用來指定服務(wù)器返回?cái)?shù)據(jù)的MIME類型。該方法必須在send()之前調(diào)用。

// 強(qiáng)制將MIME改為文本類型

xhr.overrideMimeType('text/plain; charset=x-user-defined');

在XMLHttpRequest版本升級后,一般采用指定的responseType的方法。

xhr.responseType = 'text';


2、 上傳文件
講到上傳文件,我們最常用的就是使用<input type="file">元素選擇文件,然后表單將在它產(chǎn)生POST請求主體中發(fā)送文件內(nèi)容。

<form id="form" action="upload.php" method="POST">   

  <input type="file" id="files" name="photos[]"/>   

  <button type="submit" id="upload">上傳</button>  

</form>

如果要允許選擇多個(gè)文件,可設(shè)置file控件的multiple屬性。

<input type="file" multiple/>


1.5.1 files屬性
每個(gè)<input type="file">元素都有一個(gè)files屬性,返回一個(gè)FileList對象,它是File對象中的類數(shù)組對象,包含了用戶選擇的文件。

var fileInput = document.getElementById('files');

var files = fileInput.files;

通常情況下,對于文件上傳元素,我們都會添加change事件處理程序,每次文件信息有變化,它都會觸發(fā)。

fileInput.addEventListener('change',function(){

  var file = this.files[0];

  if(!file) return;

  var xhr = new XMLHttpRequest();

  xhr.open('POST','upload.php');

  xhr.send(file);

},false);

文件類型是二進(jìn)制大對象(Blob)類型中的一個(gè)子類型。XHR2允許向send()方法傳入任何Blob對象。如果沒有顯式設(shè)置Content-Type頭,這個(gè)Blob對象的type屬性用于設(shè)置待上傳的Content-Type頭。
也可以顯式設(shè)置:

xhr.setRequestHeader('Content-Type', file.type);


1.5.2 multipart/form-data請求
當(dāng)HTML表單同時(shí)包含文件上傳元素和其他元素時(shí),瀏覽器不能使用普通的表單編碼而必須使用成為“multipart/form-data”的特殊Content-Type頭來用POST方法提交表單。

1.5.3 FormData對象
XHR2定義了新的FormData API,它容易實(shí)現(xiàn)多部分請求主體。

首先,新建一個(gè)FormData對象的實(shí)例,用來模擬發(fā)送到服務(wù)器的表單數(shù)據(jù),把選中的文件添加到這個(gè)對象上面。

var formdata = new FormData();

然后按需多次調(diào)用這個(gè)對象的append()方法把個(gè)體“部分”添加到請求中。

for(var i = 0;i < files.length; i++){

  var file = files[i];

  formdata.append('photos[]',file,file.name);

}

最后,把FormData對象傳遞給send()方法

xhr.send(formdata);

除了可以添加文件,還可以添加二進(jìn)制對象(Blob)或者字符串。

// Files  

formdata.append(name, file, filename);   


// Blobs  

formdata.append(name, blob, filename);   


// Strings  

formdata.append(name, value);    

append方法的第一個(gè)參數(shù)是表單的控件名,第二個(gè)參數(shù)是實(shí)際的值,第三個(gè)參數(shù)是可選的,通常是文件名。



3、HTTP進(jìn)度事件

除了使用readystatechange事件來探測HTTP請求的完成外,在XHR2中,還定義了多個(gè)有用的事件。

abort事件:當(dāng)進(jìn)度事件被中止時(shí)觸發(fā)。如果發(fā)生錯誤,導(dǎo)致進(jìn)程中止,不會觸發(fā)該事件。  

error事件:由于錯誤導(dǎo)致資源無法加載時(shí)觸發(fā)。

load事件:進(jìn)度成功結(jié)束時(shí)觸發(fā)。

loadstart事件:進(jìn)度開始時(shí)觸發(fā)。

loadend事件:進(jìn)度停止時(shí)觸發(fā),發(fā)生順序排在error事件\abort事件\load事件后面。

progress事件:當(dāng)操作處于進(jìn)度之中,由傳輸?shù)臄?shù)據(jù)塊不斷觸發(fā)。

timeout事件:進(jìn)度超過限時(shí)觸發(fā)。

當(dāng)調(diào)用send()時(shí),觸發(fā)單個(gè)loadstart事件。當(dāng)正在加載服務(wù)器的響應(yīng)時(shí),XMLHttpRequest對象會發(fā)現(xiàn)progress事件,通常每隔50毫秒左右,可以使用這些事件給用戶反饋請求的進(jìn)度。當(dāng)事件完成,會觸發(fā)load事件。


HTTP請求無法完成有3種情況:

  1. 請求超時(shí),會觸發(fā)timeout事件
  2. 請求終止,會觸發(fā)abort事件
  3. 請求發(fā)生錯誤,會觸發(fā)error事件


注意:對于任何具體請求,瀏覽器將只會觸發(fā)load、abort、timeout和error事件中的一個(gè)。一旦這些事件中的一個(gè)發(fā)生后,瀏覽器應(yīng)該觸發(fā) loadend 事件。


要使用這些事件,有兩種方式:

xhr.onload=function(){}


xhr.addEventListener('load',function(){})


3.1 progress事件

因?yàn)檫@些事件是XHR2中才定義的,所以有時(shí)需要檢查瀏覽器是否支持progress事件:

if('onprogress' in (new XMLHttpRequest())){

  //支持progress事件

}

除了像type和timestamp這樣的常用Event對象屬性外,與progress事件相關(guān)聯(lián)的事件對象有3個(gè)有用的屬性:

lengthComputable:返回一個(gè)布爾值,表示當(dāng)前進(jìn)度是否具有可計(jì)算的長度。如果為false,就表示當(dāng)前進(jìn)度無法測量。   


total:返回一個(gè)數(shù)值,表示當(dāng)前進(jìn)度的總長度。如果是通過HTTP下載某個(gè)資源,表示內(nèi)容本身的長度,不含HTTP頭部的長度。如果lengthComputable屬性為false,則total屬性就無法取得正確的值。   


loaded:返回一個(gè)數(shù)值,表示當(dāng)前進(jìn)度已經(jīng)完成的數(shù)量。該屬性除以total屬性,就可以得到目前進(jìn)度的百分比。

我們可以利用total和loaded屬性來獲取當(dāng)前進(jìn)度:

xhr.onprogress = function(e){

  if(e.lengthComputable){

    var percentComplete = e.loaded / e.total;

  }

}


3.2 上傳進(jìn)度事件

在XHR2中,也提供了用于監(jiān)控HTTP請求上傳的事件。在實(shí)現(xiàn)這些特性的瀏覽器中,XMLHttpRequest對象有一個(gè)upload屬性,upload屬性值是一個(gè)對象,它定義了addEventListener()方法和整個(gè)progress事件集合,比如onprogress和onload。

xhr.upload.onprogress = function(e){

  var percentComplete = e.loaded / e.total;

}


3.3 中止請求和超時(shí)

我們可以通過調(diào)用XMLHttpRequest對象的abort()方法來取消正在進(jìn)行的HTTP請求,調(diào)用abort()方法時(shí),會觸發(fā)abort事件。


在XHR2中,還定義了timeout屬性來指定自動中止后的毫秒數(shù)。

xhr.timeout = 1000;


4、同源策略

同源策略是對JavaScript代碼能夠操作哪些Web內(nèi)容的一條完整的安全限制。當(dāng)Web頁面使用多個(gè)<iframe>元素或打開其他瀏覽器窗口的時(shí)候,這一策略通常就會發(fā)揮作用。


所謂“同源”指的是”三個(gè)相同“。

  • 協(xié)議相同
  • 域名相同
  • 端口相同


從不同Web服務(wù)器載入的文檔具有不同的來源。通過同一主機(jī)的不同端口載入的文檔具有不同的來源。使用http:協(xié)議載入的文檔和使用https:協(xié)議載入的文檔具有不同的來源,即使它們來自同一個(gè)服務(wù)器。


同源政策的目的,是為了保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)。


4.1 不嚴(yán)格的同源策略

有三種不嚴(yán)格的同源策略


(1)使用Document對象的domain屬性

在默認(rèn)情況下,屬性domain存放的是載入文檔的服務(wù)器的主機(jī)名。


比如:來自home.example.com的文檔里的腳本要合法的讀取developer.example.com載入的文檔的屬性(默認(rèn)情況下是不允許的,會受到同源策略的限制)。

document.domain = 'example.com';

一旦上面兩個(gè)文檔里包含的腳本把domain都設(shè)置成了上面相同的值,那么這兩個(gè)文檔就不會受同源策略的約束了,可以相互讀取對方的屬性。


(2)跨域資源共享(Cross-Origin Resource Sharing)

這種方式是應(yīng)用到后臺中:

Access-Control-Allow-Origin:*,//允許所有域名的腳本訪問該資源。  


Access-Control-Allow-Origin:http://www.example.com  //允許特定的域名訪問。


(3)跨文檔消息(cross-document messaging)

跨文檔消息允許來自一個(gè)文檔的腳本可以傳遞文本消息到另一個(gè)文檔里的腳本。



4.2 跨站腳本

跨站腳本(Cross-site scripting,XSS),這個(gè)術(shù)語用來表示一類安全問題,也就是攻擊者向目標(biāo)Web站點(diǎn)注入HTML標(biāo)簽或腳本。


通常,防止XSS攻擊的方式是,在使用任何不可信的數(shù)據(jù)來動態(tài)的創(chuàng)建文檔內(nèi)容之前,從中移除HTML標(biāo)簽。


5、Fetch API

Fetch API是一種新規(guī)范,用來取代XMLHttpRequest對象。


5.1 特性檢測

if(self.fetch){

  //支持

}else{

  //不支持

}

在Fetch API中,最常用的就是fetch()函數(shù),它接收一個(gè)URL參數(shù)(也可以是request對象),返回一個(gè)promise來處理response。response參數(shù)還帶著一個(gè)Response對象。

fetch(url).then(function(response){

   console.log(response);

});

fetch方法還可以設(shè)置第二個(gè)參數(shù),用來配置其他值,可選的參數(shù)有:

method: 請求使用的方法,如 GET、POST。  

headers: 請求的頭信息,形式為 Headers 對象或 ByteString。  

body: 請求的 body 信息:可能是一個(gè) Blob、BufferSource、FormData、URLSearchPara ms 或者 USVString 對象。注意 GET 或 HEAD 方法的請求不能包含 body 信息。  

mode: 請求的模式,如 cors、 no-cors 或者 same-origin。  

credentials: 請求的 credentials,如 omit、same-origin 或者 include。  

cache: 請求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached.

下面是發(fā)出POST請求

fetch(url,{

  method: 'POST',

  headers: {

    'Content-Type': 'application/x-www-form-urlencoded'

  },

  body: 'name=TG&love=1'

}).then(function(response){})

注意:fetch() 方法的參數(shù)與 Request() 構(gòu)造器是一樣的。

fetch(input, init).then(function(response) { ... });


var myRequest = new Request(input, init);


下面是一個(gè)Fetch API完整請求的簡單例子:

fetch(url).then(function (response) {   

  return response.json();  

}).then(function (jsonData) {   

  console.log(jsonData);  

}).catch(function () {   

  console.log('出錯了');  

});

上面代碼向指定的URL發(fā)出請求,得到回應(yīng)后,將其轉(zhuǎn)為JSON格式,輸出到控制臺。如果出錯,則輸出一條提示信息。


Fetch API引入三個(gè)新的對象(也是構(gòu)造函數(shù)):Headers, Request和Response。


5.2 Headers

Headers對象用來構(gòu)造/讀取HTTP數(shù)據(jù)包的頭信息。

reqHeaders = new Headers({   

  "Content-Type": "text/plain",   

  "Content-Length": content.length.toString(),   

  "X-Custom-Header": "ProcessThisImmediately", });

我們還可以使用append方法:

var content = 'Hello World';  

var headers = new Headers();  

headers.append("Accept", "application/json");  

headers.append("Content-Type", "text/plain");  

headers.append("Content-Length", content.length.toString());  

headers.append("X-Custom-Header", "ProcessThisImmediately");

Headers對象實(shí)例還提供了一些方法:

reqHeaders.has("Content-Type") // true  

reqHeaders.has("Set-Cookie") // false  

reqHeaders.set("Content-Type", "text/html")  

reqHeaders.append("X-Custom-Header", "AnotherValue")   


reqHeaders.get("Content-Length") // 11  

reqHeaders.getAll("X-Custom-Header") // ["ProcessThisImmediately", "AnotherValue"]   


reqHeaders.delete("X-Custom-Header")  

reqHeaders.getAll("X-Custom-Header") // []

生成Header實(shí)例以后,可以將它作為第二個(gè)參數(shù),傳入Request方法。

var headers = new Headers();  

headers.append('Accept', 'application/json');  

var request = new Request(URL, {headers: headers});   


fetch(request).then(function(response) {   

  console.log(response.headers);  

});


5.3 Request對象

Request對象用來構(gòu)造HTTP請求。

var req = new Request("/index.html");  

req.method // "GET"  

req.url // "http://example.com/index.html"

Request對象的第二個(gè)參數(shù),表示配置對象,

var uploadReq = new Request("/uploadImage", {   

  method: "POST",   

  headers: {   

    "Content-Type": "image/png",   

  },   

  body: "image data"  

});

Request對象實(shí)例的mode屬性,用來設(shè)置是否跨域,合法的值有以下三種:same-origin、no-cors(默認(rèn)值)、cors。當(dāng)設(shè)置為same-origin時(shí),只能向同域的URL發(fā)出請求,否則會報(bào)錯。


5.4 Response對象

 Fetch API 的Response接口呈現(xiàn)了對一次請求的響應(yīng)數(shù)據(jù)


5.4.1 屬性

(1)ok

如果ok屬性返回的狀態(tài)碼在200到299之間(即請求成功),這個(gè)屬性為true,否則為false。因此,我們可以這樣判斷請求是否成功:

fetch(url).then(function(response){

    if(response.ok){

      //請求成功

    }else{

      //請求失敗

    }

});


(2)status、statusText

status屬性返回HTTP的狀態(tài)碼;statusText返回一個(gè)字符串,表示服務(wù)器發(fā)送的狀態(tài)提示。比如通信成功時(shí),status是200,而statusText是“OK”


(3)url

返回完整的請求地址


(4)type

type屬性表示HTTP回應(yīng)的類型。合法的值有五個(gè)basic、cors、default、error、opaque。basic表示正常的同域請求;cors表示CORS機(jī)制的跨域請求;error表示網(wǎng)絡(luò)出錯,無法取得信息,status屬性為0,

如果需要在CORS機(jī)制下發(fā)出跨域請求,需要指明狀態(tài)。

fetch(url,{mode: 'cors'}).then(function(response){})


(5)headers

Headers對象,表示HTTP回應(yīng)的頭信息


(6)body

表示請求的內(nèi)容。

Request對象和Response對象都有body屬性,表示請求的內(nèi)容。body屬性可能是以下的數(shù)據(jù)類型。

ArrayBuffer  

ArrayBufferView (Uint8Array等)  

Blob/File  

string  

URLSearchParams  

FormData

注意:上面這些方法都只能使用一次,第二次使用就會報(bào)錯,也就是說,body屬性只能讀取一次。Request對象和Response對象都有bodyUsed屬性,返回一個(gè)布爾值,表示body是否被讀取過。


如果希望多次使用body屬性,可以使用Response對象和Request對象的clone方法。它必須在body還沒有讀取前調(diào)用,返回一個(gè)新的body,也就是說,需要使用幾次body,就要調(diào)用幾次clone方法。

response.clone()


(7)bodyUsed

bodyUsed屬性,返回一個(gè)布爾值,表示body是否被讀取過。


5.4.2 Response對象的方法


(1)text()、json()、FormData()、blob()、arrayBuffer()

在Fetch API中,數(shù)據(jù)傳送是以數(shù)據(jù)流(stream)的形式進(jìn)行的。對于大文件,數(shù)據(jù)是一段一段得到的。

而Fetch API提供了五個(gè)數(shù)據(jù)流讀取器。

text():返回字符串 

json():返回一個(gè)JSON對象 

formData():返回一個(gè)FormData對象 

blob():返回一個(gè)blob對象 

arrayBuffer():返回一個(gè)二進(jìn)制數(shù)組

簡單例子:

response.json().then(function(json){

  console.log(json);

})



參考資料:

書籍:《JavaScript權(quán)威指南》

文章:阮一峰:Fetch API

文檔:MDN:Fetch API





以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號