這是之前面試的時候面試官提問的一道面試題。
具體題目是:為什么表單提交不會出現跨域,而使用 Ajax 發送 post 請求時卻會出現跨域的情況。
那什么情況下會出現跨域:
協議 + 域名 + 端口 三者只要有一個不一樣,就會出現跨域。
那為什么表單能夠跨域發送請求,而 Ajax 卻不能發送跨域請求
歸根結底:跨域是為了阻止用戶讀取到另一個域名下的內容
而 Ajax 可以獲取響應,但瀏覽器認為這不安全,所以攔截了響應
但是表單并不會獲取新的內容,所以可以發起跨域請求。
前者是發送跨域請求給到后端,并不去接收服務器返回的信息
后者是發送跨域請求給到后端,并接收服務器返回的信息
那該如何解決跨域
#方法一:使用 JSONP
原理是利用<script>
標簽的跨域特性,可以不受限制地從其他域中加載資源
js
代碼解讀
復制代碼function jsonp(options) { var script = document.createElement('script'); // 參數處理 var params = ''; for (var attr in options.data) { params += '&' + attr + '=' + options.data[attr]; } // 設置回調函數 var successCallback = `successCallback`; window[successCallback] = options.success; script.src = options.url + '?callback=' + successCallback + params; document.body.appendChild(script);}
代碼解釋:
請求成功后,前端得執行回調函數,但 script 腳本是執行不到 success() 方法的。
這是因為 success() 方法 并不是 全局函數,所以需要將 success() 方法 改成全局函數
js
代碼解讀
復制代碼var successCallback = `successCallback`;window[successCallback] = options.success;
并在請求參數的基礎上,需要添加 callback
參數,值對應需要回調的函數名
js
代碼解讀
復制代碼script.src = options.url + '?callback=' + successCallback + params;
使用:
js
代碼解讀
復制代碼var btn = document.getElementById('btn');btn.addEventListener('click', function() { jsonp({ url: 'http://localhost:3001/getUserInfo', data: { name: '浩浩' }, success: function(data) { alert('UserInfo:' + JSON.stringify(data)); } })});
JSONP 優缺點
簡單兼容性好,可用于解決主流瀏覽器的跨域數據訪問的問題
僅支持get方法具有局限性,不安全可能會遭受 XSS 攻擊
#方法二:CORS
CORS 是一個 W3C 標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。 CORS 需要瀏覽器和服務器同時支持。但是目前基本上瀏覽器都支持,所以我們只要保證服務器端服務器實現了 CORS 接口,就可以跨源通信。
后端解決跨域問題,就是在服務器端給響應添加頭信息
Name | Required | Comments |
---|---|---|
Access-Control-Allow-Origin | 必填 | 允許請求的域 |
Access-Control-Allow-Methods | 必填 | 允許請求的方法 |
Access-Control-Allow-Headers | 可選 | 預檢請求后,告知發送請求需要有的頭部 |
Access-Control-Allow-Credentials | 可選 | 表示是否允許發送cookie,默認false; |
Access-Control-Max-Age | 可選 | 本次預檢的有效期,單位:秒; |
在 node 上處理
js
代碼解讀
復制代碼// 方式一const Koa = require('koa')const app = new Koa()app.all("*", (req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "content-type"); // 關鍵點 next()})
js
代碼解讀
復制代碼// 方式二const Koa = require('koa')const app = new Koa()app.all("*", (req, res, next) => { // 設置域名跨域 res.header("Access-Control-Allow-Origin", "http://127.0.0.1"); // 跨域允許的請求方式 res.header("Access-Control-Allow-Headers", "DELETE,PUT,POST,GET,OPTIONS"); // 關鍵點 next()})
在 Nginx 上處理
js
代碼解讀
復制代碼location /example { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods *; # add_header Access-Control-Allow-Methods GET,POST,OPTIONS;}