成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

AJAX原理與CORS跨域

jeyhan / 718人閱讀

摘要:同源策略指的是當前頁面和目標協(xié)議域名和端口均相同。發(fā)出請求的頁面所在域。響應的頭部信息在后端處理,不在此處講解。該事件會在數據接收期間不斷觸發(fā),但間隔不確定。服務器確認允許之后,才發(fā)起實際的請求。

ajax作為前端開發(fā)必需的基礎能力之一,你可能會使用它,但并不一定懂得其原理,以及更深入的服務器通信相關的知識。在最近兩天的整理過程中,看了大量的文章,發(fā)現自己的后端能力已經限制自己在網絡通信相關的知識領域的探索,還是應該盡快補齊短板。

下面我們來聊一聊ajax相關的東西,包括xhr/xdr/ajax/cors/http的一部分內容,其中會拋棄一些被棄用的歷史包袱,如IE6/7等。

Ajax的出現

2005年,Jesse James Garrett提出了Ajax的技術,其全稱為Asynchronous Javascript and XML,Ajax的核心是XMLHttpRequest對象,簡稱XHR,它用于使瀏覽器向服務器請求額外的數據而不卸載頁面,極大的提高了用戶體驗。在此之前,其實這種技術已經存在并被一些人實現,但并沒有流行也沒有被瀏覽器支持。不過在此之后,IE5第一次引入XHR對象,并支持ajax技術,后續(xù)被所有瀏覽器支持。

XMLHttpRequest對象和請求

XHR是一個API,為客戶端提供服務端和客戶端之間通信的功能,并且不會刷新頁面。它并不僅僅能取回XML類型的數據,而能取回所有類型的數據,除了http協(xié)議,還支持file和ftp協(xié)議。我們可以通過其構造函數來創(chuàng)建一個新的XHR對象,這個操作需要在其它所有操作之前完成:

var xhr = new XMLHttpRequest();

通過控制臺我們可以很方便看到XHR的原型鏈:Object -> EventTarget -> XMLHttpRequestEventTarget -> XMLHttpRequest。它擁有原型鏈上和本身的方法和屬性,現在看下我們常用的方法:

我們解釋下它的幾個主要方法,我們在創(chuàng)建了新的xhr對象之后,首先要調用它的open()方法:

// 第一個參數可以為get/post等,表示該請求的類型
// 第二個參數是請求的url,可以為相對路徑或絕對路徑
// 第三個參數代表是否異步,為true時異步,為false時同步
// 第四五個參數為可選的授權使用的參數,因為安全性不推薦明文使用
xhr.open("get", "example.php", true, username, password);

在這里受同源策略的影響,當第二個參數url跨域的時候會被瀏覽器報安全錯誤。同源策略指的是當前頁面和目標url協(xié)議、域名和端口均相同。后面也會講到,除IE之外的瀏覽器通過XHR對象實現跨域請求,只需將url設置為絕對url即可。

當初始化請求完成后,我們調用send()方法發(fā)送請求:

var data = new FormData();
data.append("name", "Nicholas");
// 接受一個請求主體發(fā)送的數據,如果不需要,傳入null
xhr.send(data);

當請求的類型為get/head時,send()的參數會被忽略并置為null,send()傳遞的參數會影響到我們請求的頭部content-type的默認值,該字段代表返回的資源內容的類型,用于瀏覽器處理,如果沒有設置或在一些場景下,瀏覽器會進行MIME嗅探來確定怎么處理返回的資源。

XHR2級中定義了FormData數據,用于常見的類表單數據序列化:

// 直接傳入表單id
var data = new FormData(document.getElementById("user-form"));

// 創(chuàng)建類表單數據
var data = new FormData();
data.append("name", "Nicholas");

// `FormData`可以直接被send()調用,會自動修改xhr的content-type頭部
xhr.send(data);

// 請求頭部的content-type: multipart/form-data; boundary=----WebKitFormBoundaryjn3q2KKRYrEH55Vz

// 請求的上傳數據 Request Payload:
------WebKitFormBoundaryjn3q2KKRYrEH55Vz
Content-Disposition: form-data; name="name"

Nicholas
------WebKitFormBoundaryjn3q2KKRYrEH55Vz--

FormData常用的方法有append/delete/entries/forEach/get/getAll/has/keys/set/values,都是常用的跟數組類似的方法,不再解釋。

請求方法

GET是最常見的請求類型,可以將查詢字符串參數添加到URL尾部,對XHR而言,該查詢字符串必須經過正確編碼,每個鍵值對必須使用encodeURIComponent()進行編碼,鍵值對之間由&分割:

