跨域

跨域

之所以会出现跨域问题,主要是因为 浏览器的同源策略 所引起的,简单来说就是

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要安全机制

  • 受到同源限制
    • 无法读取不同源的 CookieLocalStorageIndexDB
    • 无法获得不同源的 DOM
    • 不能向不同源的服务器发送 Ajax 请求
  • 不受同源限制
    • 在浏览器中 <script><img><iframe><link> 等标签都可以跨域加载资源,而不受同源策略的限制

什么是跨域

我们先来看看一个域名的组成,比如

1
http://www.aaa.com:8080/script/index.js

一般由 协议(http://),子域名(www),主域名(aaa.com),端口号(8080),请求资源地址(script/index.js)组成

  • 协议,网络协议遍及 OSI 通信模型(OSI 七层模型,常用协议有 TCP/IPHTTPFTP 协议等)
  • 域名,Domain Name,网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)
  • 端口,是设备与外界通讯交流的出口,分为物理端口和虚拟端口(比如常见的 80 端口)

当协议,子域名,主域名,端口号中任意一个不相同的时候,都算作不同域,不同域之间相互请求资源,就算作跨域,比如 http://www.aaa.com/index.html 请求 http://bbb.com/index.phpJavaScript 出于安全方面的考虑,不允许跨域调用其他页面的对象,简单的理解就是因为 JavaScript 同源策略的限制,a.com 域名下的 JavaScript 无法操作 b.com 或是 c.a.com 域名下的对象

所谓同源策略,即同域名(IP),同端口,同协议

CORS

CORS 全称 Cross-Origin Resource Sharing,是 W3C 的一个标准,它定义如何跨域访问资源,浏览器将 CORS 请求分成两类,简单请求(simple request)和非简单请求(not-so-simple request),如下

  • 请求方法是以下三种方法之一,HEADGETPOST
  • HTTP 的头信息不超出以下几种字段
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type,只限于三个值 application/x-www-form-urlencodedmultipart/form-datatext/plain

只要同时满足以下两大条件,就属于简单请求,不同时满足上面两个条件,就属于非简单请求

简单请求

浏览器会带上 Origin 的请求头发送到服务器,服务器根据 Origin 判断是否许可,如果许可就会带上 CORS 相关响应头,如果不在许可范围内就不会带上 CORS 相关的响应头,浏览器再根据响应头中是否有相关的 CORS 响应头,来判断拦截响应 body 和抛出错误

无论你是否需要用 JavaScript 通过 CORS 跨域请求资源,你都要了解 CORS 的原理,最新的浏览器全面支持,在引用外域资源时,除了 JavaScriptCSS 外,都要验证 CORS,例如当你引用了某个第三方 CDN 上的字体文件时

1
2
3
4
@font-face {
font-family: 'FontAwesome';
src: url('../fontawesome.ttf') format('truetype');
}

如果该 CDN 服务商未正确设置 Access-Control-Allow-Origin,那么浏览器无法加载字体资源

非简单请求

对于 PUTDELETE 以及其他类型如 application/jsonPOST 请求,在发送 Ajax 请求之前,浏览器会先发送一个 OPTIONS 请求(带着 OriginAccess-Control-Request-MethodAccess-Control-Request-HeadersCORS 相关的请求头的预检请求)到这个 URL 上,询问目标服务器是否接受

1
2
3
4
OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://my.com
Access-Control-Request-Method: POST

服务器必须响应并明确指出允许的 Method

1
2
3
4
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400

浏览器确认服务器响应的 Access-Control-Allow-Methods 头确实包含将要发送的 Ajax 请求的 Method,才会继续发送 Ajax,否则抛出一个错误(可见下方实例),由于以 POSTPUT 方式传送 JSON 格式的数据在 REST 中很常见,所以要跨域正确处理 POSTPUT 请求,服务器端必须正确响应 OPTIONS 请求,更多关于 CORS 的信息可以查阅 跨域资源共享 CORS 详解

处理跨域的方法

处理跨域的方法有很多,比如之前比较常见的 JSONP,亦或者现在比较常用的 CORS,所以我们就在这里小小的总结了一下解决跨域的相关方法,下面我们就一个一个来看

CORS

也算是目前使用较多的一种方式,针对于普通跨域请求(简单请求),只服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,Origin 表示本域,也就是浏览器当前页面的域

JavaScript 向外域(如 sina.com)发起请求后,浏览器收到响应后,首先检查 Access-Control-Allow-Origin 是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript 将无法获取到响应的任何数据,假设本域是 my.com,外域是 sina.com,只要响应头 Access-Control-Allow-Originhttp://my.com,或者是 *,本次请求就可以成功,可见跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的 Access-Control-Allow-Origin,决定权始终在对方(服务器)手中

