一、同源策略(Same-Origin Policy),是瀏覽器的一種安全策略.
1、同源(即url相同):協議、域名、埠號 必須完全相同。(請求是來自同一個服務)
2、跨域:違背了同源策略,即跨域。
3、ajax請求是遵循同源策略的。
■ 同源請求例子(在瀏覽器訪問127.0.0.1:9000/server-orign,然後點選按鈕傳送同源請求):
-
服務端對同源請求處理:
//3、建立路由規則(request 是請求的報文,response是響應的報文) app.get('/server-orign', (request, response) => { //響應一個頁面 response.sendFile(__dirname + '/同源策略.html'); }); app.get('/data', (request, response) => { //響應資料 response.send('服務端返回的資料'); });
-
客戶端html:
const btn = document.getElementsByTagName('button')[0]; btn.onclick = function () { //console.log('測試'); //傳送ajax請求 const xhr = new XMLHttpRequest(); xhr.open('GET', '/data');//同源請求,路徑可以簡寫 xhr.send(); xhr.onreadystatechange = function () { if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ console.log(xhr.response); } } } }
二、解決跨域(JSONP、CORS)
1、非官方解決途徑JSONP:
(1) JSONP 是什麼?
JSONP(JSON with Padding),是一個非官方的跨域解決方案,只支援 get 請求
。
(2) JSONP 怎麼工作的?
在網頁有一些標籤天生具有跨域能力
,比如:img link iframe script。 JSONP 就是利用 script 標籤
利用script的路徑src作為請求路徑,然後只需要服務端返回的資料封裝成js程式碼
即可
■ jsonp例子1:
- 服務端處理,響應返回js程式碼(跟我們平時引入外部的js程式碼一樣啦):
app.get('/jsonp', (request, response) => {
//響應資料
response.send('console.log("hello jsonp")');
});
- 客戶端處理:
<script src="http://127.0.0.1:9000/jsonp"></script>
■ jsonp例子2(驗證使用者名稱,顯示密碼):
-
服務端處理:
//服務端驗證使用者名稱 app.all('/check', (request, response) => { //響應資料 // response.send('console.log("hello jsonp")'); const data = { name:'admin', password:'123456' }; let str = JSON.stringify(data); response.end(`handle(${str})`);//呼叫客戶端(前端)的handle方法 });
-
客戶端處理:
使用者名稱: <input type="text" name="name"/> <p></p> <script> const input = document.getElementsByTagName('input')[0]; const p = document.querySelector('p'); //宣告一個handle函式,用來給服務端呼叫,以處理服務端返回的資料 function handle(data) { input.style.border = "solid 1px #f00"; let username = input.value; //若使用者名稱正確,將服務端返回的資料顯示到p標籤內 if(username === data.name){ p.innerHTML = data.password; } } input.onblur = function () { //向服務端傳送請求(jsonp),檢查使用者是否存在 const script = document.createElement('script'); //設定script標籤的src為ajax的url script.src = 'http://127.0.0.1:9000/check'; //在介面生成dom元素 document.body.appendChild(script); } </script>
❀ jsop跨域訪問第三方介面,請求成功,但是卻拿不到資料?
從上面的例子可以看到當服務端是第三方(例如訪問百度的快遞介面:https://express.baidu.com/express/api/express),
通過jsonp跨域方式去訪問第三方介面,結果:請求成功,狀態碼是200,但是無法拿到響應的資料.
- 原因1:第三方服務在建立路由規則時,
設定不允許跨域訪問
,所以提示CORB,無法直接拿到資料; - 原因2:想通過script標籤的 src='請求url' 引入服務端響應回來的js程式碼,但是且不知道第三方服務端響應時是怎麼書寫的,不知道回撥了什麼函式
- 總結:jsonp 解決跨域訪問,除了型別是GET,
還需要伺服器的配合才能完成跨域
□ 我們自己的服務端沒設定允許跨域訪問,預設為不允許跨域訪問
□ 但是我們在服務端設定請求時響應回去一些js程式碼(handle函式的呼叫(data的json物件作為引數)的js程式碼
)
□ 在客戶端我們通過 src='請求url' 引入服務端響應回來的js程式碼,而我們知道我們服務端響應回來一個handle函式的呼叫
,所以我們在客戶端定義了一個handle回撥函式(handle函式的引數是響應回來的json格式的資料)。
2、官方解決途徑CORS:
(1)CORS: Cross-Origin Resource Sharing 跨域資源共享, 即服務端設定響應頭是允許跨域的
(2)CORS的更多介紹:跨源資源共享(CORS) - HTTP | MDN (mozilla.org)
-
服務端cors跨域請求處理:
app.all('/cors', (request, response) => { //設定響應頭(允許跨域) response.setHeader('Access-Control-Allow-Origin', '*'); // response.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:9000); //設定響應頭(允許自定義請求頭) response.setHeader('Access-Control-Allow-Headers', '*'); //設定響應頭(允許自定義請求方式) response.setHeader('Access-Control-Allow-Method', '*'); //設定響應體 response.send('hello cors'); });
-
客戶端處理:
<button>點選傳送ajax請求</button> <script> const btn = document.getElementsByTagName('button')[0]; btn.onclick = function () { const xhr = new XMLHttpRequest(); xhr.open('get', 'http://127.0.0.1:9000/cors'); xhr.send(); xhr.onreadystatechange = function () { if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300){ console.log(xhr.response); } } } } </script>