摘要:屬性是此次響應的文本內(nèi)容。我們可以通過屬性,指定這個事件的回調(diào)函數(shù),對不同狀態(tài)進行不同處理。尤其是當狀態(tài)變?yōu)榈臅r候,表示通信成功,這時回調(diào)函數(shù)就可以處理服務器傳送回來的數(shù)據(jù)。由于臨時的服務器維護或者過載,服務器當前無法處理請求。
XMLHttpRequest JSON AJAX CORS 四個名詞來開會如何發(fā)請求
在前端的世界里也逛蕩了不少日子了,目前已經(jīng)get到大約5種發(fā)起請求的方式,主流的、非主流的。
何種方式 | 請求方法 | |
---|---|---|
最常見的form表單 | 默認GET,多用POST,只此兩種 | 會刷新頁面或者新開頁面 |
a 標簽 | GET請求 | 也會刷新頁面或者新開頁面 |
img的src屬性 | GET | 只能以圖片的形式展現(xiàn) |
link標簽 | GET | 只能以CSS、favicon的形式展現(xiàn) |
script標簽 | GET | 只能以腳本的形式運行 |
可是
我們可能想用GET POST PUT DELETE 方法
不想刷新整個頁面,想用一種更易于理解的方式來響應
AJAX出現(xiàn) 瀏覽器和服務器交互模式 V1.0在AJAX未出現(xiàn)之前,瀏覽器想從服務器獲得資源,注意是獲取資源,會經(jīng)過如下一個過程
瀏覽器發(fā)起請求->服務器接到請求響應給你HTML文檔->瀏覽器收到資源,刷新頁面,加載獲得的的HTML。簡略的過程
我稱這種交互方式是 V1.0,此時還是以獲取資源為導向。后來隨著時代的發(fā)展,人們?nèi)找嬖鲩L的文化需求成為了社會的主要矛盾……有一天,小明看了一篇報道,他只是想在下面評論一下,發(fā)表對實事的親切問候,問候完了,唉,你給我刷新頁面干啥,我只是想評論一下啊。
大概那是網(wǎng)民們第一次對 良好的用戶體驗 提出了要求。后來的蘋果爸爸,把大家慣壞了,天天嚷著 "你這產(chǎn)品用戶體驗太差了"……
彼時,微軟還是對web做出了很大的貢獻的。
交互模式2.0大約1999年,微軟發(fā)布IE 5.0版本,它允許JavaScript腳本向服務器發(fā)起HTTP請求。不過很遺憾,當時也沒有火起來,直到2004年Gmail發(fā)布和2005年Google Map發(fā)布,才引起廣泛重視。2005年,一個叫Jesse James Garrett的人提出了一個新術(shù)語----AJAX,它是一系列技術(shù)的組合體,全稱是 Asynchronous JavaScript + XML(異步的JS和XML)可以阻止頁面整體刷新,只是動態(tài)響應用戶的操作,快速顯示到局部,用戶就可以很愉快的繼續(xù)上網(wǎng)了。
AJAX
可以看出IE當時還是很猛的,隨著IE 6.0 市場份額進一步擴大,IE已經(jīng)把火狐整的半死不活,放眼整個瀏覽器市場,微軟是當之無愧的王者,后來微軟就把瀏覽器團隊解散了……不得不說這是一波神操作,能與之媲美的操作大概只有殘血我能反殺 塔下我能秀他了。微軟強行為后續(xù)各家瀏覽器的發(fā)展提供了優(yōu)秀的工程師,尤其是08、09年出生的谷歌瀏覽器,再看如今的IE……
既然AJAX是一系列的技術(shù)的組合體,接下來認識一下其中的幾位主角
XMLHttpRequestXMLHttpRequest對象是用來在瀏覽器和服務器之間傳輸數(shù)據(jù)的。
古代的操作的是:
瀏覽器構(gòu)造XMLHttpRequest實例化對象
用這個對象發(fā)起請求
服務器響應一個XML格式的字符串,是字符串,是字符串,是字符串,也就是說響應的第四部分是字符串。
JS解析符合XML格式的字符串,更新局部頁面。
什么是XML,可擴展標記語言。
以上是最初的用法,用的是XML,前端代碼片段如下
let request = new XMLHttpRequest() //實例化XMLHttpRequest對象 request.onreadystatechange = () => { if (request.readyState === 4) { console.log("請求和響應都完畢了") if (request.status >= 200 && request.status <= 300) { console.log("說明請求成功了") console.log(request.responseText) let parser = new DOMParser() let xmlDoc = parser.parseFromString(request.responseText, "text/xml") //用parser解析request.responseText let c = xmlDoc.getElementsByTagName("body")[0].textContent console.log(c) } else if (request.status >= 400) { console.log("說明請求失敗了") } } } request.open("GET", "/xxx") //配置request request.send()
服務器端的對應代碼片段如下
... response.statusCode = 200 response.setHeader("Content-Type", "text/xml;charset=utf-8") response.write(``) response.end() ... 木木 少少 你好哇 好久不見啊
本地模擬的話,一定要記得開倆不同的端口
例如:
node server.js 8001 node server.js 8002XMLHttpRequest實例的詳解
正如上面的前端代碼片段寫的一樣,主要用到了open() send()方法, onreadystatechange readyState 屬性。
request.open(method, URL, async)方法。
一般用三個參數(shù),第一個參數(shù)是請求的方法,可以用GET POST DELETE PUT等等,URL是用訪問的路徑,async是是否使用同步,默認true,開啟異步,不需要做修改即可,所以實際中只寫前兩個參數(shù)
如果非要寫false,開啟同步,會對瀏覽器有阻塞效應,而且如果值為false,則send()方法不會返回任何東西,直到接受到了服務器的返回數(shù)據(jù)
request.send()方法。
發(fā)送請求. 如果該請求是異步模式(默認),該方法會立刻返回. 相反,如果請求是同步模式,則直到請求的響應完全接受以后,該方法才會返回
readyState屬性。
描述請求的五個狀態(tài)。
0 === 常量 UNSENT (未打開) open()方法未調(diào)用
1 === OPENED (未發(fā)送) 只是open()方法調(diào)用了
2 === HEADERS_RECEIVED (已獲取響應頭) send()方法調(diào)用了,響應頭和響應狀態(tài)已經(jīng)返回了
3 === LOADING (正在下載響應體) 響應體下載中,responseText已經(jīng)獲取了部分數(shù)據(jù)
4 === DONE (請求完成) 整個響應過程完畢了。 這個值是實際中用到的。
只要不等于4,就表示請求還在進行中。
responseText屬性是此次響應的文本內(nèi)容。
onreadystatechange屬性。
readyState屬性的值發(fā)生改變,就會觸發(fā)readyStateChange事件。
我們可以通過onReadyStateChange屬性,指定這個事件的回調(diào)函數(shù),對不同狀態(tài)進行不同處理。尤其是當狀態(tài)變?yōu)?的時候,表示通信成功,這時回調(diào)函數(shù)就可以處理服務器傳送回來的數(shù)據(jù)。即前面的代碼片段的處理方式。
其他的方法、屬性、事件詳見阮一峰博客、MDN文檔
習慣用javaScript的前端是不想和XML打交道的,應該用一種符合js風格的數(shù)據(jù)格式語言。
JSON后來一個美國程序員道格拉斯·克羅克福特發(fā)明了JSON,解決了上面的問題,這貨還寫了一本蝴蝶書JavaScript語言精粹,還發(fā)明了一個JS校驗器 ----JSLint。
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式。 易于人閱讀和編寫。同時也易于機器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON采用完全獨立于語言的文本格式,但是也使用了類似于C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成為理想的數(shù)據(jù)交換語言。
以上是JSON官網(wǎng)的簡介,可以看出它是一門全新的語言,不是JavaScript的子集。
JSON很簡單,數(shù)據(jù)類型和JS有點不同的地方。
JavaScript | JSON |
---|---|
string | "string" 必須寫雙引號 |
number | number |
object | {"object": "name"} 必須雙引號 |
undefined | 沒有 |
null | null |
boolean | 直接寫true false |
array | array |
function | 沒有 |
variable |
瀏覽器的全局對象window上有JSON對象,直接使用window.JSON.parse(string)
let string = request.responseText let json = window.JSON.parse(string) //string 要符合JSON的格式
以上是JSON解析部分的代碼。
此時服務器端代碼是
response.statusCode = 200 response.setHeader("Content-Type", "text/json;charset=utf-8") response.write(` { "note" : { "to" : "木木", "from" : "少少", "heading" : "你好哇", "content" : "好久不見啊" } } `)
我們?yōu)g覽器有同源政策,不是同協(xié)議 同域名 同端口 的網(wǎng)頁無法相互訪問。
4.AJAX恰好是同源政策的擁躉
CORS
如果AJAX向非同源的地址發(fā)起請求,會報錯。
這種錯誤無法通過狀態(tài)碼識別,因為HTTP回應的狀態(tài)碼有可能是200,也就是說即使你看到了200的正確碼,也沒有用
但是form表單無視同源政策,可以發(fā)起跨域請求。
上述請求響應都沒有問題
然而對于AJAX就不行
... request.open("GET", "http://www.baidu.com") ...
這是為什么呢,因為
原頁面用 form 提交到另一個域名之后,原頁面的腳本無法獲取新頁面中的內(nèi)容,所以瀏覽器認為這是安全的。
而 AJAX 是可以讀取響應內(nèi)容的,因此瀏覽器不能允許你這樣做。如果你細心的話你會發(fā)現(xiàn),其實請求已經(jīng)發(fā)送出去了,你只是拿不到響應而已。
所以瀏覽器這個策略的本質(zhì)是,一個域名的 JS ,在未經(jīng)允許的情況下,不得讀取另一個域名的內(nèi)容。但瀏覽器并不阻止你向另一個域名發(fā)送請求。作者:方應杭
鏈接:https://www.zhihu.com/questio...
來源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
那么如何讓AJAX跨域發(fā)起請求呢。
答案是CORS
CORS目前是W3C的標準,它允許瀏覽器跨域發(fā)起XMLHttpRequest請求,而且可以發(fā)起多種請求,不像JSONP只能發(fā)起GET請求,全稱是"跨域/源資源共享"(Cross-origin resource sharing)。
如果想要發(fā)起跨域請求 例如: http://wushao.com:8001 要想訪問 http://shaolin.com:8002,可以做如下處理
request.open("GET", "http://wushao.com:8001/xxx") //配置request
服務器端的代碼需要做如下處理
response.setHeader("Access-Control-Allow-Origin", "http://shaolin.com:8002")
一定要注意是誰去訪問誰,8001去訪問8002,那么8001的前端代碼要告訴8002的后端代碼,咱們是一家人,你和瀏覽器說說別讓它禁我了。
AJAX一些其他知識既然可以發(fā)請求,那么請求頭的四部分如何獲得的,響應的四部分又是如何獲得呢
獲得請求頭的方法
request.open("GET", "http://shaolin.com:8002/xxx")// 請求的第一部分 request.setRequestHeader("Content-Type", "x-www-form-urlencoded")//請求的第二部分 request.setRequestHeader("wushao", "18") //請求的第二部分 request.send("我要設置請求的第四部分") //請求的第四部分 request.send("name=wushao&password=wushao") //請求的第四部分
對應的典型的http請求四部分
GET /xxx HTTP/1.1 HOST: http://shaolin.com:8002 Content-Type: x-www-form-urlencoded wushao: 18 name=wushao&password=wushao
獲得響應的方法
request.status //響應的第一部分 200 request.statusText //響應的第一部分 OK request.getAllResponseHeaders //響應的第二部分,這個方法好啊,全部的響應頭 request.getResponseHeader("Content-Type") //響應的第二部分具體的 request.responseText //響應的第四部分
對應的典型的http響應的四部分
HTTP/1.1 200 OK Content-Type: text/json;charset=utf-8 { "note" : { "to" : "木木", "from" : "少少", "heading" : "你好哇", "content" : "好久不見啊" } }
回顧一下各個status對應的意思
100 200 === OK,請求成功 301 === 被請求的資源已永久移動到新位置 302 === 請求臨時重定向,要求客戶端執(zhí)行臨時重定向 304 === 和上次請求一樣,未改變 403 === 服務器已經(jīng)理解請求,但是拒絕訪問 404 === 請求失敗,服務器上沒有這個資源 502 === 作為網(wǎng)關(guān)或者代理工作的服務器嘗試執(zhí)行請求時,從上游服務器接收到無效的響應。 503 === Service Unavailable 由于臨時的服務器維護或者過載,服務器當前無法處理請求。
初級的jq封裝
這是一個很簡陋的效果,首先我還是把jq假設的很簡單,就是一個window的屬性,請輕噴……
window.jQuery = function (nodeOrSelector) { let nodes = {} nodes.addClass = function () {} nodes.html = function () {} return nodes } window.jQuery.ajax = function (options) { let url = options.url let method = options.method let headers = options.headers let body = options.body let successFn = options.successFn let failFn = options.failFn let request = new XMLHttpRequest() //實例化XMLHttpRequest對象 request.open(method, url) for (let key in headers) { let value = headers[key] request.setRequestHeader(key, value) } request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status >= 200 && request.status <= 300) { successFn.call(undefined, request.responseText) } else if (request.status >= 400) { failFn.call(undefined, request) } } } request.send(body) }
以上就是jq對ajax的簡陋的封裝,ajax()方法接受一個對象作為參數(shù),這個對象有很多鍵。這些鍵就是http請求的頭的各個部分,以及一個成功函數(shù)和一個失敗函數(shù)。
myButton.addEventListener("click", (e) => { window.jQuery.ajax ({ url: "/xxx", method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", "wushao": "18" }, body: "a=1&b=6", successFn: (x) => { ... }, failFn: (x) => { ... } }) })
以上就是簡化后的使用方法,給button綁定事件的時候,函數(shù)體直接就是ajax()
目前你會發(fā)現(xiàn)options這個對象傻傻的,因為總有一些用戶不希望只傳一個參數(shù)。所以我們稍微改造一下。
let url if (arguments.length === 1) { url = options.url } else if (arguments.length === 2) { url = arguments[0] options = arguments[1] } let method = options.method let headers = options.headers let body = options.body let successFn = options.successFn let failFn = options.failFn
加了一點,判斷ajax()的參數(shù)個數(shù)。
一千個人有一千零一個成功或失敗函數(shù)的寫法,所以為了維護世界和平,大家約定俗成了一套理論 Promise then( )
//Promise這個對象呢,大概長這個樣子,真實面目我是沒見過 //簡單的寫一下promise window.Promise = function (fn) { //...一些其他代碼 return { then: function () {} } }
Promise這個構(gòu)造函數(shù)呢,又會返回一個函數(shù),這個返回的函數(shù)一個then屬性,value又是一個函數(shù)。處處都體現(xiàn)著函數(shù)是第一公民的地位?。?!
那我們可以利用這個強大的Promise對象搞一些事情了。
//第一步的代碼改造成這樣,第一步用到了ES6的解構(gòu)賦值法 window.jQuery.ajax = function ({url, method, body, headers}) { return new Promise(function (resolve, reject) { let request = new XMLHttpRequest() request.open(method, url) for(let key in headers) { let value = headers[key] request.setRequestHeader(key, value) } request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status >= 200 && request.status <= 300) { resolve.call(undefined, request.responseText) } else if (request.status >= 400) { reject.call(undefined, request) } } } request.send(body) }) }
關(guān)于解構(gòu)賦值:ES6 允許按照一定模式,從數(shù)組和對象中提取值,對變量進行賦值,這被稱為解構(gòu)(Destructuring)
詳見ES6解構(gòu)賦值
//經(jīng)過上面這么一折騰,可以很簡單的使用了 myButton.addEventListener("click", (e) => { let promise = window.jQuery.ajax({ url: "/xxx", method: "get", headers: { "content-type": "application/x-www-form-urlencoded", "wushao": "18" } }) promise.then( (responseText) => { console.log(responseText) }, (request) => { console.log(request) } ) })
注意then可以傳入兩個函數(shù),第一個函數(shù)表示成功了執(zhí)行這個,第二個函數(shù)表示失敗了執(zhí)行這個,而且可以進行鏈式調(diào)用,一直點下去。
所以實際上jq的寫法大多是這么寫的
myButton.addEventListener("click", (e) => { $.ajax({ url: "/xxx", type: "GET", }).then( (responseText) => { console.log(responseText) return responseText }, (request) => { console.log("error") return "已經(jīng)處理" } ).then( (responseText) => { console.log(responseText) }, (request) => { console.log(error2) } ) })
鏈式調(diào)用的意思就是:成功函數(shù)成功了,就執(zhí)行第二個then的第一個函數(shù);成功函數(shù)失敗了,就執(zhí)行第二個then的第二個函數(shù)。
完整代碼詳見我的gitHub
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107111.html
摘要:在通常情況下,為了網(wǎng)站的安全性,對象只能訪問到與當前頁面位于同一個域下的資源。由兩部分組成,一部分是回調(diào)函數(shù),一部分是所包含的數(shù)據(jù)?;卣{(diào)函數(shù)是請求數(shù)據(jù)時指定,而數(shù)據(jù)則是跨域資源返回的數(shù)據(jù)。 在通常情況下,為了網(wǎng)站的安全性,Ajax對象只能訪問到與當前頁面位于同一個域下的資源。但有時往往需要訪問別的域中的資源,這是就運用到了跨域這個概念,這其中運用最廣泛的就是JSONP。JSONP,即J...
摘要:可以說同源策略在安全中扮演著及其重要的角色。我把這個領(lǐng)域的東西寫成了一個系列,以后還會繼續(xù)完善下去安全一同源策略與跨域安全二攻擊安全三攻擊 之所以要將同源策略與跨域?qū)懺谝黄?,是因為存在瀏覽器的同源策略,才會存在跨域問題 何為同源策略 同源策略是瀏覽器實現(xiàn)的一種安全策略,它限制了不同源之間的文檔和腳本交互的權(quán)限。只有同一個源的腳本才會具有操作dom、讀寫cookie、session 、a...
摘要:由于瀏覽器的同源策略導致無法直接通過拿到后臺數(shù)據(jù)。目前,如果非同源,共有三種行為受到限制。此處應有掌聲參考關(guān)于跨域資源共享和瀏覽器的同源策略限制的具體講解。 工作中,經(jīng)常會遇到需要跨域請求數(shù)據(jù)的情況。由于瀏覽器的同源策略,導致無法直接通過ajax拿到后臺數(shù)據(jù)。解決這個問題的方式之一就是JSONP。還有一種方式更高效簡單——跨域資源共享(Cross-origin Resource Sha...
摘要:方案瀏覽器設置一級域名。場景完全不同源的網(wǎng)站,需要窗口通信方案父子窗口互相寫互相監(jiān)聽子窗口寫后跳回同域父窗口讀瀏覽器跨文檔通信場景請求非同源地址方案架設服務器代理參考資料瀏覽器同源政策及其規(guī)避方法阮一峰前端常見跨域解決方案全跨域幾種方式 同源 概念:協(xié)議,域名,端口 相同。目的:保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)。限制的行為: Cookie、LocalStorage 和 In...
閱讀 1986·2021-09-30 09:46
閱讀 1397·2019-08-30 15:43
閱讀 1150·2019-08-29 13:28
閱讀 1950·2019-08-29 11:24
閱讀 1728·2019-08-26 13:22
閱讀 4022·2019-08-26 12:01
閱讀 1849·2019-08-26 11:33
閱讀 3270·2019-08-23 15:34