不过有一个需要注意的地方,那就是跨域请求默认不会携带 Cookie 信息,如果需要携带,可以采取如下操作

1
'Access-Control-Allow-Credentials': true

或者在请求当中配置

1
http.post(url, data, { withCredentials: true })

如果是非简单请求,前台则需要添加额外的 Headers 来触发非简单请求,比如下面的示例,前台采用 Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var xhr = new XMLHttpRequest()

xhr.withCredentials = true
xhr.open('post', 'http://www.aaa.com', true)

// 添加额外的 Headers 来触发非简单请求
xhr.setRequestHeader('Content-Type', 'application/json')

xhr.send()

xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText)
}
}

后台采用 Node.js

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
var http = require('http')
var server = http.createServer()
var qs = require('querystring')

server.on('request', function (req, res) {
var postData = ''

req.addListener('data', function (chunk) {
postData += chunk
})

req.addListener('end', function () {
postData = qs.parse(postData)

// 跨域后台设置
res.writeHead(200, {
'Access-Control-Allow-Credentials': 'true', // 后端允许发送 Cookie
'Access-Control-Allow-Origin': 'http://www.aaa.com', // 允许访问的域(协议 + 域名 + 端口)
'Set-Cookie': 'x=123;Path=/;Domain=www.aaa.com;HttpOnly' // HttpOnly 的作用是让 JavaScript 无法读取 Cookie
})

res.write(JSON.stringify(postData))
res.end()
})
})

server.listen('8000')

JSONP

JSONPJSON With Padding)是 JSON 的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题,主要原理是借助 <script> 等标签的 src 属性可以请求不同域名下的资源,即 <script> 请求不受浏览器同源策略影响,实现过程主要通过网页客户端动态添加 <script> 标签内的 src 属性,向服务端发送请求(不受同源策略束缚),当服务器收到请求后,将数据放在一个指定名字的回调函数里(作为参数)传回来,前台代码如下示例

1
2
3
4
5
6
7
8
9
10
11
12
var script = document.createElement('script')
script.src = 'http://localhost:3000/jsonp?callback=_callback'

// 插入标签
document.body.appendChild(script)

// 回调处理函数
var _callback = function (obj) {
for (key in obj) {
console.log('key: ' + key + ' value: ' + obj[key])
}
}

上面的 <script> 标签会向本地服务器发送请求,这个请求的后面带了个 callback 参数,是用来告诉服务器回调方法的方法名的,因为服务器收到请求后,会把相应数据写进回调函数的参数位置,后端响应代码如下

1
2
3
4
5
6
7
8
9
app.get('/jsonp', (req, res) => {
let callback = req.query.callback;
let obj = {
type: 'jsonp',
name: 'weapon-x'
};
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(callback + '(' + JSON.stringify(obj) + ')');
})

这样浏览器通过 <script> 下载的资源就是上面的脚本了,当 <script> 下载完成就会立即执行,也就是说这个请求返回后就会立即执行上面的脚本代码,而这个脚本代码就是调用回调方法和拿到 JSON 数据,但是有一个需要注意的地方,JSONP 只支持 GET 请求方式,因为本质上 <script> 加载资源就是 GET,但是如果我们需要发送 POST 请求那该怎么办呢?

iframe + form

如果想要发送 POST 请求,可以采用这种方式,主要原理是利用 iframe 标签的跨域能力,我们先来看看前台代码

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
const requestPost = ({ url, data }) => {
// 首先创建一个用来发送数据的 iframe,并且将其隐藏
const iframe = document.createElement('iframe')
iframe.name = 'iframePost'
iframe.style.display = 'none'
document.body.appendChild(iframe)

// 获取元素
const form = document.createElement('form')
const node = document.createElement('input')

// 注册 iframe 的 load 事件处理程序,如果你需要在响应返回时执行一些操作的话
iframe.onload = function() {
console.log('post success')
}

// 在指定的 iframe 中执行 form
form.action = url
form.target = iframe.name
form.method = 'post'
for (let name in data) {
node.name = name
node.value = data[name].toString()
form.appendChild(node.cloneNode())
}

// 表单元素需要添加到主文档中
form.style.display = 'none'
document.body.appendChild(form)
form.submit()

// 表单提交后,就可以删除这个表单,不影响下次的数据发送
document.body.removeChild(form)
}

// 使用方式
requestPost({
url: 'http://localhost:3000',
data: {
msg: 'hello'
}
})

后台来接收并处理数据

