谈谈跨域的其他方式

此篇文章是对跨域知识的补充,由于前面一篇已经介绍了 JSONP 这种跨域方式,这里就不再多赘述

前言

先总体来说说跨域的几种方式吧

  • JSONP

    通过动态生成 script 标签的方式来进行不同源网页之间的数据传递,具体看 这篇博客

  • CORS

    全称 跨源资源共享(Cross-Origin Resource Sharing),是目前 主流的 跨域方案

  • postMessage

    HTML5 的一个 api,使用它也可以进行跨域操作

CORS

这个 CORS 要怎么进行跨域呢?很简单,它的原理就是 设置 HTTP 请求头,一般来讲在前后端进行 Ajax 通信时使用,比如 xxx.com 想向 yyy.com 发送 Ajax 请求

// xxx.com 前端 Ajax 代码
let xhr = new XMLHttpRequest();
xhr.open('GET', 'yyy.com', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();

这个时候你去打开控制台,会看到一个 Access-Control-Allow-Origin 的错误,就是说你进行跨域请求了,但是 yyy.com 上的服务器没有给你跨域的控制权限,所以你需要 在 yyy.com 这个服务器上设置 Access-Control-Allow-Origin 这个 HTTP 头,使它允许来自 xxx.com 的请求

// 假设你的后端是基于 node.js 写的
var server = http.createServer(function(request, response){
...
response.setHeader('Access-Control-Allow-Origin', 'yyy.com');
...
}
...

当然了,你也可以图方便,将 Access-Control-Allow-Origin 设置成 *, 表示允许任意源的请求,若你不想这样,想要允许多个源的请求,你可以这样设置

var server = http.createServer(function(request, response){
...
if (request.headers.origin === 'yyy.com'
|| request.headers.origin === 'zzz.com') {
response.setHeader('Access-Control-Allow-Origin', request.headers.origin);
}
...
}
...

可是对于简单请求(GET, POST, HEAD)来说,只是需要设置这一个头就行了,假如需要非简单的请求呢(PUT, DELETE),这个时候浏览器一般会发送一个 预请求, 这个预请求的格式如下

Request Method: OPTIONS

所以相应的服务端需要设置 Access-Control-Allow-Methods 这个头,浏览器才能用 非简单请求进行通信

var server = http.createServer(function(request, response){
...
if (request.headers.origin === 'yyy.com'
|| request.headers.origin === 'zzz.com') {
response.setHeader('Access-Control-Allow-Origin', request.headers.origin);
response.setHeader('Access-Control-Allow-Methods', 'put,delete');
}
...
}

如果你还想要传送 Cookie ,你需要在浏览器端设置 withCredentials, 然后在服务器端设置 Access-Control-Allow-Credentials

// xxx.com 前端 Ajax 代码
...
xhr.withCredentials = true;
...

// yyy.com 后端代码
var server = http.createServer(function(request, response){
...
response.setHeader('Access-Control-Allow-Credentials', true);
...
}

postMessage

使用方法: postMessage('需要发送的消息', '对方域名')

<!-- xxx.com 前端代码 -->
<iframe id="iframe" src="yyy.com"></iframe>
<script>
let iframe = document.querySelector('#iframe');
iframe.setAttribute('style', 'display: none');
iframe.onload = function() {
iframe.contentWindow.postMessage('我是 xxx.com', 'yyy.com');
}
</script>

<!-- yyy.com 前端代码 -->
<script>
window.addEventListener('message', function(e) {
console.log(e.data, e.origin);
});
</script>

还有其他的方式?

hash + iframe

当然还有其他的方式了,比如有一种 hash + iframe

<!-- xxx.com 前端代码 -->
<iframe id="iframe" src="yyy.com"></iframe>
<script>
let iframe = document.querySelector('#iframe');
iframe.setAttribute('style', 'display: none');
iframe.src = iframe.src + '#' + JSON.stringify({data: 'hash'});
</script>

<!-- yyy.com 前端代码 -->
window.onhashchange = function() {
console.log(window.location.hash);
}

原理: 动态改变 iframe src 属性的 hash 值,然后接受方通过监听 onhashchange 这个事件来接受到其传过来的数据

WebSockets

WebSockets 的基础用法如下

const ws = new WebSocket('ws://xxx.com');
ws.onopen = function() {
// 连接成功建立
}
ws.onmessage = function(event) {
// 处理数据
}
ws.onerror = function() {
// 发生错误时触发,连接中断
}
ws.onclose = function() {
// 连接关闭时触发
}

其实就是在 xxx.com 上建立一个 长连接,一般使用 socket.io 第三方库来代替使用

几种方式的特点

  1. JSONP
    仅支持 GET 方式,而且也不方便确认 JSONP 是否请求失败

  2. CORS
    支持所有类型的 HTTP 方法,但是不兼容老版本浏览器

  3. postMessage
    仅支持 GET 方式

  4. hash + iframe
    仅支持 GET 方式以及 单向通信

  5. WebSockets
    支持双向通信