跨域是咱們在項目中常常遇到的,先後端數據交互常常碰到請求跨域,首先咱們來想一下爲何會有跨域這個詞的出現?本文帶你來探討一下如下幾個問題:javascript
跨域
是指的瀏覽器不能執行其它網站的腳本,它是由瀏覽器的同源策略
形成,是瀏覽器對JavaScript實施的安全限制。html
跨域
實際上指從一個域的網頁去請求另外一個域的資源,好比:從 http://www.baidu.com
網站去請求http://www.google.com
網站的資源。前端
同源策略
指的是 域名,協議,端口 三者都相同~
java
要知道URL
由協議,域名,端口以及路徑組成,若兩個URL
的協議、域名和端口相同,則表示他們同源。
相反,只要協議,域名,端口有任何一個的不一樣,就被看成是跨域。node
這下邊三個含有 src
標籤的是容許跨域加載資源的jquery
<img src=XXX>
<link href=XXX>
<script src=XXX>
jsonp
全稱是JSON with Padding
,是爲了解決跨域請求資源而產生的解決方案,是一種依賴開發人員創造出的一種非官方跨域數據交互協議。nginx
優勢:web
缺點:ajax
具體代碼實現以下:chrome
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> function jsonp({url,params,cb}){ return new Promise((resolve,reject)=>{ let script = document.createElement('script'); window[cb]=function(data){ resolve(data); document.body.removeChild(script); } params={...params,cb}//wd=b&cb=show let arrs = []; for(let key in params){ arrs.push(`${key}=${params[key]}`); } script.src = `${url}?${arrs.join('&')}`; document.body.appendChild(script) }) } //只能發送get請求,不支持post put delete //不安全xss攻擊 不採用 jsonp({ url:'http://localhost:3000/say', params:{wd:'早上好'}, cb:'show' }).then(data=>{ console.log(data) }) </script> </body> </html>
serve.js
let express = require('express'); let app = express(); app.get('/say',function (req,res){ let {wd,cb} = req.query; console.log(wd); res.end(`${cb}('晚上好')`) }) app.listen(3000)
注意: 須要安裝npm install express
, 而後在終端裏面輸入node serve.js
, 再把index.html
在瀏覽器上邊console
欄查看返回結果
若是從 192.168.19.1
發ajax
請求到 192.168.19.6
會產生跨域問題, 利用jquery
的jsonp
參數可輕鬆這個問題。
注意:Jsonp都是GET和異步請求
function get() { $.ajax({ type: "GET", url: 'http://192.168.19.6:8080/jsgd/bill.jsp?userCode=?&date='+ new Date(), dataType:"jsonp", jsonp:"jsonpcallback", success: function(msg){ $('#callcenter').html(msg.text); } }); }
cors
全稱"跨域資源共享"(Cross-origin resource sharing), 是一種ajax跨域請求資源的方式。
cors分爲簡單請求
和複雜請求
兩類
請求方式使用下列方法之一:
GET HEAD POST
Content-Type的值僅限於下列三者之一:
text/plain multipart/form-data application/x-www-form-urlencoded
注意:對於簡單的請求,瀏覽器會直接發送cors請求,具體來講就是在header中加入origin請求頭字段。在響應頭回服務器設置相關的cors請求,響應頭字段爲容許跨域請求的源。請求時瀏覽器在請求頭的Origin中說明請求的源,服務器收到後發現容許該源跨域請求,則會成功返回。
使用了下面任一HTTP方法
PUT DELETE CONNECT OPTIONS TRACE PATCH
Content-Type的值不屬於下列之一:
application/x-www-form-urlencoded multipart/form-data text/plain
當符合複雜請求的條件時,瀏覽器會自動先發送一個options請求。若是發現瀏覽器支持該請求,則會將真正的請求發送到後端。若是瀏覽器發現服務端不支持該請求,則會在控制檯拋出錯誤。
這個字段是必要的,它的值是逗號分割的一個字符串,代表服務器支持的全部跨域請求的方式
Access-Control-Allow-Headers
若是瀏覽器請求包括這個字段,則這個字段也是必須的,它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在「預檢"中請求的字段
這個字段與簡單請求時的含義相同
該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即容許緩存該條迴應1728000秒(即20天),在此期間,不用發出另外一條預檢請求
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> hello </body> </html>
serve.js
let express = require('express'); let app = express(); app.use(express.static(__dirname)); app.listen(3000)
以當前這個做爲靜態文件目錄,先要在終端裏面node serve.js
服務器打開,訪問localhost:3000
就能夠把 hello 顯示出來。
這是一個完整的複雜請求例子:
index.js
let xhr = new XMLHttpRequest() document.cookie = 'name=xiaoming' xhr.withCredentials = true xhr.open('PUT', 'http://localhost:4000/getData', true) xhr.setRequestHeader('name', 'xiaoming') xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { console.log(xhr.response) //獲得響應頭,後臺需設置Access-Control-Expose-Headers console.log(xhr.getResponseHeader('name')) } } } xhr.send()
serve.js
let express = require('express'); let app = express(); app.use(express.static(__dirname)); app.listen(3000)
serve2.js
let express = require('express') let app = express() let whitList = ['http://localhost:3000'] app.use(function(req, res, next) { let origin = req.headers.origin if (whitList.includes(origin)) { // 設置哪一個源能夠訪問我 res.setHeader('Access-Control-Allow-Origin', origin) // 容許攜帶哪一個頭訪問我 res.setHeader('Access-Control-Allow-Headers', 'name') // 容許哪一個方法訪問我 res.setHeader('Access-Control-Allow-Methods', 'PUT') // 容許攜帶cookie res.setHeader('Access-Control-Allow-Credentials', true) // 預檢的存活時間 res.setHeader('Access-Control-Max-Age', 6) // 容許返回的頭 res.setHeader('Access-Control-Expose-Headers', 'name') if (req.method === 'OPTIONS') { res.end() // OPTIONS請求不作任何處理 } } next() }) app.put('/getData', function(req, res) { console.log(req.headers) res.setHeader('name', 'ming') //返回一個響應頭,後臺需設置 res.end('早上好') }) app.get('/getData', function(req, res) { console.log(req.headers) res.end('早上好') }) app.use(express.static(__dirname)) app.listen(4000)
postMessage
方法容許來自不一樣源的腳本採用異步方式進行有限的通訊,能夠實現跨文本檔、多窗口、跨域消息傳遞。
otherWindow.postMessage(message, targetOrigin, [transfer])
高級瀏覽器Internet Explorer 8+
, chrome
,Firefox
, Opera
和 Safari
都將支持這個功能
a.html 向 b.html傳遞 "早上好",而後 a.html 傳回"今每天氣真好"
a.html
<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> <script> function load(){ let frame = document.getElementById('frame'); frame.contentWindow.postMessage('早上好','http://localhost:4000'); window.onmessage=function(e){ console.log(e.data) } } </script>
b.html
<script> window.onmessage = function(e){ console.log(e.data); e.source.postMessage('今每天氣不錯',e.origin) } </script>
a.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(3000)
b.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(4000)
window.name
是一個 window
對象的內置屬性,name
屬性能夠設置或返回存放窗口的名稱的一個字符串。
在頁面在瀏覽器端展現的時候,咱們總能在控制檯拿到一個全局變量window,該變量有一個name屬性,有如下的特徵:
http://localhost:3000
http://localhost:4000
window.name
,把a引用的地址改成ba.html
<iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe> <script> let first = true function load(){ if(first){ let iframe = document.getElementById('iframe'); iframe.src="http://localhost:3000/b.html"; first = false; }else{ console.log(iframe.contentWindow.name) } } <script>
b.html
<body> 早上好 </body>
c.html
<script> window.name='今每天氣不錯' </script>
a.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(3000)
b.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(4000)
location
是javascript
裏面的內置對象,如location.href
就管理頁面的url,用loaction.href=url
就能夠直接將頁面重定向url,而location.hash
則能夠用來獲取或設置頁面的標籤值,hash屬性是一個可讀可寫的字符串,該字符串是URL的錨部分(從#號開始的部分)
#
的含義
#
表明網頁中的位置,其右邊的字符,就是該位置的標識符,例如:
http://www.juejin.com/index.html#drafts
就是表明index.html的drafts位置,瀏覽器讀取這個URL後,會自動將print位置滾動至可視區域
HTTP請求不包括#
#是用來指導瀏覽器的動做的,對服務器端徹底無用,因此,HTTP請求中不包括#
例如:
http://www.juejin.com/index.html#drafts
瀏覽器實際發出的請求是這樣的:
GET/index.html HTTP/1.1 Host:www.juejin.com
能夠看到,只是請求的index.html,沒有#drafts部分
#
後的字符
在第一個#出現的任何字符,都會被瀏覽器解讀爲位置標識符,這意味着,這些字符不會被髮送到服務器端
改變#不觸發網頁重構
單單改變#後的部分,瀏覽器只會滾動到相應的位置,不會從新加載網頁
改變#會改變瀏覽器的訪問歷史
每一次改變#後的部分,都會在瀏覽器的訪問歷史中增長一個記錄,使用"後退"按鈕,就能夠回到上一個位置
讀取#值
window.location.hash這個屬性可讀可寫。讀取時,能夠用來判斷網頁狀態是否改變;寫入時,則會在不重載網頁的前提下,創造一條訪問歷史記錄
onhashchange事件
當#值發生變化時,就會觸發這個事件。IE8+、Firefox 3.6+、Chrome 5+、Safari 4.0+支持該事件
Google抓取#的機制
默認狀況下,Google的網絡忽視URL的#部分
路徑後面的hash值能夠用來通訊。目的:a.html
想訪問 c.html
跨域相互通訊。
a.html
<iframe src="http://localhost:4000/c.html#goodmorning"></iframe> <script> window.onhashchange = function () { console.log(location.hash); } </script>
b.html
<script> window.parent.parent.location.hash = location.hash </script>
c.html
<script> console.log(location.hash); let iframe = document.createElement('iframe'); iframe.src = 'http://localhost:3000/b.html#goodevening'; document.body.appendChild(iframe); </script>
a.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(3000)
b.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(4000)
主要用於主域相同的域之間的數據通訊,注意 僅限主域相同,子域不一樣的跨域應用場景。
實現的原理:兩個頁面都經過js強制設置 document.domain
爲基礎主域,就實現了同域
這個方法只能用於二級域名相同的狀況下,好比:
www.baidu.com hhh.baidu.com
這就適用於domain方法
a.html
<iframe src="http://b.ming.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe> <script> document.domain = 'ming.cn' function load() { console.log(frame.contentWindow.a); } </script>
b.html
<div> 早上好啊 </div> <script type="text/javascript"> document.domain = 'ming.cn' var a = 99999; </script>
a.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(3000, () => { console.log('server run at 3000') })
b.js
let express = require('express') let app = express(); app.use(express.static(__dirname)); app.listen(4000, () => { console.log('server run at 4000') })
這個就能夠經過http://a.ming.cn:3000/a.html獲取到頁面http://a.ming.cn:3000/b.htm中的a的值99999
注意:這裏我把我電腦上邊的hosts修改了一下,否則不能出來效果
WebSocket
是一種網絡通訊協議,它實現了瀏覽器與服務器全雙工通訊,同時容許跨域通信。原生WebSocket API使用起來不太方便,咱們使用Socket.io
,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。
Web瀏覽器和服務器都必須實現 WebSockets 協議來創建和維護鏈接。因爲 WebSockets 鏈接長期存在,與典型的HTTP鏈接不一樣,對服務器有重要的影響。
注意:基於多線程或多進程的服務器沒法適用於WebSockets
,由於它旨在打開鏈接,儘量快地處理請求,而後關閉鏈接。任何實際的WebSockets
服務器端實現都須要一個異步服務器。
a.html
<script> //高級api 不兼容 socket.io(通常使用它) let socket = new WebSocket('ws://localhost:3000'); socket.onopen=function(){ socket.send('早上好啊') } socket.onmessage = function(e){ console.log(e.data); } </script>
a.js
let express = require('express') let app = express(); let WebSocket = require('ws') let wss = new WebSocket.Server({port:3000}) wss.on('connection',function(ws){ ws.on('message',function(data){ console.log(data) ws.send('今每天氣真好') }) })
以上就是整理的一些跨域的方法,我以爲通常用cors,jsonp等常見的方法就能夠了,不過遇到了一些特殊狀況,咱們也要作到有不少方法是能夠選擇的,相信這篇文字會對你們有幫助!
歡迎你們加入❤️❤️❤️