1
2
3
4
5
6
7
8
9
10
11
12
// 处理成功失败返回格式的工具
const { successBody } = require('./utli')

class CrossDomain {
static async iframePost(ctx) {
let postData = ctx.request.body
console.log(postData)
ctx.body = successBody({ postData: postData }, 'success')
}
}

module.exports = CrossDomain

这样一来我们就可以发送 POST 请求了

document.domain + iframe

这种方法有些局限性,仅限主域相同,子域不同的跨域应用场景,实现原理就是让两个页面都通过 JavaScript 强制设置 document.domain 为同一域名,这样一来就实现了同域,这里有两种场景

  1. 第一种场景是在父页面调用内嵌的 iframe 当中的元素,如下

    • 我们的父窗口是 http://www.aaa.com/a.html
    • 子窗口(内嵌的 iframe)是 http://www.aaa.com/b.html

这时候如果想在 a 页面里获取 b 页面里的 DOM 进行操作,就会发现你不能获得 bDOM,比如使用 document.getElementById('myIFrame').contentWindow.document 或者 window.parent.document.body 都获取不到,都将因为两个窗口不同源而报错,在这个时候只需要在 a 页面里和 b 页面里把 document.domain 设置成相同的值就可以在两个页面里操作 DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 父窗口当中内嵌子页面 -->
<iframe id="myIFrame" src="http://www.aaa.com/b.html"></iframe>
<script>
document.domain = 'domain.com'
var user = 'admin'
</script>


<!-- 子页面当中设置相同的 domain -->
<script>
document.domain = 'domain.com'
// 获取父窗口中变量
alert('get js data from parent ==> ' + window.parent.user)
</script>
  1. 第二种场景是共享 Cookie 引起的问题

a 页面里写入了 document.cookie = 'test1=hello',但是在 b 页面当中是获取不到这个 Cookie 的,Cookie 是服务器写入浏览器的一小段信息,只有『同源』的网页才能共享,但是两个网页一级域名相同,只是二级域名不同,在这种情况下浏览器允许通过设置 document.domain 来共享 Cookie

另外,服务器也可以在设置 Cookie 的时候,指定 Cookie 的所属域名为一级域名,这样的话,二级域名或者三级域名不用做任何设置,都可以读取这个 Cookie,但是这里有一些需要注意的地方

  • document.domain 也是有限制的,虽然可读写,但只能设置成自身或者是高一级的父域且主域必须相同,所以只能解决一级域名相同二级域名不同的跨域问题
  • document.domain 只适用于 Cookieiframe 窗口,LocalStorageIndexDB 无法通过这种方法跨域

window.name + iframe

window 对象有个 name 属性,该属性有个特征,即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置,并且可以支持非常长的 name 值(2MB),这里可以分为下面几种情况

  1. 第一种情况是在同一个浏览器标签页里打开了不同域名下的页面

比如先在浏览器的一个标签页里打开了 http://www.aaa.com/a.html 页面,你通过 location.href = http://www.bbb.com/b.html 在同一个浏览器标签页里打开了不同域名下的页面,这时候这两个页面可以使用 window.name 来传递参数,因为 window.name 指的是浏览器窗口的名字,只要浏览器窗口相同,那么无论在哪个网页里访问值都是一样的

  1. 第二种情况和上面的 document.domain + iframe 当中的第一种场景类似,但是不同之处就是两个页面的一级域名也不相同,这时候 document.domain 就解决不了了

这个时候就可以使用 window.name 来解决,比如你在 b 页面里设定 window.name='hello',你再返回到 a 页面,在 a 页面里访问 window.name,可以得到 hello

  1. 第三种情况比较少见,动态创建 iframe,利用 window.name 来传递数据,成功后再切换到同域代理页面,如下,这里分为三个页面
    • 父窗口,http://www.aaa.com/a.html
    • 中间代理页面,http://www.aaa.com/proxy.html,中间代理页,与 a.html 同域,内容为空即可
    • 子窗口(内嵌的 iframe),http://www.bbb.com/b.html(一级域名也不相同)
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
var proxy = function (url, callback) {

// 防止页面无限刷新
var state = 0
var iframe = document.createElement('iframe')

// 加载跨域页面
iframe.src = url

// onload 事件会触发 2 次,第 1 次加载跨域页,并留存数据于 window.name
iframe.onload = function () {
if (state === 0) {
// 第 1 次 onload 成功后(跨域页),切换到同域代理页面(指向当前域),为防止错误,可以设置为空白页面
iframe.contentWindow.location = 'http://www.aaa.com/proxy.html'
state = 1
} else if (state === 1) {
// 第 2 次 onload 成功后(同域的 proxy.html),读取同域 window.name 中数据
callback(iframe.contentWindow.name)
// 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域的 iframe 访问)
iframe.contentWindow.document.write('')
iframe.contentWindow.close()
document.body.removeChild(iframe)
}
}
document.body.appendChild(iframe)
}