// 封裝序列化鍵值對
function addURLParam(url, name, value) {
    url += (url.indexOf("?") === -1 ? "?" : "&";
    url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
    return url;
}

POST請求使用頻率僅次于GET請求,通常發(fā)送較多數據,且格式不限,數據傳遞給send()作為參數。

HTTP一共規(guī)定了九種請求方法,每一個動詞代表不同的語義,但是常用的只有上面兩種:

 - OPTIONS:返回服務器針對特定資源所支持的HTTP請求方法。也可以利用向Web服務器發(fā)送"*"的請求來測試服務器的功能性。 
 - HEAD:向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。 
 - GET:向特定的資源發(fā)出請求。 
 - POST:向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的創(chuàng)建和/或已有資源的修改。 
 - PUT:向指定資源位置上傳其最新內容。 
 - DELETE:請求服務器刪除Request-URI所標識的資源。 
 - TRACE:回顯服務器收到的請求,主要用于測試或診斷。 
 - CONNECT:HTTP/1.1協(xié)議中預留給能夠將連接改為管道方式的代理服務器。
 - PATCH: 用于對資源進行部分修改
HTTP頭部信息

每個HTTP請求和響應都帶有頭部信息,xhr對象允許我們操作部分頭部信息。我們可以通過xhr.setRequestHeader()方法來設置自定義的頭部信息或者修改瀏覽器默認的正常頭部信息。常用的請求頭部:

// 下面的實例是從我本地的一次請求取出的

Accept: 瀏覽器能夠處理的內容類型。// */*
Accept-Charset: 瀏覽器能夠顯示的字符集。// 未取到
Accept-Encoding: 瀏覽器能夠處理的壓縮編碼。// gzip,deflate
Accept-Language: 瀏覽器當前設置的語言。// zh-CN,zh;q=0.8,en;q=0.6
Connection: 瀏覽器與服務器之間連接的類型。// keep-alive
Cookie: 當前頁面設置的任意Cookie。// JlogDataSource=jomodb
Host: 發(fā)出請求的頁面所在域。// gzhxy-cdn-oss-06.gzhxy.baidu.com:8090
Referer: 發(fā)出請求的頁面URI。// http://gzhxy-cdn-oss-06.gzhxy.baidu.com:8090/jomocha/index.php?r=tools/offline/index
User-Agent: 瀏覽器的用戶代理字符串。// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

我們一般不修改瀏覽器正常的頭部信息,可能會影響到服務器響應。如果需要可以通過xhr.setRequestHeader()進行修改:

// 傳入頭部鍵值對,鍵值不區(qū)分大小寫,如果多次設置,則追加
// 此時請求頭部的content-type: application/json, text/html
xhr.setRequestHeader("content-type", "application/json");
xhr.setRequestHeader("content-type", "application/json");

設置頭部信息需要在open()之后,send()之前進行調用。響應的頭部信息在后端處理,不在此處講解。有一部分請求頭部信息不允許設置,如Accept-Encoding, Cookie等。

在請求返回后,我們可以獲取到響應頭部:

// 獲取指定項的響應頭
xhr.getResponseHeader("content-type"); // application/json;charset=utf-8
// 獲取所有的響應頭部信息
xhr.getAllResponseHeaders();

這里簡單說下content-type值,指的是請求和響應的HTTP內容類型,影響到服務器和瀏覽器對數據的處理方式,默認為text/html,常用的如:

// 包含資源類型,字符編碼, 邊界字符串三個參數,可選填

text/html;charset=utf-8    // html標簽文本
text/plain    // 純文本
text/css    // css文件
text/javascript    // js文件
// 普通的表單數據,可以通過表單標簽的enctype屬性指定
application/x-www-form-urlencode
// 發(fā)送文件的POST包,包過大需要分片時使用`boundary`屬性分割數據作邊界
multipart/form-data; boundary=something
// json數據格式
application/json
// xml類型的標記語言
application/xml
XHR對象的響應

我們現在對請求的發(fā)起很了解了,接著看下如何拿到響應數據。如果我們給open()傳遞的第三個參數是true,則代表為同步請求,那么js會被阻塞直到拿到響應,而如果為false則是異步請求,我們只需要綁定xhr.onreadystatechange()事件監(jiān)聽響應即可。最上面的圖已經說明了readystate的值含義,所以我們可以:

// xhr v1 的寫法,檢測readystate的值,為4則說明數據準備完畢,需要在open()前定義
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200 || xhr.status === 304) {
            console.log(xhr.responseText);
        } else {
            console.log(xhr.statusText);
        }
    }   
}

