可能是最好的跨域解决方案了

如果是非简单请求,会在正式通信之前,发送一个预检请求(preflight),目的在于询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段,只有得到肯定答复,浏览器才会发出正式的请求,否则就报错。你可能发现我们在日常的开发中,会看到很多使用 OPTION 方法发起的请求,它其实就是一个预检请求:

OPTIONS /cors HTTP/1.1

Origin: http://xxx.xx

Access-Control-Request-Method: PUT

Accept-Language: en-US

… …

那么到底哪些是简单请求,哪些是非简单请求 ?

请求类型

不会触发 CORS 预检的,就是简单请求。哪些请求不会触发预检 ?使用以下方法之一:GET, HEAD, POST,并且 Content-Type 的值仅限于下列三者之一:

  • text/plain

  • multipart/form-data

  • application/x-www-form-urlencoded

相反,不符合上述条件的就是非简单请求啦。所以,实现 CORS 的关键是服务器,只要服务器实现了 CORS 的相关接口,就可以实现跨域。CORS 与 JSONP相比,优势是支持所有的请求方法,缺点是兼容性上较 JSONP 差。除了 JSONP 和 CORS 外,还有一种常用的跨域解决方案:PostMessage,它更多地用于窗口间的消息传递。

PostMessage


PostMessage 是 Html5 XMLHttpRequest Level 2 中的 API,它可以实现跨文档通信(Cross-document messaging)。兼容性上,IE8+,Chrome,Firfox 等主流浏览器都支持,可以放心用😊,如何理解跨文档通信?你可以类比设计模式中的发布-订阅模式,在这里,一个窗口发送消息,另一个窗口接受消息,之所以说类似发布-订阅模式,而不是观察者模式,是因为这里两个窗口间没有直接通信,而是通过浏览器这个第三方平台。

window.postMessage(message, origin, [transfer])

postMessage 方法接收三个参数,要发送的消息,接收消息的源和一个可选的 Transferable 对象,如何接收消息 ?

window.addEventListener(“message”, function receiveMessage(event) {}, false); // 推荐,兼容性更好

window.onmessage = function receiveMessage(event) {} // 不推荐,这是一个实验性的功能,兼容性不如上面的方法

接收到消息后,消息对象 event 中包含了三个属性:source,origin,data,其中 data 就是我们发送的 message。此外,除了实现窗口通信,postMessage 还可以同 Web Worker 和 Service Work 进行通信,有兴趣的可以 戳这里[6]。

Websocket


Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。什么是全双工通信 ?简单来说,就是在建立连接之后,server 与 client 都能主动向对方发送或接收数据。原生的 WebSocket API 使用起来不太方便,我们一般会选择自己封装一个 Websocket(嗯,我们团队也自己封了一个 : ))或者使用已有的第三方库,我们这里以第三方库 ws[7] 为例:

const WebSocket = require(‘ws’);

const ws = new WebSocket(‘ws://www.host.com/path’);

ws.on(‘open’, function open() {

ws.send(‘something’);

});

ws.on(‘message’, function incoming(data) {

console.log(data);

});

… …

需要注意的是,Websocket 属于长连接,在一个页面建立多个 Websocket 连接可能会导致性能问题。

Nginx 反向代理


我们知道同源策略限制的是:浏览器向服务器发送跨域请求需要遵循的标准,那如果是服务器向服务器发送跨域请求呢?答案当然是,不受浏览器的同源策略限制。利用这个思路,我们就可以搭建一个代理服务器,接受客户端请求,然后将请求转发给服务器,拿到响应后,再将响应转发给客户端:1cfcf2b95d0aa8fce5db2a47bff364e5.png这就是 Nginx 反向代理的原理,只需要简单配置就可以实现跨域:

# nginx.config

# …

server {

listen       80;

server_name  www.domain1.com;

location / {

proxy_pass   http://www.domain2.com:8080;  #反向代理

proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名

index  index.html index.htm;

# 当用 webpack-dev-server 等中间件代理接口访问 nignx 时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用

add_header Access-Control-Allow-Origin *;

add_header Access-Control-Allow-Credentials true;

# …

}

}

Node 中间件代理


实现的原理和我们前文提到的代理服务器原理如出一辙,只不过这里使用了 Node 中间件做为代理。需要注意的是,浏览器向代理服务器请求时仍然遵循同源策略,别忘了在 Node 层通过 CORS 做跨域处理:

const https = require(‘https’)

// 接受客户端请求

const sever = https.createServer((req, res) => {

const { method, headers } = req

// 设置 CORS 允许跨域

res.writeHead(200, {

‘Access-Control-Allow-Origin’: ‘*’,

‘Access-Control-Allow-Methods’: ‘*’,

‘Access-Control-Allow-Headers’: ‘Content-Type’,

})

// 请求服务器

const pr 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 oxy = https.request({ host: ‘xxx’, method, headers, …}, response => {

let body = ‘’

response.on(‘data’, chunk => { body = body + chunk })

response.on(‘end’, () => {

// 响应结果转发给客户端

res.end(body)

})

})

// 结束请求

proxy.end()

})

document.domain


二级域名相同的情况下,设置 document.domain 就可以实现跨域。**什么是二级域名 ?**a.test.com 和 b.test.com 就属于二级域名,它们都是 test.com 的子域。如何实现跨域 ?

document.domain = ‘test.com’ // 设置 domain 相同

// 通过 iframe 嵌入跨域的页面

const iframe = document.createElement(‘iframe’)

iframe.setAttribute(‘src’, ‘b.test.com/xxx.html’)

iframe.onload = function() {

// 拿到 iframe 实例后就可以直接访问 iframe 中的数据

console.log(iframe.contentWindow.xxx)

}

document.appendChild(iframe)

总结

当然,除了上述的方案外,比较 Hack 的还有:window.name, location.hash,但是这些跨域的方式现在我们已经不推荐了,为什么 ?因为相比之下有更加安全和强大的 PostMessage 作为替代。跨域的方案其实有很多,总结下来:

  • CORS 支持所有的 HTTP 请求,是跨域最主流的方案

  • JSONP 只支持 GET 请求,但是可以兼容老式浏览器

  • Node 中间件和 Nginx 反向代理都是利用了服务器对服务器没有同源策略限制

  • Websocket 也是一种跨域的解决方案

  • PostMessage 可以实现跨文档通信,更多地用于窗口通信

  • document.domain, window.name, location.hash 逐渐淡出历史舞台,作为替代 PostMessage 是一种不错的方案

写在最后


本文首发于我的 博客[8],才疏学浅,难免有错误,文章有误之处还望不吝指正!如果有疑问或者发现错误,可以在相应的 issues 进行提问或勘误如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励(完)

关于本文

作者:campcc

https://juejin.cn/post/6844903992057659400

参考资料

[1]

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

[2]

http://www.google.com**%EF%BC%8C%E7%AB%AF%E5%8F%A3%E6%98%AF

[3]

https://codepen.io/lycheelee/pen/MWWXKeN?editors=1000

[4]

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

[5]

https://caniuse.com/#search=CORS

[6]

https://juejin.cn/post/6844903665694687240#heading-9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值