瀏覽器存在同源策略见妒,當(dāng)schema孤荣、IP、port中任何一個不相同,瀏覽器就認(rèn)為是跨域盐股,就會忽略返回結(jié)果钱豁,并且在console中報錯。瀏覽器會發(fā)送請求疯汁,并且接受請求牲尺,但是解析后發(fā)現(xiàn)是不滿足條件,則幌蚊。谤碳。。霹肝。如果客戶端沒有實現(xiàn)這個功能估蹄,那么就不存在跨域問題,比如自己實現(xiàn)的一個客戶端沫换。
下面將通過一個簡單的例子來闡述一些問題臭蚁,知識點(diǎn)包括,http server端, ajax的原生實現(xiàn)讯赏,異步垮兑,跨域問題。一共包括三個文件漱挎,命名為server.js系枪、server2.js、test.html磕谅。
test.html中展示了一種簡單形式的ajax的實現(xiàn)私爷,其中的readyState和status的含義如下:
readyState:
0-(未初始化)還沒有調(diào)用send()方法。
1-(載入)已經(jīng)調(diào)用send()方法膊夹,正在發(fā)送請求衬浑。
2-(載入完成)send()方法執(zhí)行完成,已經(jīng)接收到全部響應(yīng)內(nèi)容放刨。
3-(交互)正在解析響應(yīng)內(nèi)容工秩。
4-(完成)響應(yīng)內(nèi)容解析完成,可以在客戶端調(diào)用了进统。
status:
2xx-表示成功處理助币。
3xx-需要重定向,瀏覽器直接跳轉(zhuǎn)螟碎。
4xx-客戶端請求錯誤眉菱,如404
5xx-服務(wù)器端錯誤,比如代碼有問題500掉分,504服務(wù)端連接數(shù)據(jù)庫超時倍谜。
例子中8888端口通過xhr訪問8887端口的內(nèi)容迈螟,由于端口不一樣存在跨域叉抡,則瀏覽器出現(xiàn)錯誤:
但是在network中仍然收到了response,說明了跨域問題是因為瀏覽器的同源策略引起的尔崔,雖然可以收到response,瀏覽器卻不準(zhǔn)許用這個內(nèi)容來渲染或者處理頁面褥民。
下面將server2.js加上'Access-Control-Allow-Origin':'http://127.0.0.1:8888'或者'Access-Origin-Allow-Origin':'*'季春,瀏覽器檢查到這個頭,則準(zhǔn)許8888對8887跨域訪問消返。'*'表示8887準(zhǔn)許任何server對其訪問载弄。
然而,是不是只要加上相應(yīng)的跨域請求頭撵颊,就可以解決所有服務(wù)間跨域之間的問題呢宇攻,其實并不是,瀏覽器還有一些其他的限制倡勇,現(xiàn)在將test.html改為下圖所示逞刷,加上request header:
瀏覽器報錯,如下:
然我們看下network里面:
可以看出妻熊,這里就一個OPTIONS的請求夸浅,可是我們在test.html中定義的method明明是GET。這是為什么呢扔役,看console中報的錯誤帆喇,考慮跟我們加進(jìn)去的request header有關(guān),也就是xhr.setRequestHeader('X-Test-Core', '123')亿胸。
再來看另外一個例子坯钦,此時我們把這個報錯的的request header略去,將request method從GET改為PUT侈玄。
此時瀏覽器報錯婉刀,如下:
我們看下network中的情況:
這個network中也不見我們在test.html中定義的PUT方法,卻只看見了一個OPTIONS的方法拗馒。而console中也提示是Acess-Control-Methods的錯誤路星,考慮到本文一開始用GET方法的例子正確,可以猜測跟PUT methods有關(guān)诱桂。
綜上洋丐,我?guī)е蓡柸ふ掖鸢福簿褪腔拥龋瑸g覽器對于除了對跨域有基本的限制之外友绝,還存在一些具體的限制。這些限制大多是通過request header來判定的肝劲,server端response header必須有對應(yīng)的字段才能準(zhǔn)許跨域請求迁客,同時郭宝,瀏覽器通過CORS預(yù)請求對其進(jìn)行驗證。上面兩個失敗的例子掷漱,我通過修改server2.js中返回的header可以得到正確的結(jié)果粘室,這里我不再一個個驗證卜范,而是一起解決。
總結(jié)锦爵,默認(rèn)跨域允許方法:GET,HEAD,POST?
?默認(rèn)允許Content-Type:text/plain? multipart/form-data? application/x-www-form-urlencoded? ?(form表單的3種數(shù)據(jù)類型)
XMLHttpRequestUpload對象均沒有注冊任何事件監(jiān)聽器。
請求中沒有使用ReadableStream對象奥裸。
其他限制:請求頭的限制,具體見'https://fetch.spec.whatwg.org/#cors-safelisted-request-header'
超出以上范圍則需要在server端設(shè)置準(zhǔn)許才行湾宙,同時瀏覽器會發(fā)送OPTIONS請求進(jìn)行驗證。
然而创倔,瀏覽器對html標(biāo)簽中存在的src href等屬性并不會做跨域限制嗡害,因此實際工作中我們利用這個特性來實現(xiàn)跨域的需求凝危。這種方法通常稱為JSONP啦辐。下面以script標(biāo)簽中的src屬性為例置逻。
8888端口請求8887端口的數(shù)據(jù)派任,控制臺沒有報錯秸侣,由于返回了callback('abc')罢绽,字符串'abc'作為參數(shù)傳遞到callback中静盅,最后返回到test.html請求的的script標(biāo)簽的位置,由于callback函數(shù)在上面有定義明垢,則執(zhí)行callback函數(shù)市咽,打印出'abc'。由以上過程看出施绎,JSONP作為跨域的解決方式贞绳,需要后端更加緊密的配合冈闭,返回需要執(zhí)行的代碼豺裆。