// xhr v2 的寫法,onload()事件說明數據準備完畢
xhr.onload = function () {
    if (xhr.status === 200 || xhr.status === 304) {
        console.log(xhr.responseText);
    } else {
        console.log(xhr.statusText);
    }
}

xhr對象的響應數據中包含幾個屬性:

response    // 響應的數據
responseURL    // 發(fā)起響應的URL
responseType    // 響應的類型,用于瀏覽器強行重置響應數據的類型
responseText    // 如果為普通文本,則在這顯示
responseXML    // 如果為xml類型文本,在這里顯示

數據會出現在responseText/responseXML中的哪一個,取決于服務器返回的MIME類型,當然我們也有一些方式在瀏覽器端設置如何處理這些數據:

// xhr v1 的寫法,設置響應資源的處理類型
xhr.overrideMimeType("text/xml");

// xhr v2 的寫法, 可用值為 arraybuffer/blob/document/json/text
xhr.responseType = "document";

響應數據相關的屬性默認為null / "",只有當請求完成并被正確解析的時候才會有值,取決于responseType的值,來確定response/responseText/responseXML誰最終具有值。

XHR的高級功能

xhr v2里提供了超時和進度事件。

超時
xhr.timeout = 1000;    // 1分鐘,單位為ms
xhr.ontimeout = function () {};

在請求send()之后開始計時,等待timeout時長后,如果沒有收到響應,則觸發(fā)ontimeout()事件,超時會將readystate=4,直接觸發(fā)onreadystatechange()事件。

請求進度

像上圖所示,xhr v2定義了不同的進度事件:loadstart/progress/error/abort/load/loadend,這其中我們已經說過了onload()事件為內容加載完成可用?,F在說一下onprogress()進度事件:

xhr.onprogress = function (event) {
    if (event.lengthComputable) {
        console.log(event.loaded / event.total);
    }
}

該事件會接收一個event對象,其target屬性為該xhr對象,lengthComputable屬性為total size是否已知,即是否可用進度信息,loaded屬性為已經接收的字節(jié)數,total為總字節(jié)數。該事件會在數據接收期間不斷觸發(fā),但間隔不確定。

跨域CORS

提到XHR對象,我們就會講到跨域問題,它是為了預防某些惡意行為的安全策略,但有時候我們需要跨域來實現某些功能。需要注意的是跨域并不僅僅是前端單方面的事情,它需要后端代碼進行配合,我們只是通過一些方式跳過了瀏覽器的阻攔。

對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發(fā)起實際的 HTTP 請求。

CORS(Cross-Origin Resource Sharing, 跨域資源共享)的思想是瀏覽器和服務端通過頭部信息來進行溝通確認是否給予響應。如:

Origin: http://www.baidu.com    // 瀏覽器的頭部信息

// 如果服務端認可這個域名的跨域請求,如下設置就可跨域訪問資源
Access-Control-Allow-Origin: http://www.baidu.com

如上就可以實現最簡單的跨域訪問,但是此時不能攜帶任何的cookie,如果我們需要傳遞cookie進行身份認證,需要設置:

xhr.withCredentials = true;    // 瀏覽器端
Access-Control-Allow-Credentials: true;    // 服務端

這樣我們就可以傳遞認證信息了,但如果允許認證,Access-Control-Allow-Origin不能設置為*,而一定是具體的域名信息。

現在的瀏覽器都對CORS有了實現,如IE使用XDomainRequest對象,其它瀏覽器使用XMLHttpRequest對象。所以在此之前有很多奇技淫巧,如通過jsonp/圖像 Ping方法都不再詳述,而且其都需要服務端配合并且有很多局限性。

IE實現: XDomainRequest
var xdr = new XDomainRequest();
xdr.open("get", "http://www.site.com/page");
xdr.send(null);

XDR區(qū)別于普通XHR:

不能傳輸cookie

只能設置請求頭部的content-type

不能訪問響應頭部信息

只支持get/post方法

通過這些區(qū)別可以阻止一部分的CSRF(Cross-Site Request Forgery,跨站點請求偽造)XSS(Cross-Site Scripting,跨站點腳本)

XDR與XHR的使用上非常相似,區(qū)別有幾點:

open()方法只接受兩個參數,請求類型和URL

只允許異步請求

響應完成觸發(fā)onload()事件,但我們只能訪問responseText原始文本,并且無法獲取響應的status.

異常事件都會觸發(fā)error事件,并且無錯誤信息可用。

其余瀏覽器實現: XMLHttpRequest

其余瀏覽器通過XHR對象直接實現了CORS,你只需要做的就是open()方法中傳入一個絕對URL。

xhr.open("get", "http://www.site.com/page", true);

相對于普通的XHR對象,CORS-XHR依然有部分限制:

