摘要:導讀全稱即異步與它最早在中被使用然后由推廣開來典型的代表應用有以及現代網頁中幾乎無不歡前后端分離也正是建立在異步通信的基礎之上瀏覽器為做了什么現代瀏覽器中雖然幾乎全部支持但它們的技術方案卻分為兩種標準瀏覽器通過對象實現了的功能只需要通過一行
導讀
Ajax 全稱 Asynchronous JavaScript and XML, 即異步JS與XML. 它最早在IE5中被使用, 然后由Mozilla, Apple, Google推廣開來. 典型的代表應用有 Outlook Web Access, 以及 GMail. 現代網頁中幾乎無ajax不歡. 前后端分離也正是建立在ajax異步通信的基礎之上.
瀏覽器為ajax做了什么現代瀏覽器中, 雖然幾乎全部支持ajax, 但它們的技術方案卻分為兩種:
① 標準瀏覽器通過 XMLHttpRequest 對象實現了ajax的功能. 只需要通過一行語句便可創建一個用于發送ajax請求的對象.
var xhr = new XMLHttpRequest();
② IE瀏覽器通過 XMLHttpRequest 或者 ActiveXObject 對象同樣實現了ajax的功能.
MSXML鑒于IE系列各種 "神級" 表現, 我們先來看看IE瀏覽器風騷的走位.
IE下的使用環境略顯復雜, IE7及更高版本瀏覽器可以直接使用BOM的 XMLHttpRequest 對象. MSDN傳送門: Native XMLHTTPRequest object. IE6及更低版本瀏覽器只能使用 ActiveXObject 對象來創建 XMLHttpRequest 對象實例. 創建時需要指明一個類似"Microsoft.XMLHTTP"這樣的ProgID. 而實際呢, windows系統環境下, 以下ProgID都應該可以創建XMLHTTP對象:
Microsoft.XMLHTTP Microsoft.XMLHTTP.1.0 Msxml2.ServerXMLHTTP Msxml2.ServerXMLHTTP.3.0 Msxml2.ServerXMLHTTP.4.0 Msxml2.ServerXMLHTTP.5.0 Msxml2.ServerXMLHTTP.6.0 Msxml2.XMLHTTP Msxml2.XMLHTTP.3.0 Msxml2.XMLHTTP.4.0 Msxml2.XMLHTTP.5.0 Msxml2.XMLHTTP.6.0
簡言之, Microsoft.XMLHTTP 已經非常老了, 主要用于提供對歷史遺留版本的支持, 不建議使用.對于 MSXML4, 它已被 MSXML6 替代; 而 MSXML5 又是專門針對office辦公場景, 在沒有安裝 Microsoft Office 2003 及更高版本辦公軟件的情況下, MSXML5 未必可用. 相比之下, MSXML6 具有比 MSXML3 更穩定, 更高性能, 更安全的優勢, 同時它也提供了一些 MSXML3 中沒有的功能, 比如說 XSD schema. 唯一遺憾的是, MSXML6 只在 vista 系統及以上才是默認支持的; 而 MSXML3 在 Win2k SP4及以上系統就是可用的. 因此一般情況下, MSXML3 可以作為 MSXML6 的優雅降級方案, 我們通過指定 PorgID 為 Msxml2.XMLHTTP 即可自動映射到 Msxml2.XMLHTTP.3.0. 如下所示:
var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3,等同于如下語句 var xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");
MSDN有篇文章專門講解了各個版本的MSXML. 傳送門: Using the right version of MSXML in Internet Explorer.
親測了 IE5, IE5.5, IE6, IE7, IE8, IE9, IE10, IE edge等瀏覽器, IE5及之后的瀏覽器均可以通過如下語句獲取xhr對象:
var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3 var xhr = new ActiveXObject("Microsoft.XMLHTTP");// 很老的api,雖然瀏覽器支持,功能可能不完善,故不建議使用
以上, 思路已經很清晰了, 下面給出個全兼容的方法.
全平臺兼容的XMLHttpRequest對象function getXHR(){ var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("您的瀏覽器暫不支持Ajax!"); } } } return xhr; }ajax有沒有破壞js單線程機制
對于這個問題, 我們先看下瀏覽器線程機制. 一般情況下, 瀏覽器有如下四種線程:
GUI渲染線程
javascript引擎線程
瀏覽器事件觸發線程
HTTP請求線程
那么這么多線程, 它們究竟是怎么同js引擎線程交互的呢?
通常, 它們的線程間交互以事件的方式發生, 通過事件回調的方式予以通知. 而事件回調, 又是以先進先出的方式添加到任務隊列 的末尾 , 等到js引擎空閑時, 任務隊列 中排隊的任務將會依次被執行. 這些事件回調包括 setTimeout, setInterval, click, ajax異步請求等回調.
瀏覽器中, js引擎線程會循環從 任務隊列 中讀取事件并且執行, 這種運行機制稱作 Event Loop (事件循環).
對于一個ajax請求, js引擎首先生成 XMLHttpRequest 實例對象, open過后再調用send方法. 至此, 所有的語句都是同步執行. 但從send方法內部開始, 瀏覽器為將要發生的網絡請求創建了新的http請求線程, 這個線程獨立于js引擎線程, 于是網絡請求異步被發送出去了. 另一方面, js引擎并不會等待 ajax 發起的http請求收到結果, 而是直接順序往下執行.
當ajax請求被服務器響應并且收到response后, 瀏覽器事件觸發線程捕獲到了ajax的回調事件 onreadystatechange (當然也可能觸發onload, 或者 onerror等等) . 該回調事件并沒有被立即執行, 而是被添加到 任務隊列 的末尾. 直到js引擎空閑了, 任務隊列 的任務才被撈出來, 按照添加順序, 挨個執行, 當然也包括剛剛append到隊列末尾的 onreadystatechange 事件.
在 onreadystatechange 事件內部, 有可能對dom進行操作. 此時瀏覽器便會掛起js引擎線程, 轉而執行GUI渲染線程, 進行UI重繪(repaint)或者回流(reflow). 當js引擎重新執行時, GUI渲染線程又會被掛起, GUI更新將被保存起來, 等到js引擎空閑時立即被執行.
以上整個ajax請求過程中, 有涉及到瀏覽器的4種線程. 其中除了 GUI渲染線程 和 js引擎線程 是互斥的. 其他線程相互之間, 都是可以并行執行的. 通過這樣的一種方式, ajax并沒有破壞js的單線程機制.
ajax與setTimeout排隊問題通常, ajax 和 setTimeout 的事件回調都被同等的對待, 按照順序自動的被添加到 任務隊列 的末尾, 等待js引擎空閑時執行. 但請注意, 并非xhr的所有回調執行都滯后于setTImeout的回調. 請看如下代碼:
function ajax(url, method){ var xhr = getXHR(); xhr.onreadystatechange = function(){ console.log("xhr.readyState:" + this.readyState); } xhr.onloadstart = function(){ console.log("onloadStart"); } xhr.onload = function(){ console.log("onload"); } xhr.open(method, url, true); xhr.setRequestHeader("Cache-Control",3600); xhr.send(); } var timer = setTimeout(function(){ console.log("setTimeout"); },0); ajax("http://louiszhai.github.io/docImages/ajax01.png","GET");
上述代碼執行結果如下圖:
由于ajax異步, setTimeout回調本應該最先被執行, 然而實際上, 一次ajax請求, 并非所有的部分都是異步的, 至少"readyState==1"的 onreadystatechange 回調以及 onloadstart 回調就是同步執行的. 因此它們的輸出排在最前面.
XMLHttpRequest 屬性解讀首先在Chrome console下創建一個 XMLHttpRequest 實例對象xhr. 如下所示:
inherit試運行以下代碼.
var xhr = new XMLHttpRequest(), i=0; for(var key in xhr){ if(xhr.hasOwnProperty(key)){ i++; } } console.log(i);//0 console.log(XMLHttpRequest.prototype.hasOwnProperty("timeout"));//true
可見, XMLHttpRequest 實例對象沒有自有屬性. 實際上, 它的所有屬性均來自于 XMLHttpRequest.prototype .
追根溯源, XMLHttpRequest 實例對象具有如下的繼承關系. (下面以a<
xhr << XMLHttpRequest.prototype << XMLHttpRequestEventTarget.prototype << EventTarget.prototype << Object.prototype
由上, xhr也具有Object等原型中的所有方法. 如toString方法.
xhr.toString();//"[object XMLHttpRequest]"
通常, 一個xhr實例對象擁有10個普通屬性+9個方法.
readyState只讀屬性, readyState屬性記錄了ajax調用過程中所有可能的狀態. 它的取值簡單明了, 如下:
readyState | 對應常量 | 描述 |
---|---|---|
0 (未初始化) | xhr.UNSENT | 請求已建立, 但未初始化(此時未調用open方法) |
1 (初始化) | xhr.OPENED | 請求已建立, 但未發送 (已調用open方法, 但未調用send方法) |
2 (發送數據) | xhr.HEADERS_RECEIVED | 請求已發送 (send方法已調用, 已收到響應頭) |
3 (數據傳送中) | xhr.LOADING | 請求處理中, 因響應內容不全, 這時通過responseBody和responseText獲取可能會出現錯誤 |
4 (完成) | xhr.DONE | 數據接收完畢, 此時可以通過通過responseBody和responseText獲取完整的響應數據 |
注意, readyState 是一個只讀屬性, 想要改變它的值是不可行的.
onreadystatechangeonreadystatechange事件回調方法在readystate狀態改變時觸發, 在一個收到響應的ajax請求周期中, onreadystatechange 方法會被觸發4次. 因此可以在 onreadystatechange 方法中綁定一些事件回調, 比如:
xhr.onreadystatechange = function(e){ if(xhr.readystate==4){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } } }
注意: onreadystatechange回調中默認會傳入Event實例, 如下:
status只讀屬性, status表示http請求的狀態, 初始值為0. 如果服務器沒有顯式地指定狀態碼, 那么status將被設置為默認值, 即200.
statusText只讀屬性, statusText表示服務器的響應狀態信息, 它是一個 UTF-16 的字符串, 請求成功且status==20X時, 返回大寫的 OK . 請求失敗時返回空字符串. 其他情況下返回相應的狀態描述. 比如: 301的 Moved Permanently , 302的 Found , 303的 See Other , 307 的 Temporary Redirect , 400的 Bad Request , 401的 Unauthorized 等等.
onloadstartonloadstart事件回調方法在ajax請求發送之前觸發, 觸發時機在 readyState==1 狀態之后, readyState==2 狀態之前.
onloadstart方法中默認將傳入一個ProgressEvent事件進度對象. 如下:
ProgressEvent對象具有三個重要的Read only屬性.
lengthComputable 表示長度是否可計算, 它是一個布爾值, 初始值為false.
loaded 表示已加載資源的大小, 如果使用http下載資源, 它僅僅表示已下載內容的大小, 而不包括http headers等. 它是一個無符號長整型, 初始值為0.
total 表示資源總大小, 如果使用http下載資源, 它僅僅表示內容的總大小, 而不包括http headers等, 它同樣是一個無符號長整型, 初始值為0.
onprogressonprogress事件回調方法在 readyState==3 狀態時開始觸發, 默認傳入 ProgressEvent 對象, 可通過 e.loaded/e.total 來計算加載資源的進度, 該方法用于獲取資源的下載進度.
注意: 該方法適用于 IE10+ 及其他現代瀏覽器.
xhr.onprogress = function(e){ console.log("progress:", e.loaded/e.total); }onload
onload事件回調方法在ajax請求成功后觸發, 觸發時機在 readyState==4 狀態之后.
想要捕捉到一個ajax異步請求的成功狀態, 并且執行回調, 一般下面的語句就足夠了:
xhr.onload = function(){ var s = xhr.status; if((s >= 200 && s < 300) || s == 304){ var resp = xhr.responseText; //TODO ... } }onloadend
onloadend事件回調方法在ajax請求完成后觸發, 觸發時機在 readyState==4 狀態之后(收到響應時) 或者 readyState==2 狀態之后(未收到響應時).
onloadend方法中默認將傳入一個ProgressEvent事件進度對象.
timeouttimeout屬性用于指定ajax的超時時長. 通過它可以靈活地控制ajax請求時間的上限. timeout的值滿足如下規則:
通常設置為0時不生效.
設置為字符串時, 如果字符串中全部為數字, 它會自動將字符串轉化為數字, 反之該設置不生效.
設置為對象時, 如果該對象能夠轉化為數字, 那么將設置為轉化后的數字.
xhr.timeout = 0; //不生效 xhr.timeout = "123"; //生效, 值為123 xhr.timeout = "123s"; //不生效 xhr.timeout = ["123"]; //生效, 值為123 xhr.timeout = {a:123}; //不生效ontimeout
ontimeout方法在ajax請求超時時觸發, 通過它可以在ajax請求超時時做一些后續處理.
xhr.ontimeout = function(e) { console.error("請求超時!!!") }response responseText
均為只讀屬性, response表示服務器的響應內容, 相應的, responseText表示服務器響應內容的文本形式.
responseXML只讀屬性, responseXML表示xml形式的響應數據, 缺省為null, 若數據不是有效的xml, 則會報錯.
responseTyperesponseType表示響應的類型, 缺省為空字符串, 可取 "arraybuffer" , "blob" , "document" , "json" , and "text" 共五種類型.
responseURLresponseURL返回ajax請求最終的URL, 如果請求中存在重定向, 那么responseURL表示重定向之后的URL.
withCredentialswithCredentials是一個布爾值, 默認為false, 表示跨域請求中不發送cookies等信息. 當它設置為true時, cookies , authorization headers 或者 TLS客戶端證書 都可以正常發送和接收. 顯然它的值對同域請求沒有影響.
注意: 該屬性適用于 IE10+, opera12+及其他現代瀏覽器.
abortabort方法用于取消ajax請求, 取消后, readyState 狀態將被設置為?0?(UNSENT). 如下, 調用abort 方法后, 請求將被取消.
getResponseHeadergetResponseHeader方法用于獲取ajax響應頭中指定name的值. 如果response headers中存在相同的name, 那么它們的值將自動以字符串的形式連接在一起.
console.log(xhr.getResponseHeader("Content-Type"));//"text/html"getAllResponseHeaders
getAllResponseHeaders方法用于獲取所有安全的ajax響應頭, 響應頭以字符串形式返回. 每個HTTP報頭名稱和值用冒號分隔, 如key:value, 并以rn結束.
xhr.onreadystatechange = function() { if(this.readyState == this.HEADERS_RECEIVED) { console.log(this.getAllResponseHeaders()); } } //Content-Type: text/html"
以上, readyState === 2 狀態時, 就意味著響應頭已接受完整. 此時便可以打印出完整的 response headers.
setRequestHeader既然可以獲取響應頭, 那么自然也可以設置請求頭, setRequestHeader就是干這個的. 如下:
//指定請求的type為json格式 xhr.setRequestHeader("Content-type", "application/json"); //除此之外, 還可以設置其他的請求頭 xhr.setRequestHeader("x-requested-with", "123456");onerror
onerror方法用于在ajax請求出錯后執行. 通常只在網絡出現問題時或者ERR_CONNECTION_RESET時觸發(如果請求返回的是407狀態碼, chrome下也會觸發onerror).
uploadupload屬性默認返回一個 XMLHttpRequestUpload 對象, 用于上傳資源. 該對象具有如下方法:
onloadstart
onprogress
onabort
onerror
onload
ontimeout
onloadend
上述方法功能同 xhr 對象中同名方法一致. 其中, onprogress 事件回調方法可用于跟蹤資源上傳的進度.
xhr.upload.onprogress = function(e){ var percent = 100 * e.loaded / e.total |0; console.log("upload: " + precent + "%"); }overrideMimeType
overrideMimeType方法用于強制指定response 的 MIME 類型, 即強制修改response的 Content-Type . 如下, 服務器返回的response的 MIME 類型為 text/plain .
xhr.getResponseHeader("Content-Type");//"text/plain" xhr.responseXML;//null
通過overrideMimeType方法將response的MIME類型設置為 text/xml;charset=utf-8 , 如下所示:
xhr.overrideMimeType("text/xml; charset = utf-8"); xhr.send();
此時雖然 response headers 如上圖, 沒有變化, 但 Content-Type 已替換為新值.
xhr.getResponseHeader("Content-Type");//"text/xml; charset = utf-8"
此時, xhr.responseXML 也將返回DOM對象, 如下圖.
XHR一級XHR1 即 XMLHttpRequest Level 1. XHR1時, xhr對象具有如下缺點:
僅支持文本數據傳輸, 無法傳輸二進制數據.
傳輸數據時, 沒有進度信息提示, 只能提示是否完成.
受瀏覽器 同源策略 限制, 只能請求同域資源.
沒有超時機制, 不方便掌控ajax請求節奏.
XHR二級XHR2 即 XMLHttpRequest Level 2. XHR2針對XHR1的上述缺點做了如下改進:
支持二進制數據, 可以上傳文件, 可以使用FormData對象管理表單.
提供進度提示, 可通過 xhr.upload.onprogress 事件回調方法獲取傳輸進度.
依然受 同源策略 限制, 這個安全機制不會變. XHR2新提供 Access-Control-Allow-Origin 等headers, 設置為 * 時表示允許任何域名請求, 從而實現跨域CORS訪問(有關CORS詳細介紹請耐心往下讀).
可以設置timeout 及 ontimeout, 方便設置超時時長和超時后續處理.
這里就H5新增的FormData對象舉個例.
//可直接創建FormData實例 var data = new FormData(); data.append("name", "louis"); xhr.send(data); //還可以通過傳入表單DOM對象來創建FormData實例 var form = document.getElementById("form"); var data = new FormData(form); data.append("password", "123456"); xhr.send(data);
目前, 主流瀏覽器基本上都支持XHR2, 除了IE系列需要IE10及更高版本. 因此IE10以下是不支持XHR2的.
那么問題來了, IE7, 8,9的用戶怎么辦? 很遺憾, 這些用戶是比較尷尬的. 對于IE8,9而言, 只有一個閹割版的 XDomainRequest 可用,IE7則沒有. 估計IE7用戶只能哭暈在廁所了.
XDomainRequestXDomainRequest 對象是IE8,9折騰出來的, 用于支持CORS請求非成熟的解決方案. 以至于IE10中直接移除了它, 并重新回到了 XMLHttpRequest 的懷抱.
XDomainRequest 僅可用于發送 GET 和 POST 請求. 如下即創建過程.
var xdr = new XDomainRequest();
xdr具有如下屬性:
timeout
responseText
如下方法:
open: 只能接收Method,和url兩個參數. 只能發送異步請求.
send
abort
如下事件回調:
onprogress
ontimeout
onerror
onload
除了缺少一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.
必須要明確的是:
XDomainRequest 不支持跨域傳輸cookie.
只能設置請求頭的Content-Type字段, 且不能訪問響應頭信息.
$.ajax$.ajax是jquery對原生ajax的一次封裝. 通過封裝ajax, jquery抹平了不同版本瀏覽器異步http的差異性, 取而代之的是高度統一的api. jquery作為js類庫時代的先驅, 對前端發展有著深遠的影響. 了解并熟悉其ajax方法, 不可謂不重要.
參數列表$.ajax() 只有一個參數, 該參數為key-value設置對象. 實際上, jq發送的所有ajax請求, 都是通過調用該ajax方法實現的. 它的詳細參數如下表:
序號 | 參數 | 類型 | 描述 |
---|---|---|---|
1 | accepts | PlainObject | 用于通知服務器該請求需要接收何種類型的返回結果. 如有必要, 推薦在 $.ajaxSetup()?方法中設置一次. |
2 | async | Boolean | 默認為true, 即異步. |
3 | beforeSend | Function | 請求發送前的回調, 默認傳入參數jqXHR和settings. 函數內顯式返回false將取消本次請求. |
4 | cache | Boolean | 請求是否開啟緩存, 默認為true, 如不需要緩存請設置為false. 不過, dataType為"script"和"jsonp"時默認為false. |
5 | complete | Function | 請求完成后的回調(請求success?和?error之后均調用), 默認傳入參數jqXHR和textStatus(請求狀態, 取值為 "success","notmodified","error","timeout","abort","parsererror"之一). 從jq1.5開始, complete可以設置為一個包含函數的數組. 如此每個函數將依次被調用. |
6 | contents | PlainObject | 一個以"{字符串/正則表達式}"配對的對象, 根據給定的內容類型, 解析請求的返回結果. |
7 | contentType | String | 編碼類型, 相對應于http請求頭域的"Content-Type"字段. 默認值為"application/x-www-form-urlencoded; charset=UTF-8". |
8 | context | Object | 設置ajax回調函數的上下文. 默認上下文為ajax請求傳入的參數設置對象. 如設置為document.body, 那么所有ajax回調函數中將以body為上下文. |
9 | converters | PlainObject | 一個數據類型到數據類型轉換器的對象. 默認為 {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML} . 如設置converters:{"json jsonp": function(msg){}} |
10 | crossDomain | Boolean | 默認同域請求為false, 跨域請求為true. |
11 | data | Object, Array | 發送到服務器的數據, 默認data為鍵值對格式對象, 若data為數組則按照traditional參數的值, 自動轉化為一個同名的多值查詢字符串. 如{a:1,b:2}將轉換為"&a=1&b=2". |
12 | dataFilter | Function | 處理XMLHttpRequest原始響應數據的回調, 默認傳入data和type參數, data是Ajax返回的原始數據, type是調用$.ajax時提供的dataType參數 |
13 | dataType | String | 預期服務器返回的數據類型, 可設置為"xml","html","script","json","jsonp","text"之一, 其中設置為"xml"或"text"類型時, 數據不會經過處理. |
14 | error | Function | 請求失敗時的回調函數, 默認傳入jqXHR(jq1.4以前為原生xhr對象),textStatus(請求狀態,取值為null,"timeout","error","abort" 或 "parsererror"),errorString(錯誤內容), 當一個HTTP錯誤發生時, errorThrown?接收HTTP狀態的文本部分,比如"Not Found"等. 從jq1.5開始, error可以設置為一個包含函數的數組. 如此每個函數將依次被調用.注意: 跨域腳本和JSONP請求時error不被調用. |
15 | global | Boolean | 表示是否觸發全局ajax事件, 默認為true. 設為false將不再觸發ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站腳本和jsonp請求, 該值自動設置為false. |
16 | headers | PlainObject | 設置請求頭, 格式為k-v鍵值對對象. 由于該設置會在beforeSend函數被調用之前生效, 因此可在beforeSend函數內覆蓋該對象. |
17 | ifModified | Boolean | 只有上次請求響應改變時, 才允許請求成功. 它使用HTTP包的Last-Modified 頭信息判斷, 默認為false. 若設置為true, 且數據自從上次請求后沒有更改過就會報錯. |
18 | isLocal | Boolean | 運行當前環境設置為"本地",默認為false, 若設置為true, 將影響請求發送時的協議. |
19 | jsonp | String | 顯式指定jsonp請求中的回調函數的名稱. 如jsonp:cb, jq會將cb代替callback, 以 "cb=?"傳給服務器. 從jq1.5開始, 若設置jsonp:false, 那么需要明確設置jsonpCallback:"callbackName". |
20 | jsonpCallback | String,Function | 為jsonp請求指定一個回調函數名, 以取代jq自動生成的隨機函數名. 從jq1.5開始, 可以將該屬性設置為一個函數, 函數的返回值就是jsonpCallback的結果. |
21 | mimeType | String | 設置一個MIME類型, 以覆蓋xhr的MIM類型(jq1.5新增) |
22 | password | String | 設置認證請求中的密碼 |
23 | processData | Boolean | jq的ajax方法默認會將傳入的data隱式轉換為查詢字符串(如"&a=1&b=2"), 以配合 默認內容類型 "application/x-www-form-urlencoded", 如果不希望轉換請設置為false. angular中想要禁用默認轉換, 需要重寫transformRequest方法. |
24 | scriptCharset | String | 僅在"script"請求中使用(如跨域jsonp, dataType為"script"類型). 顯式指定時, 請求中將在script標簽上設置charset屬性, 可在發現本地和遠程編碼不一致時使用. |
25 | statusCode | PlainObject | 一組http狀態碼和回調函數對應的鍵值對對象. 該對象以 {404:function(){}} 這種形式表示. 可用于根據不同的http狀態碼, 執行不同的回調.(jq1.5新增) |
26 | timeout | Number | 設置超時時間. |
27 | traditional | Boolean | 是否按照默認方式序列化data對象, 默認值為false. |
28 | type | String | 可以設置為8種http method之一, jq中不區分大小寫. |
29 | url | String | 請求的uri地址. |
30 | username | String | 設置認證請求中的用戶名 |
31 | xhr | Function | 在回調內創建并返回xhr對象 |
32 | xhrFields | PlainObject | 鍵值對對象, 用于設置原生的xhr對象, 如可用來設置withCredentials:true(jq1.5.1新增) |
$.ajax() 方法返回jqXHR對象(jq1.5起), 如果使用的不是XMLHttpRequest對象時, 如jsonp請求, 返回的jqXHR對象將盡可能模擬原生的xhr. 從jq1.5起, 返回的jqXHR對象實現了promise接口, 具有如下新方法.
新方法 | 被替代的老方法(jq1.8起棄用) |
---|---|
done(function(data, textStatus, jqXHR) {}) | |
fail(function(jqXHR, textStatus, errorThrown) {}) | |
always(function(data or jqXHR, textStatus, jqXHR or errorThrown) {}) |
從jq1.6開始, done, fail, always按照FIFO隊列可以分配多個回調.
使用轉換器$.ajax() 的轉換器可以將支持的數據類型映射到其它數據類型. 如果需要將自定義數據類型映射到已知的類型. 需要使用 contents 選項在響應的 "Content-Type" 和實際數據類型之間添加一個轉換函數.
$.ajaxSetup({ contents: { myContentType: /myContentType/ }, converters: { "myContentType json": function(data) { //TODO something return newData; } } });
轉換一個支持的類型為自定義類型, 然后再返回. 如 text—>myContentType—>json.
$.ajaxSetup({ contents: { myContentType: /myContentType/ }, converters: { "text myContentType": true, "myContentType json": function(data) { //TODO something return newData; } } });事件觸發順序
$.ajax()方法觸發的事件紛繁復雜, 有將近20個之多. 為了囊括最多的事件, 這里以一次成功的上傳請求為例, 以下是它們的調用順序(請求出現錯誤時的順序, 請自行對應).
序號 | 事件名稱 | 是否全局事件 | 是否能關閉 | 默認形參 |
---|---|---|---|---|
1 | $.ajaxPrefilter | ?? | ? | function(options, originalOptions, jqXHR){} |
2 | $(document).ajaxStar | ?? | ?? | function(){}(只在當前無激活ajax時觸發) |
3 | beforeSend | ? | - | function(jqXHR, settings){} |
4 | $(document).ajaxSend | ?? | ?? | function(){} |
5 | xhr.onloadstart | - | - | ProgressEvent |
6 | xhr.upload.onloadstart | - | - | ProgressEvent |
7 | xhr.upload.onprogress | - | - | ProgressEvent |
8 | xhr.upload.onload | - | - | ProgressEvent |
9 | xhr.upload.onloadend | - | - | ProgressEvent |
10 | xhr.onprogress | - | - | ProgressEvent |
11 | xhr.onload | - | - | ProgressEvent |
12 |
|
? | - | function(data, textStatus, jqXHR){} |
13 | $(document).ajaxSuccess | ?? | ?? | function(event, jqXHR, options){} |
14 |
|
? | - | function(jqXHR, textStatus){} |
15 | $(document).ajaxComplete | ?? | ?? | function(event, jqXHR, textStatus) |
16 | $(document).ajaxStop | ?? | ?? | function(){} |
17 | xhr.onloadend | - | - | ProgressEvent |
從jq1.8起, 對于函數 ajaxStart, ajaxSend, ajaxSuccess, ajaxComplete, ajaxStop , 只能為document對象綁定事件處理函數, 為其他元素綁定的事件處理函數不會起作用.
Axios實際上, 如果你僅僅只是想要一個不錯的http庫, 相比于龐大臃腫的jquery, 短小精悍的Axios可能更加適合你. 原因如下:
Axios支持node, jquery并不支持.
Axios基于promise語法, jq3.0才開始全面支持.
Axios短小精悍, 更加適合http場景, jquery大而全, 加載較慢.
vue作者尤大放棄推薦vue-resource, 轉向推薦Axios. 以下為尤大原話.
"最近團隊討論了一下, Ajax 本身跟 Vue 并沒有什么需要特別整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等都可以起到同等的效果, vue-resource 提供的價值和其維護成本相比并不劃算, 所以決定在不久以后取消對 vue-resource 的官方推薦."
Axios大小僅12k, 目前最新版本號為:
語法上Axios基本就和promise一樣, 在then方法中處理回調, 在catch方法中處理異常. 如下:
axios.get("https://api.github.com/users/louiszhai") .then(function(response){ console.log(response); }) .catch(function (error) { console.log(error); });
除了get, 它還支持post, delete, head, put, patch, request請求. 具體使用攻略, 請戳這里: axios .
如需在網頁上引入 Axios, 可以鏈接CDN axios | Bootstrap中文網開源項目免費 CDN 服務 或者將其下載到本地.
Fetch說到ajax, 就不得不提及fetch, 由于篇幅較長, fetch已從本文中獨立出來, 請戳 Fetch進階指南 .
ajax跨域請求 什么是CORSCORS是一個W3C(World Wide Web)標準, 全稱是跨域資源共享(Cross-origin resource sharing).它允許瀏覽器向跨域服務器, 發出異步http請求, 從而克服了ajax受同源策略的限制. 實際上, 瀏覽器不會攔截不合法的跨域請求, 而是攔截了他們的響應, 因此即使請求不合法, 很多時候, 服務器依然收到了請求.(Chrome和Firefox下https網站不允許發送http異步請求除外)
通常, 一次跨域訪問擁有如下流程:
移動端CORS兼容性當前幾乎所有的桌面瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通過名為跨域資源共享的協議支持ajax跨域調用.
那么移動端兼容性又如何呢? 請看下圖:
可見, CORS的技術在IOS Safari7.1及Android webview2.3中就早已支持, 即使低版本下webview的canvas在使用跨域的video或圖片時會有問題, 也絲毫不影響CORS的在移動端的使用. 至此, 我們就可以放心大膽的去應用CORS了.
CORS有關的headers1) HTTP Response Header(服務器提供):
Access-Control-Allow-Origin: 指定允許哪些源的網頁發送請求.
Access-Control-Allow-Credentials: 指定是否允許cookie發送.
Access-Control-Allow-Methods: 指定允許哪些請求方法.
Access-Control-Allow-Headers: 指定允許哪些常規的頭域字段, 比如說 Content-Type.
Access-Control-Expose-Headers: 指定允許哪些額外的頭域字段, 比如說 X-Custom-Header.
該字段可省略. CORS請求時, xhr.getResponseHeader() 方法默認只能獲取6個基本字段: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma . 如果需要獲取其他字段, 就需要在Access-Control-Expose-Headers 中指定. 如上, 這樣xhr.getResponseHeader("X-Custom-Header") 才能返回X-Custom-Header字段的值.(該部分摘自阮一峰老師博客)
Access-Control-Max-Age: 指定preflight OPTIONS請求的有效期, 單位為秒.
2) HTTP Request Header(瀏覽器OPTIONS請求默認自帶):
Access-Control-Request-Method: 告知服務器,瀏覽器將發送哪種請求, 比如說POST.
Access-Control-Request-Headers: 告知服務器, 瀏覽器將包含哪些額外的頭域字段.
3) 以下所有的header name 是被拒絕的:
Accept-Charset
Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
Cookie2
Date
DNT
Expect
Host
Keep-Alive
Origin
Referer
TE
Trailer
Transfer-Encoding
Upgrade
Via
包含以Proxy- 或 Sec- 開頭的header name
CORS請求CORS請求分為兩種, ① 簡單請求; ② 非簡單請求.
滿足如下兩個條件便是簡單請求, 反之則為非簡單請求.(CORS請求部分摘自阮一峰老師博客)
1) 請求是以下三種之一:
HEAD
GET
POST
2) http頭域不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type字段限三個值 application/x-www-form-urlencoded、multipart/form-data、text/plain
對于簡單請求, 瀏覽器將發送一次http請求, 同時在Request頭域中增加 Origin 字段, 用來標示請求發起的源, 服務器根據這個源采取不同的響應策略. 若服務器認為該請求合法, 那么需要往返回的 HTTP Response 中添加 Access-Control-* 等字段.( Access-Control-* 相關字段解析請閱讀我之前寫的CORS 跨域訪問 )
對于非簡單請求, 比如Method為POST且Content-Type值為 application/json 的請求或者Method為 PUT 或 DELETE 的請求, 瀏覽器將發送兩次http請求. 第一次為preflight預檢(Method: OPTIONS),主要驗證來源是否合法. 值得注意的是:OPTION請求響應頭同樣需要包含 Access-Control-* 字段等. 第二次才是真正的HTTP請求. 所以服務器必須處理OPTIONS應答(通常需要返回20X的狀態碼, 否則xhr.onerror事件將被觸發).
以上請求流程圖為:
HTML啟用CORShttp-equiv 相當于http的響應頭, 它回應給瀏覽器一些有用的信息,以幫助正確和精確地顯示網頁內容. 如下html將允許任意域名下的網頁跨域訪問.
圖片啟用CORS通常, 圖片允許跨域訪問, 也可以在canvas中使用跨域的圖片, 但這樣做會污染畫布, 一旦畫布受污染, 將無法讀取其數據. 比如無法調用 toBlob(), toDataURL() 或 getImageData()方法. 瀏覽器的這種安全機制規避了未經許可的遠程服務器圖片被濫用的風險.(該部分內容摘自 啟用了 CORS 的圖片 - HTML(超文本標記語言) | MDN)
因此如需在canvas中使用跨域的圖片資源, 請參考如下apache配置片段(來自HTML5 Boilerplate Apache server configs).
ajax文件上傳SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS
ajax實現文件上傳非常簡單, 這里我選取原生js, jq, angular 分別來比較下, 并順便聊聊使用它們時的注意事項.(ajax文件上傳的代碼已上傳至github, 請戳這里預覽效果: ajax 文件上傳 demo | louis)
1) 為了上傳文件, 我們得先選中一個文件. 一個type為file的input框就夠了.
2) 然后用FormData對象包裹?選中的文件.
var input = document.getElementById("input"), formData = new FormData(); formData.append("file",input.files[0]);//key可以隨意定義,只要后臺能理解就行
3) 定義上傳的URL, 以及方法. github上我搭建了一個 node-webserver, 根據需要可以自行克隆下來npm start后便可調試本篇代碼.
var url = "http://localhost:10108/test", method = "POST";js文件上傳
4.1) 封裝一個用于發送ajax請求的方法.
function ajax(url, method, data){ var xhr = null; if(window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("您的瀏覽器暫不支持Ajax!"); } } } xhr.onerror = function(e){ console.log(e); } xhr.open(method, url); try{ setTimeout(function(){ xhr.send(data); }); }catch(e){ console.log("error:",e); } return xhr; }
4.2) 上傳文件并綁定事件.
var xhr = ajax(url, method, formData); xhr.upload.onprogress = function(e){ console.log("upload progress:", e.loaded/e.total*100 + "%"); }; xhr.upload.onload = function(){ console.log("upload onload."); }; xhr.onload = function(){ console.log("onload."); }
上傳結果如下所示:
fetch上傳5) fetch只要發送一個post請求, 并且body屬性設置為formData即可. 遺憾的是, fetch無法跟蹤上傳的進度信息.
fetch(url, { method: method, body: formData }).then(function(res){ console.log(res); }).catch(function(e){ console.log(e); });jquery文件上傳
jq提供了各式各樣的上傳插件, 其原理都是利用jq自身的ajax方法.
6) jq的ajax提供了xhr屬性用于自定義各種事件.
$.ajax({ type: method, url: url, data: formData, processData : false, contentType : false ,//必須false才會自動加上正確的Content-Type xhr: function(){ var xhr = $.ajaxSettings.xhr();//實際上就是return new window.XMLHttpRequest()對象 if(xhr.upload) { xhr.upload.addEventListener("progress", function(e){ console.log("jq upload progress:", e.loaded/e.total*100 + "%"); }, false); xhr.upload.addEventListener("load", function(){ console.log("jq upload onload."); }); xhr.addEventListener("load", function(){ console.log("jq onload."); }); return xhr; } } });
jq上傳結果如下所示:
有關jq ajax更多的api, 請參考中文文檔 jQuery.ajax() | jQuery API 中文文檔 .
angular文件上傳7.1) angular提供了$http方法用于發送http請求, 該方法返回一個promise對象.
$http({ method: method, url: url, data: formData, }).success(function(res) { console.log(res); }).error(function(err, status) { console.log(err); });
angular文件上傳的代碼已上傳至github, 請戳這里預覽效果: angular 文件上傳 demo | louis.
低版本angular中文件上傳的功能并不完整, 直到angular1.5.5才在$http中加入了eventHandler和uploadEventHandlers等方法, 使得它支持上傳進度信息. 如下:
$http({ method: method, url: url, eventHandlers: { progress: function(c) {//下載進度 console.log("Progress -> " + c); } }, uploadEventHandlers: { progress: function(e) {//上傳進度 console.log("UploadProgress -> " + e); } }, data: formData, }).success(function(res) { console.log(res); }).error(function(err, status) { console.log(err); });
angular1.5.5以下低版本中, 請參考成熟的實現方案 angular-file-upload 以及它提供的demo Simple example .
ajax請求二進制文件 FileReader處理二進制文件主要使用的是H5的FileReader.
PC支持性如下:
IE | Edge | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|---|
10 | 12 | 3.6 | 6 | 6 | 11.5 |
Mobile支持性如下:
IOS Safari | Opera Mini | Android Browser | Chrome/Android | UC/Android |
---|---|---|---|---|
7.1 | - | 4 | 53 | 11 |
以下是其API:
屬性/方法名稱 | 描述 |
---|---|
error | 表示讀取文件期間發生的錯誤. |
readyState | 表示讀取文件的狀態.默認有三個值:0表示文件還沒有加載;1表示文件正在讀取;2表示文件讀取完成. |
result | 讀取的文件內容. |
abort() | 取消文件讀取操作, 此時readyState屬性將置為2. |
readAsArrayBuffer() | 讀取文件(或blob對象)為類型化數組(ArrayBuffer), 類型化數組允許開發者以數組下標的方式, 直接操作內存, 由于數據以二進制形式傳遞, 效率非常高. |
讀取文件(或blob對象)為二進制字符串, 該方法已移出標準api, 請謹慎使用. | |
readAsDataURL() | 讀取文件(或blob對象)為base64編碼的URL字符串, 與window.URL.createObjectURL方法效果類似. |
readAsText() | 讀取文件(或blob對象)為文本字符串. |
onload() | 文件讀取完成時的事件回調, 默認傳入event事件對象. 該回調內, 可通過this.result 或 event.target.result獲取讀取的文件內容. |
var xhr = new XMLHttpRequest(), url = "http://louiszhai.github.io/docImages/ajax01.png"; xhr.open("GET", url); xhr.responseType = "blob"; xhr.onload = function(){ if(this.status == 200){ var blob = this.response; var img = document.createElement("img"); //方案一 img.src = window.URL.createObjectURL(blob);//這里blob依然占據著內存 img.onload = function() { window.URL.revokeObjectURL(img.src);//釋放內存 }; //方案二 /*var reader = new FileReader(); reader.readAsDataURL(blob);//FileReader將返回base64編碼的data-uri對象 reader.onload = function(){ img.src = this.result; }*/ //方案三 //img.src = url;//最簡單方法 document.body.appendChild(img); } } xhr.send();ajax請求二進制文本并展示
var xhr = new XMLHttpRequest(); xhr.open("GET","http://localhost:8080/Information/download.jsp?data=node-fetch.js"); xhr.responseType = "blob"; xhr.onload = function(){ if(this.status == 200){ var blob = this.response; var reader = new FileReader(); reader.readAsBinaryString(blob);//該方法已被移出標準api,建議使用reader.readAsText(blob); reader.onload=function(){ document.body.innerHTML = "" + this.result + ""; } } } xhr.send();
有關二進制文件的讀取, 請移步這篇博客 HTML5新特性之文件和二進制數據的操作 .
如何等待多個ajax請求完成原生js可以使用ES6新增的Promise. ES6的Promise基于 Promises/A+ 規范(該部分 Fetch入門指南 一文也有提及).
這里先提供一個解析responses的函數.
function todo(responses){ responses.forEach(function(response){ response.json().then(function(res){ console.log(res); }); }); }
原生js使用 Promise.all 方法. 如下:
var p1 = fetch("http://localhost:10108/test1"), p2 = fetch("http://localhost:10108/test2"); Promise.all([p1, p2]).then(function(responses){ todo(responses); //TODO do somethings }); //"test1" //"test2"
jquery可以使用$.when方法. 該方法接受一個或多個Deferred對象作為參數, 只有全部成功才調用resolved狀態的回調函數, 但只要其中有一個失敗,就調用rejected狀態的回調函數. 其實, jq的Deferred是基于 Promises/A規范實現, 但并非完全遵循. (傳送門: jQuery 中的 Deferred 和 Promises (2) ).
var p1 = $.ajax("http://localhost:10108/test1"), p2 = $.ajax("http://localhost:10108/test2"); $.when(p1, p2).then(function(res1, res2){ console.log(res1);//["test1", "success", Object] console.log(res2);//["test2", "success", Object] //TODO do somethings });
如上, $.when默認返回一個jqXHR對象, 可以直接進行鏈式調用. then方法的回調中默認傳入相應的請求結果, 每個請求結果的都是數組, 數組中依次是responseText, 請求狀態, 請求的jqXHR對象.
angular中可以借助 $q.all() 來實現. 別忘了, $q 需要在controller中注入. 此外, $q 相關講解可參考 AngularJS: ng.$q 或 Angular $q service學習筆記 .
var p1 = fetch("http://localhost:10108/test1"), p2 = fetch("http://localhost:10108/test2"); $q.all([p1, p2]).then(function(responses){ todo(responses); //TODO do somethings }); //"test1" //"test2"
$q.all() 實際上就是對 Promise.all 的封裝.
ajax與history的兼容ajax的一大痛點就是無法支持瀏覽器前進和后退操作. 因此早期的Gmail 采用 iframe, 來模擬ajax的前進和后退.
如今, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 組合的一種技術. 使用它便可以無刷新通過瀏覽器前進和后退來改變頁面內容.
先看下兼容性.
IE | Edge | Firefox | Chrome | Safari | Opera | iOS Safari | Android Browser | Chrome for Android | |
---|---|---|---|---|---|---|---|---|---|
pushState/replaceState | 10 | 12 | 4 | 5 | 6 | 11.5 | 7.1 | 4.3 | 53 |
history.state | 10 | 4 | 18 | 6 | 11.5 |
可見IE8,9并不能使用 H5的history. 需要使用墊片 HTML5 History API expansion for browsers not supporting pushState, replaceState .
pjaxpjax簡單易用, 僅需要如下三個api:
history.pushState(obj, title, url) 表示往頁面history末尾新增一個歷史項(history entry), 此時history.length會+1.
history.replaceState(obj, title, url) 表示替換當前歷史項為新的歷史項. 此時history.length保持不變.
window.onpopstate 僅在瀏覽器前進和后退時觸發(history.go(1), history.back() 及location.href="xxx" 均會觸發), 此時可在history.state中拿到剛剛塞進去的state, 即obj對象(其他數據類型亦可).
我們注意到, 首次進入一個頁面, 此時 history.length 值為1, history.state 為空. 如下:
1) 為了在onpopstate事件回調中每次都能拿到 history.state , 此時需要在頁面載入完成后, 自動替換下當前url.
history.replaceState("init", title, "xxx.html?state=0");
2) 每次發送ajax請求時, 在請求完成后, 調用如下, 從而實現瀏覽器history往前進.
history.pushState("ajax請求相關參數", title, "xxx.html?state=標識符");
3) 瀏覽器前進和后退時, popstate 事件會自動觸發, 此時我們手動取出 history.state , 構建參數并重新發送ajax請求或者直接取用state值, 從而實現無刷新還原頁面.
window.addEventListener("popstate", function(e) { var currentState = history.state; //TODO 拼接ajax請求參數并重新發送ajax請求, 從而回到歷史頁面 //TODO 或者從state中拿到關鍵值直接還原歷史頁面 });
popstate 事件觸發時, 默認會傳入 PopStateEvent 事件對象. 該對象具有如下屬性.
如有不懂, 更詳細講解請移步 : ajax與HTML5 history pushState/replaceState實例 ? 張鑫旭-鑫空間-鑫生活 .
ajax緩存處理js中的http緩存沒有開關, 受制于瀏覽器http緩存策略. 原生xhr請求中, 可通過如下設置關閉緩存.
xhr.setRequestHeader("If-Modified-Since","0"); xhr.setRequestHeader("Cache-Control","no-cache"); //或者 URL 參數后加上 "?timestamp=" + new Date().getTime()
jquery的http緩存是否開啟可通過在settings中指定cache.
$.ajax({ url : "url", dataType : "xml", cache: true,//true表示緩存開啟, false表示緩存不開啟 success : function(xml, status){ } });
同時jquery還可以全局設置是否緩存. 如下將全局關閉ajax緩存.
$.ajaxSetup({cache:false});
除此之外, 調試過程中出現的瀏覽器緩存尤為可惡. 建議開啟隱私瀏覽器或者勾選??控制臺的 Disable cache 選項. (這里以Chrome舉例, 其他瀏覽器類似)
ajax的錯誤處理前面已經提過, 通常只要是ajax請求收到了http狀態碼, 便不會進入到錯誤捕獲里.(Chrome中407響應頭除外)
實際上, $.ajax 方法略有區別, jquery的ajax方法還會在類型解析出錯時觸發error回調. 最常見的便是: dataType設置為json, 但是返回的data并非json格式, 此時 $.ajax 的error回調便會觸發.
ajax調試技巧有關調試, 如果接口只是做小部分修改. 那么可以使用charles(Mac) 或者fiddler(Windows), 做代理, 將請求的資源替換為本地文件, 或者使用其斷點功能, 直接編輯response.
如果是新增接口的調試, 可以本地搭建node服務. 利用hosts文件配置dns + nginx將http請求轉發到本地node服務器. 簡易的node調試服務器可參考我的 node-webserver . 如下舉一個栗子?:
hosts+nginx+node-webserver假設我們要調試的是 www.test.com 的GET接口. 以下所有步驟以Mac為例, 其他系統, 請自行搜索?文件路徑.
1) hosts配置.
sudo vim /etc/hosts #新增一行 127.0.0.1 www.test.com
2) nginx 配置
brew install nginx #安裝 #安裝成功后進入目標目錄 cd /usr/local/etc/nginx/ cd servers #默認配置入口為nginx.conf.同時servers目錄下*.conf文件已自動加入到配置文件列表中 vim test.conf #粘貼如下內容 server { listen 80; server_name www.test.com; index index.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location / { proxy_pass http://localhost:10108/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Read-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } #:wq保存并退出 #啟動nginx sudo nginx -s reload #如果啟動了只需重啟即可 sudo nginx #如果沒有啟動,便啟動之
3) node-webServer 配置
參考 node-webserver . 啟動服務前只需更改index.js, 在第9行后插入如下內容:
"get": { "/": { getKey : "Welcome to Simple Node WebServer!" }, "接口api": "你的response內容"http://插入的代碼 },
如需在nginx中配置CORS, 請看這里: Nginx通過CORS實現跨域.
編碼問題XMLHttpRequest 返回的數據默認的字符編碼是utf-8, post方法提交數據默認的字符編碼也是utf-8. 若頁面編碼為gbk等中文編碼, 那么就會產生亂碼.
后端接口測試技巧通常, 如果后端接口開發OK了, 前端同學需要通過一些手段來確認接口是能正常訪問的.
使用命令測試OPTIONS請求curl -I -X OPTIONS -H "Origin: http://example.com" http://localhost:10108/ # response HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/json;charset=UTF-8 Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: x-requested-with,Content-Type Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Origin: http://example.com Access-Control-Max-Age: 3600 Server: Node WebServer Website: https://github.com/Louiszhai/node-webserver Date: Fri, 21 Oct 2016 09:00:40 GMT Connection: keep-alive Transfer-Encoding: chunked
以上, http狀態碼為200, 表示允許OPTIONS請求.
GET, POST 請求與GET類似, 其他請求亦然.
curl -I -X GET -H "Origin: http://example.com" http://localhost:10108/ #HTTP/1.1 200 OK curl -I -X POST -H "Origin: http://example.com" http://localhost:10108/test #HTTP/1.1 200 OKpostman
除此之外, 我們還可以通過chrome的postman擴展進行測試. 請看postman素潔的界面:
postman支持所有類型的http請求, 由于其向chrome申請了cookie訪問權限及所有http(s)網站的訪問權限. 因此可以放心使用它進行各種網站api的測試.
同時, 強烈建議閱讀本文的你升級postman的使用技巧, 這里有篇: 基于Postman的API自動化測試 , 拿走不謝.
ajax移動端兼容性移動端的支持性比較弱, 使用需謹慎. 看表.
IOS Safari | Opera Mini | Android Browser | Android Chrome | Android UC | |
---|---|---|---|---|---|
XMLHttpRequest | 8.4 | - | 4.4.4 | 53
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。 轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81998.html 相關文章
發表評論0條評論Aomine男|高級講師TA的文章閱讀更多
閱讀需要支付1元查看
|