实际开发中,经常会遇到需要发起跨域请求的时候,总的来说还是有不少解决办法的,说到需要跨域的原因就不得不提同源政策了,还有跨域的究极解法 CORS。
什么是同源政策(Same-Origin Policy
)
只要协议、域名、端口有任何一个不同,都被当作是不同的源。
更详细的解释可以看这里
为什么要有同源政策
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。而且保护用户隐私信息,防止身份伪造等(读取 Cookie)。
document.domain
对于主域相同,子域不同的情况,可以设置 document.domain
来规避同源政策。
1 2
| document.domain = 'tinykid.org'
|
location.hash
这种方式是子框架具有修改父框架 src 的 hash 值,通过这个属性进行传递数据,且更改 hash 值,页面不会刷新。但是传递的数据的字节数是有限的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| iframe = document.createElement('iframe') iframe.style.display = 'none' var state = 0
iframe.onload = function() { if (state === 1) { var data = window.location.hash iframe.contentWindow.document.write('') iframe.contentWindow.close() document.body.removeChild(iframe) } else if (state === 0) { state = 1 iframe.contentWindow.location = 'http://tinykid.org/xxx.html' } } document.body.appendChild(iframe)
parent.location.hash = "data";
|
window.postMessage
这个方法是 HTML5 中引入的一个新 API,用于跨域的父子窗口通信,不受同源策略限制。通过它可以实现对存储的读写,DOM 的操作等。
1 2 3 4 5 6 7 8 9 10 11 12 13
| var popup = window.open('http://child.com');
popup.postMessage('Hello World!', 'http://child.com');
window.opener.postMessage('Nice to see you', 'http://parent.com');
window.addEventListener('message', function(e) { }, false);
|
WebSocket
WebSockets 是一个可以创建和服务器间进行双向会话的高级技术。通过这个 API 你可以向服务器发送消息并接受基于事件驱动的响应,这样就不用向服务器轮询获取数据了。更多信息点这里。
JSONP
JSONP 虽然很好用,但是只支持 Get 方法,其思路是 script 标签是没有同源限制的,所以可以利用这点来发起跨域请求。看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| ;(function(global) { var id = 0, container = document.getElementsByTagName('head')[0]
function jsonp(options) { if (!options || !options.url) return
var scriptNode = document.createElement('script'), data = options.data || {}, url = options.url, callback = options.callback, fnName = 'jsonp' + id++
data['callback'] = fnName
var params = [] for (var key in data) { params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])) } url = url.indexOf('?') > 0 ? url + '&' : url + '?' url += params.join('&') scriptNode.src = url
global[fnName] = function(ret) { callback && callback(ret) container.removeChild(scriptNode) delete global[fnName] }
scriptNode.onerror = function() { callback && callback({ error: 'error' }) container.removeChild(scriptNode) global[fnName] && delete global[fnName] }
scriptNode.type = 'text/javascript' container.appendChild(scriptNode) }
global.jsonp = jsonp })(this)
|
CORS
CORS 可以说是跨域的究极解法方法,由浏览器自动完成。这个机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。具体描述可以看这里。
由于 MDN 已经讲解的非常仔细了,我就说一下注意事项。
对于客户端,需要注意设置 xhr.withCredentials = true
,不然无法携带 cookie。
对于服务端需要返回两个关键字段,Access-Control-Allow-Origin
和 Access-Control-Allow-Credentials
,这样基本就能顺利完成跨域请求。