不能使用setRequestHeader()定義頭部

不能傳遞cookie

調用getAllResponseHeaders(),結果為空

其余跨域方法

上面的兩種方法已經很成熟了,但是仍然有一部分方法可以跨域,比如圖像Ping

var img = new Image();
img.onload = img.onerror = function () {
    console.log("done");
}
img.src = "http://www.site.com/test?name=Nicholas";

這種方式常用于服務端統(tǒng)計廣告的點擊次數,其缺陷為:

只能是GET請求

單向通信,無法獲取響應文本

另外還有JSONP

function handleResponse(response) {
    console.log(response.ip, response.city);
}

var script = document.createElement("script");
script.src = "http://freegeoip.net/json?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

這種方式通過和服務器配合,跨域請求一個js文件并被服務器處理后傳回:

handleResponse({"name": "Nicholas"});

然后直接在瀏覽器調用了該函數,傳回的數據被當做response形參進行處理。但它也有一些缺陷:

訪問的方式是請求js,所以如果域名不安全,則很容易被惡意代碼直接執(zhí)行并攻擊

無法檢測是否錯誤,因為js不支持這樣的接口事件,只能超時判斷

上面兩種方式很容易看出,我們在支持CORS之前,使用的方法只不過是采用img/css/js等不受跨域訪問限制的對象,變相拿到了響應數據,但都有缺陷,所以如果沒有歷史包袱,建議采用XDR或XHR對象來實現跨域訪問。

XHR的兼容性

我們可以直接到Can I use這個網站上查詢兼容性問題:

參考資料

MDN - 禁止修改的消息首部:https://developer.mozilla.org...

理解HTTP之Content-Type:http://homeway.me/2015/07/19/...

MDN - Content-Type:https://developer.mozilla.org...

HTTP請求方法:https://developer.mozilla.org...

Javascript高級程序設計 第21章(Ajax與Comet)

你真的會使用XHR嗎? https://segmentfault.com/a/11...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/89027.html

相關文章

  • ajax跨域,這應該是最全的解決方案了

    摘要:關于,強烈推薦閱讀跨域資源共享詳解阮一峰另外,這里也整理了一個實現原理圖簡化版如何判斷是否是簡單請求瀏覽器將請求分成兩類簡單請求和非簡單請求。 前言 從剛接觸前端開發(fā)起,跨域這個詞就一直以很高的頻率在身邊重復出現,一直到現在,已經調試過N個跨域相關的問題了,16年時也整理過一篇相關文章,但是感覺還是差了點什么,于是現在重新梳理了一下。 個人見識有限,如有差錯,請多多見諒,歡迎提出iss...

    ytwman 評論0 收藏0
  • 20K前端大佬面試(關于如何回答ajax跨域問題)

    摘要:在接觸前端開發(fā)起,跨域這個詞就一直以很高的頻率在我們學習工作中重復出現,最近在工作中遇到了跨域的相關問題,這里我把它總結記錄一下。 在接觸前端開發(fā)起,跨域這個詞就一直以很高的頻率在我們學習工作中重復出現,最近在工作中遇到了跨域的相關問題,這里我把它總結記錄一下。關于跨域,有N種類型,現在我只專注于ajax請求跨域(ajax跨域只是屬于瀏覽器同源策略中的一部分,其它的這里不做介紹),內容...

    Yangyang 評論0 收藏0
  • 跨域知識梳理

    摘要:摘自阮一峰博客延伸閱讀不涉及跨域跨源資源分享為標準兼容性參考優(yōu)點提供安全的跨域數據傳輸,且不限于請求??缬蛸Y源共享阿里云技術文檔跨域資源共享詳解阮一峰 參考:維基百科 - Root domainhttps://en.wikipedia.org/wiki...瀏覽器同源政策及其規(guī)避方法 - 阮一峰http://www.ruanyifeng.com/blo...window.name 跨域...

    SwordFly 評論0 收藏0
  • 同源策略CORS跨域

    摘要:所以瀏覽器認為這是安全的。如果你細心的話你會發(fā)現,其實請求已經發(fā)送出去了,你只是拿不到響應而已。所以瀏覽器這個策略的本質是,一個域名的,在未經允許的情況下,不得讀取另一個域名的內容。但瀏覽器并不阻止你向另一個域名發(fā)送請求。 同源策略與CORS跨域 PS:這篇文章是緊接著JSONP原理和Ajax學習與理解寫的,有些內容是承接了上兩篇文章.這篇文章只算是我的個人學習筆記,內容沒有經過精心排...

    ninefive 評論0 收藏0

發(fā)表評論

0條評論

jeyhan

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<