瀏覽器有同源策略,協議 域名 端口不一樣,就不能互相訪問資源javascript
function show(data){
console.log(data)
}
複製代碼
<script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=lol&cb=show"></script>
// cb就是厚度返回的執行函數名
複製代碼
//後端會返回
show({
p: false
q: "lol"
s: (10) ["lol官網", "lol手遊", "lol轉區", "lol是什麼意思",
"lol半價吧", "lol春季賽", "lol轉區系統"]
})
複製代碼
//使用
jsonp({
url:'xxx',
params:{wd:'xxx'},
cb:'xx'
}).then(res=>console.log(data))
複製代碼
let jsonp = ({url,params,cb})=>{
return new Promise((resolve,reject)=>{
//1.建立一個script標籤
let script = document.createElement('script')
//3. 將cb掛載到全局中,後端會返回cb(數據),接收數據返回數據便可
window[cb]=function(data){
resolve(data)
//4. 記得將增長到script刪除
script.remove()
}
//2.將params和cb轉化成wd=xxx&cb=xxx
params = {...params,cb}
let arr = []
for(let key in params){
arr.push(`${key}=${params[key]}`)
}
script.src=url+'?'+arr.join('&')
document.body.appendChild(script)
})
}
複製代碼
let express = require('express')
let app = new express()
app.get('/say',function(req,res){
let {wd,cb}=req.query
res.end(`${cb}('返回的數據')`)
})
app.listen(3000,function(){console.log('啓動')})
複製代碼
這是一個後端的解決方案,能夠用node來模擬html
啓動兩個服務器,一個3000端口,一個4000端口java
3000端口啓動一個網頁,在網頁中訪問4000端口的資源node
const express = require('express');
let app = express();
//將這個目錄下的資源以靜態資源的方式提供訪問
app.use(express.static(__dirname));
app.listen(3000, function () {
console.log('3000啓動');
});
複製代碼
// index.html
<script> let xhr = new XMLHttpRequest(); xhr.open('get', 'http://localhost:4000/say'); xhr.onreadystatechange = function () { if(xhr.readyState === 4 && 300 >= xhr.status&&xhr.status >= 200) { console.log(xhr.response); } }; xhr.send(); </script>
複製代碼
瀏覽器輸入:localhost:3000/index.html
,會警告跨域問題git
而且端口:4000的服務是有接受到端口3000的請求的,只是返回的內容被瀏覽器屏蔽了github
提示:Access-Control-Allow-Origin
沒有容許http://localhost:3000
訪問資源,能夠ajax
//設置容許訪問的域名的白名單
let whiteList = ['http://localhost:3000'];
app.use(function (req, res, next) {
//獲取請求的origin
let origin = req.headers.origin;
if (whiteList.includes(origin)) {
//若是在白名單內,則容許他訪問
res.setHeader('Access-Control-Allow-Origin', origin);
}
next();
});
複製代碼
但這種方式只容許get,post,head這種簡單的請求,若是是put呢express
xhr.open('put','http://localhost:3000/say')
複製代碼
app.put('/say',function(req,res){
res.end('post請求')
})
複製代碼
瀏覽器報錯,咱們須要後端設置容許put請求json
app.use(function(req,res){
res.setHeader('Access-Control-Allow-Methods','PUT')//記得大寫
})
複製代碼
若是咱們在請求頭中加點信息呢,後端
xhr.setRequestHeader('name','huzhiwu')
複製代碼
瀏覽器警告說,須要後端容許那個請求頭
但端口4000能接受到這個請求頭
設置容許的請求頭
res.setHeaders('Access-Control-Allow-Headers','name')
複製代碼
咱們在端口4000的服務中,打印每次請求的method
app.use(function(req,res){
console.log(req.method)
})
複製代碼
刷新瀏覽器時,會發現。端口4000打印了兩個method
options是瀏覽器在發現跨域時,會先向服務器一個預檢請求,該方法返回容許訪問的methods,若是請求方法不在methods中則報錯,存在的話,就發送真正的請求
這樣子每次發送一個請求,實際發送了兩個請求,是比較耗性能的,若是咱們的方法不常常改變,可讓
瀏覽器在一段時間內預檢一次就夠了
req.setHeader('Access-Control-Max-Age',5)//單位秒
複製代碼
let xhr = new XMLHttpRequest();
xhr.open('put', 'http://localhost:4000/say');
document.cookie='name=huzhiwu'
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && 300 >= xhr.status && xhr.status >= 200) {
console.log(xhr.response);
}
};
xhr.send();
複製代碼
// 端口4000的服務器
app.use(function(req,res){
console.log(req.headers)
})
複製代碼
發現沒有咱們攜帶的cookie
在ajax請求中增長
xhr.withCredentials = true;
複製代碼
瀏覽器又報錯了
在node中設置容許跨域攜帶cookie便可
res.serHeader('Access-Control-Allow-Credentials')
複製代碼
app.put('/say',function(req,res){
res.setHeader('name','huzhiwu')
res.end('put')
})
複製代碼
let xhr = new XMLHttpRequest();
xhr.open('put', 'http://localhost:4000/say');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && 300 >= xhr.status && xhr.status >= 200) {
console.log(xhr.getResponseHeader('name'));
}
};
xhr.send();
複製代碼
瀏覽器又報錯
瀏覽器以爲服務器返回給你的請求頭不安全,不給你獲取
在node中設置
res.setHeader('Access-Control-Expose-Headers','name')
複製代碼
//容許哪源能夠訪問我
res.setHeader('Access-Control-Allow-Origin',origin)
//容許那個方法能夠訪問我
res.setHeader('Access-Control-Allow-Methods','PUT')
//預檢存活時間
res.setHeader('Access-Control-Max-Age',5)//單位秒
//容許請求攜帶的headers
res.setHeader('Access-Control-Allow-Headers','name')
//容許瀏覽器獲取服務器返回的headers
res.setHeader('Access-Control-Expose-Headers','name')
複製代碼
頁面跟嵌套在iframe中的頁面通訊
分別開啓a,b兩個服務,端口號分別是3000,4000
//a.js b.js
const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.listen(4000/3000, function () {
console.log('4000/3000啓動');
});
複製代碼
a頁面放在3000端口,b頁面放在4000端口
在b頁面中引用a頁面
<body>
<iframe src='http://localhost:3000/a.html' id='aIframe' onload='load()'>
</iframe>
<script> function load(){ let frame = document.getElementById('aIframe') //給嵌套在iframe中的頁面的window發送信息 //第二個參數是origin frame.contentWindow.postMessage('你好我是b頁面','http://localhost:3000/a.html') } //b頁面監聽來自a頁面的信息 window.onmessage=function(e){ console.log(e.data) } </script>
</body>
複製代碼
在a頁面中監聽b頁面的信息
window.onmessage=function(e){
console.log(e.data)
//收到信息後給b頁面發送信息,
e.source.postMessage('你好,我是a頁面',e.origin)
}
複製代碼
瀏覽器打開http://localhost:4000/b.html
發送信息
window.postMessage('信息',orign)
複製代碼
接收信息
window.onmessage=function(e){
console.log(e.data)
}
複製代碼
window.name默認爲空字符串,咱們能夠在name中存入信息,跨域訪問他
http://localhost:3000
http://localhost:4000
開啓兩個服務器,分別是3000和4000端口
c頁面
<script> window.name='我是c頁面' </sctript> 複製代碼
a頁面
<body>
<iframe src='http://localhost:4000/c.html' id='frame' onload='load()'>
</iframe>
<script> let first=true let frame = document.getElementById('frame') function load(){ //第一次加載完成,當即將frame的src轉到同源下 if(first){ frame.src='http://localhost:3000/b.html' first=false; }else{ //同源下,便可訪問name console.log(frame.contentWindow.name) } } </script>
</body>
複製代碼
當網絡請求跨域時,可使用代理。
服務器訪問服務器沒有跨域問題.因此,咱們的作法是利用中間的代理瀏覽器向目標瀏覽器發請求。
a.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
//將請求轉發到另外一個服務器
const apiProxy = createProxyMiddleware('/say', {
target: 'http://localhost:4000',
});
const app = express();
app.use(express.static(__dirname));
app.use(apiProxy);
app.listen(3000, function () {
console.log('3000啓動');
});
複製代碼
b.js
const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.get('/say', function (req, res) {
res.end('我是b服務器的信息');
});
app.listen(4000, function () {
console.log('4000啓動');
});
複製代碼
<script> let xhr = new XMLHttpRequest(); xhr.open('get', 'http://localhost:3000/say'); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && 300 >= xhr.status && xhr.status >= 200) { console.log(xhr.response); } }; xhr.send(); </script>
複製代碼
http-proxy-middleware的更多參數請看https://github.com/chimurai/http-proxy-middleware
舉個例子www.video.baidu.com和www.map.baidu.com是兩個二級域名,
// www.map.baidu.com
<body>
<script> window.name='huzhiwu' </script>
</body>
// www.video.baidu.com
<body>
<iframe src='www.map.baidu.com' id='frame' onload='load'>
</iframe>
<script> function load(){ let frame = document.getElementById('frame') console.log(frame.contentWindow.name)//跨域訪問不到 } </script>
</body>
複製代碼
由於跨域訪問不到,
但只要在兩個二級域名中加個document.domain='baidu.com'
便可互相訪問
做者:胡志武
時間:2020/05/06
若是喜歡的話,請點個贊吧,若是有錯漏處,請指正