// 请求跨域 b 页面数据
proxy('http://www.bbb.com/b.html', function (data) {
alert(data)
})

使用的话,在 b 页面当中直接设置 window.name 即可

1
2
// b 页面
window.name = 'This is page b data!'

通过 iframesrc 属性由外域转向本地域,跨域数据即由 iframewindow.name 从外域传递到本地域,这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作

location.hash

location.hash 就是指 url# 号后面的部分,这种情况一般使用在父窗口和 iframe 的子窗口之间通讯或者是 window.open 打开的子窗口之间的通讯,如果是两个不同域的页面 ab 之间需要相互通信,则需要通过借助中间页 c 来实现,实现原理如下

1
a.html(A 域) ==> b.html(B 域) ==> c.html(A 域)

ab 不同域只能通过 Hash 值单向通信,bc 也不同域也只能单向通信,但 ca 同域,所以 c 可通过 parent.parent 访问 a 页面所有对象,实例如下,三个测试页面如下

  • A 域下的 a.html,地址为 http://www.aaa.com/a.html
  • B 域下的 b.html,地址为 http://www.bbb.com/b.html
  • A 域下的 c.html,地址为 http://www.ccc.com/c.html
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
<!-- A 域下的 a.html(内嵌 B 域下的 b.html) -->
<iframe id="iframe" src="http://www.bbb.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe')

// 向b.html传hash值
setTimeout(function () {
iframe.src = iframe.src + '#user=zhangsan'
}, 1000)

// 开放给同域 c.html 使用的回调方法
function onCallback(res) {
alert('data from c.html ==> ' + res)
}
</script>


<!-- B 域下的 b.html(内嵌 A 域下的 c.html) -->
<iframe id="iframe" src="http://www.aaa.com/c.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe')

// 监听 a.html 传来的 hash 值,再传给 c.html
window.onhashchange = function () {
iframe.src = iframe.src + location.hash
}
</script>


<!-- A 域下的 c.html -->
<script>
// 监听 b.html 传来的 hash 值
window.onhashchange = function () {
// 再通过操作同域 a.html 当中提供的回调函数,将结果传回
window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''))
}
</script>

postMessage

postMessageHTML5 XMLHttpRequest Level 2 中的 API,且是为数不多可以跨域操作的 window 属性之一,它可用于解决以下方面的问题

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的 iframe 消息传递
  • 上面三个场景的跨域数据传递

语法如下

1
window.postMessage(message, targetOrigin, [transfer])

有三个参数

  • data,向目标窗口发送的数据,任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用 JSON.stringify() 序列化
  • origin,协议 + 主机 + 端口号,也可以设置为 *,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为 '/'
  • transfer,可选参数,是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权

另外消息的接收方必须有监听事件,否则发送消息时就会报错,如下所示

1
The target origin provided ('http://localhost:3000') does not match the recipient window's origin ('http://localhost:3001').

接收消息可以直接监听 window 对象的 message 事件即可

1
window.addEventListener('message', callback)

callback 接收到的 message 事件包含三个属性

  • data,从其他 window 中传递过来的数据
  • origin,调用 postMessage 时消息发送方窗口的 origin
    • 需要注意的是,这个 origin 不能保证是该窗口的当前或未来 origin
    • 因为 postMessage 被调用后可能被导航到不同的位置
  • source,对发送消息的窗口对象的引用,可以使用此来在具有不同 origin 的两个窗口之间建立双向通信

简单来说,就是一个页面发送数据,另一个页面接收数据,下面来看一个实例

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
<!-- 父页面(内嵌一个 iframe,内容为页面 b) -->
<iframe id="iframe" src="http://www.bbb.com/index.html" style="display:none"></iframe>
<script>
var iframe = document.getElementById('iframe')

iframe.onload = function () {
var data = {
name: 'zhangsan'
}
// 向 b 页面发送数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.bbb.com')
}

// 接受 b 页面返回的数据
window.addEventListener('message', function (e) {
alert('data from page b ==> ' + e.data)
}, false)
</script>


<!-- 子页面 -->
<script>
// 接收 a 页面的数据
window.addEventListener('message', function (e) {
alert('data from page a ==> ' + e.data)

var data = JSON.parse(e.data)
if (data) {
data.age = 18

// 处理后再发回给 a 页面
window.parent.postMessage(JSON.stringify(data), 'http://www.aaa.com')
}
}, false)
</script>

