1、Ajax 請求限制
Ajax 只能向自己的服務器發送請求。
比如現在有一個A網站,一個B網站,A網站中的HTML文件只能向A網站服務器發送Ajax請求,B網站中的HTML文件只能向B網站服務器發送Ajax請求,但是A網站時不能向B網站發送Ajax請求的,同理,B網站不能向A網站發送Ajax請求的
當A網站先B網站發送Ajax請求,我們稱之為跨域請求
如:在本地打開AJax文件先服務器請求內容
上面報錯熟不熟悉,這就是因為發生了跨域請求導致的錯誤
2、什麼是同源
瀏覽器為了安全問題,做出了同源策略的限制,即跨域請求默認是不被允許的
這裏所説的同源是指:協議名、主機號、端口號 這三部分是一樣。相同域之間的請求是不受限制的,而不同域之間不能互相請求的,這就是Ajax的同源策略
如果兩個頁面擁有相同的協議、域名和端口,那麼兩個頁面就屬於同一個源,其中只要有一個不同,就是不同源
同源策略是為了保護用户信息的安全,防止惡意的網站竊取數據,最初的同源策略是指A網站在客户端設置Cookie,B網站則不能訪問的
3、解決跨域請求
Ajax默認是不允許進行跨域請求的,但是我們卻時常進行非同源請求,擺在我們面前的就是解決跨域請求
解決跨域請求有4種方法:
JSONP解決跨域請求CORS跨域資源共享- 代理服務器
- 基於
<iframe>標籤
主要了解前兩種
3.1 JSONP 解決跨域請求
JSONP 不屬於Ajax請求,但是它可以模擬 Ajax 請求
有沒有想過,我們平時在HTML頁面中的 <img>、<link>、<script> 等標籤中的src屬性不受跨域請求的,所以我們是否也可以讓 <script> 去請求服務器的JS文件來獲取數據嗎?
of course 當然
分為三步走:
- 將不同源的服務器請求地址寫在
<script>標籤的src屬性中
<script src="http://127.0.0.1:3000/test"></script>
- 服務端響應數據必須是一個函數的調用,真正要發送給客户端的數據需要作為函數調用的參數
const data = "getData({namer: "周深", song: "大魚"})";
res.rend(data); // 發送數據
- 在全局作用域下定義函數(兩函數名要一樣)
function getData(options) {
alert(options.namer + '唱了一首' + options.song)
}
栗子:
客户端:
<body>
<script>
function getData(data) {
alert(data.namer + '唱了一首' + data.song)
}
</script>
<script src="http://127.0.0.1:3000/test"></script>
</body>
服務器端:
app.get('/test', function(req, res) {
let data = 'getData({namer: "周深", song: "大魚"})'
res.send(data)
})
JSONP 優化
第一種:把函數名通過URL傳給服務端
我們可以把本地函數通過URL傳參 ?callback=getData 的方式,讓服務器知道本地函數的名字
這樣的好處就是客户端修改函數名,服務器就不受影響
<script>
function getData(data) {
alert(data.namer + '唱了一首' + data.song)
}
</script>
<script src="http://127.0.0.1:3000/test?callback=getData></script>
app.get('/test', function(req, res) {
res.jsonp({ namer: '薛之謙', song: '演員' })
})
jsonp 函數已經幫我們做好了處理,會自動獲取參數中callback 的值作為函數,所以服務端只需要傳遞數據即可
第二種:將script請求的變成動態請求
<button>按鈕</button>
<script>
function getData(data) {
alert(data.namer + '唱了一首' + data.song)
}
</script>
<script type="text/javascript">
let btn = document.querySelector('button');
btn.onclick = function() {
let script = document.createElement('script');
script.src = 'http://127.0.0.1:3000/test';
document.body.appendChild(script);
// 為script添加onload事件,過河拆橋
script.onload = function() {
document.body.removeChild(script)
};
};
</script>
發送請求的時候動態創建script標籤發送,結束之後隨即把它刪除了
但是每次我們都要寫這麼一坨代碼嗎?説這句話的時候就知道又要瘋轉了
下面我們來封裝一個jsonp函數吧
function jsonp(options) {
// 創建標籤
let script = document.createElement('script')
// 拼接字符串
let params = '';
for (let key in options.data) {
params += '&' + key + '=' + options.data[key]
}
// 創建隨機函數,toString將數字轉換字符串去除小數點.
let fnName = 'myFn' + Math.random().toString().replace('.', '');
// fnName不是全局函數,利用window將其變成函數
window[fnName] = options.success;
script.src = options.url + '?callback=' + fnName + params;
document.body.appendChild(script)
// 為script標籤添加onload事件
script.onload = function() {
// 刪除掉script標籤
document.body.removeChild(script)
}
}
調用jsonp(需要自行導入jsonp函數)
<body>
<button id="xzq">薛之謙</button>
<button id="zs">周深</button>
<script type="text/javascript">
let btn1 = document.querySelector('#xzq');
let btn2 = document.querySelector('#zs');
btn1.onclick = function() {
jsonp({
url: 'http://127.0.0.1:3000/test',
data: {
namer: '薛之謙',
song: '剛剛好'
},
success: function(data) {
alert(data.namer + '--' + data.song);
}
})
};
btn2.onclick = function() {
jsonp({
url: 'http://127.0.0.1:3000/test',
data: {
namer: '周深',
song: '大魚'
},
success: function(data) {
alert(data.namer + '--' + data.song);
}
})
};
</script>
</body>
後台:
app.get('/test', function(req, res) {
res.jsonp(req.query)
})
3.2 CORS 跨域資源共享
CORS 全稱為 Cross-origin Rescourse Sharing,即跨域資源共享,是W3C推出的一種新的機制。
它允許瀏覽器向跨域服務器發送Ajax請求,克服了Ajax 只能同源使用的限制
所以,當我們在使用CORS處理跨域請求時,瀏覽器判斷這是一個跨域請求,會自動幫我們處理好相應的跨域請求配置,添加一些附加頭部信息,而我們要做的僅僅是在服務器端判斷是否允許這個域訪問
1、簡單請求
簡單請求必須滿足三個請求:
- 請求方式為
GET、POST、HEAD - 數據類型
Content-Type只能是application/x-www-form-urlencoded、multipart/form-data或text/plain - 不使用自定義的請求頭
所以,如果是簡單請求,可以設置一個允許跨域請求的頭信息
在服務端設置:
app.post('/cache', function(req, res) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080')
res.send('你要一直走,直到燈火通明')
})
允許這個 http://localhost:8080 域請求,如果我們想要任何域能夠請求,只需要換成 * 即可
res.setHeader('Access-Control-Allow-Origin', '*')
現在任何所屬域的Ajax來請求這個服務器,都會被賦予訪問權限,都可以正常響應數據了
2、預請求
域請求是一種相對比較複雜的一些的請求,當出現以下條件時,就會被當做預請求
- 請求方式是
GET、POST、HEAD以外的方式,比如:PUT、DELETE等 - 使用
POST請求,但數據類型是application/xml或者text/xml的XML數據類型 - 使用自定義的請求頭信息
3、附帶憑證信息的請求
XMLHttpRequest 對象在發送請求的同時會發送憑證(Cookie 和 驗證信息),但是跨域請求並不會發送
所以想要傳遞Cookie給服務器,就要在請求頭裏面設置允許發送憑證信息。客户端和服務端都需要設置
感興趣的話可以繼續探索......
總結
Ajax的同源策略就是説只有協議、主機號、端口的都一樣的域才能進行請求,而這樣做的目的就是為了安全,防止網站信息被竊取- 解決跨域請求的方式有很多,
JSONP請求的是可執行腳本,適合用於請求我們自己的服務器 CORS是H5的一種新特性,基本支持所有的請求,也是我們常用的,缺點就是低版本的瀏覽器會有兼容性問題