Nginx 代理

基本原理是我们请求的时候还是使用的前端域名,但是 Nginx 会帮我们把这个请求转发到真正的后端域名上,这样就可以避免跨域问题,Nginx 配置如下

1
2
3
4
5
6
7
8
9
10
11
12
server{
# 监听 3000 端口
listen 3000;

# 域名是 localhost
server_name localhost;

# 凡是类似 localhost:3000/api 这样的请求,都会转发到真正的服务端地址 http://localhost:3001
location ^ ~ /api {
proxy_pass http://localhost:3001;
}
}

在请求的时候,还是跟往常一样正常请求即可

1
2
3
4
5
6
7
8
9
10
11
12
// 请求的时候直接使用 http://localhost:3000
// Nginx 会帮我们进行监听
fetch('http://localhost:3000', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
msg: 'hello'
})
})

Node.js 中间件代理跨域

中间件实现跨域代理,原理大致与 Nginx 相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置 cookieDomainRewrite 参数修改响应头中 Cookie 中域名,实现当前域的 Cookie 写入,方便接口登录认证,利用 node + express + http-proxy-middleware 搭建一个 proxy 服务器

前台代码如下

1
2
3
4
5
6
7
8
var xhr = new XMLHttpRequest()

// 前端开关,浏览器是否读写 Cookie
xhr.withCredentials = true

// 访问 http-proxy-middleware 代理服务器
xhr.open('get', 'http://www.aaa.com/login?user=zhangsan', true)
xhr.send()

中间件服务器代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var express = require('express')
var proxy = require('http-proxy-middleware')
var app = express()

app.use('/', proxy({
// 代理跨域目标接口
target: 'http://www.bbb.com',
changeOrigin: true,

// 修改响应头信息,实现跨域并允许带 Cookie
onProxyRes: function (proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://www.aaa.com')
res.header('Access-Control-Allow-Credentials', 'true')
},

// 修改响应信息中的cookie域名
// 可以为 false,表示不修改
cookieDomainRewrite: 'www.aaa.com'
}))

app.listen(3000)

后台代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var http = require('http')
var server = http.createServer()
var qs = require('querystring')

server.on('request', function (req, res) {
var params = qs.parse(req.url.substring(2))

// 向前台写 Cookie
// HttpOnly 表示脚本无法读取
res.writeHead(200, {
'Set-Cookie': 'x=123;Path=/;Domain=www.bbb.com;HttpOnly'
})

res.write(JSON.stringify(params))
res.end()
})

server.listen('3000')

如果使用的是 Webpack 构建的项目,可以使用 webpack-dev-server 代理接口跨域,在开发环境下,由于渲染服务和接口代理服务都是 webpack-dev-server 同一个,所以页面与代理接口之间不再跨域,无须设置 Headers 跨域信息了,webpack.config.js 部分配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.bbb.com', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些 https 服务报错时用
cookieDomainRewrite: 'www.aaa.com' // 可以为 false,表示不修改
}],
noInfo: true
}
}

WebSocket 协议跨域

WebSocket protocolHTML5 一种新的协议,它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现,原生 WebSocket API 使用起来不太方便,我们可以选择使用 Socket.io,它很好地封装了 WebSocket 接口,提供了更简单、灵活的接口,也对不支持 WebSocket 的浏览器提供了向下兼容

前台代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.bbb.com');

// 连接成功处理
socket.on('connect', function () {
// 监听服务端消息
socket.on('message', function (msg) {
console.log('data from server: ==> ' + msg)
})

// 监听服务端关闭
socket.on('disconnect', function () {
console.log('Server socket has closed.')
})
})

document.getElementsByTagName('input')[0].onblur = function () {
socket.send(this.value)
};
</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
var http = require('http')
var socket = require('socket.io')

// http 服务
var server = http.createServer(function (req, res) {
res.writeHead(200, {
'Content-type': 'text/html'
})
res.end()
})

server.listen('3000')

// 监听 socket 连接
socket.listen(server).on('connection', function (client) {
// 接收信息
client.on('message', function (msg) {
client.send('hello:' + msg)
console.log('data from client: ==> ' + msg)
})

// 断开处理
client.on('disconnect', function () {
console.log('Client socket has closed.')
})
})

总结

处理跨域的方法有许多种,现在比较流行的还是使用 CORS 的方式,也就是服务端来进行设置,从而一劳永逸,不过多了解一些其他的方式也是不错的,还是那句老话,根据实际使用场景来进行选择

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×