跨域

跨域

1、JSONP实现

优缺点

JSONP实现方式

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入<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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>

<script>
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}

window.onload = function () {
// addScriptTag('http://example.com/ip?callback=foo');
addScriptTag('http://localhost:3000/user?callback=foo');
}

function foo(data) {
console.log('xxxxx',data)
}
</script>
<!--<script src="http://localhost:3000/user?callback=foo"></script>-->

</html>

上面代码通过动态添加<script>元素,向服务器localhost:3000发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

服务器端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const http = require('http')
const url = require('url')
http.createServer((req, res) => {
const data = {name: 'uthus'}
// 获取查询参数callback,就是我们在客户端定义的jsonpCb
const callback = url.parse(req.url, true).query.callback
if (callback) {
const str = callback + '(' + JSON.stringify(data) + ')'
// 以字符串的形式返回
res.end(str)
} else {
res.end('hello world')
}
}).listen(3000)
console.log('xxxxxxxxxxx000', 'listen at 3000');

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

1
foo({name: 'uthus'});

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

基于Promise,封装一个jsonp工具

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
49
50
51
/**
* 获取随机字符串,用于拼接
* @param {string} prefix [前导名字]
* @param {number} num [字符串长度]
*/
function getRandomName (prefix, num) {
return prefix + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, num)
}
/**
* 创建script标签
* @param {请求路径} url
*/
function createScript (url) {
const script = document.createElement('script')
script.setAttribute('type', 'text/javascript')
script.setAttribute('src', url)
script.async = true
return script
}
/**
* 实现请求
* @param {路径} url
*/
function jsonp (url) {
return new Promise((resolve, reject) => {
const cbName = getRandomName('callback')
window[cbName] = function (data) {
resolve(data)
}
url += url.indexOf('?') > -1 ? '&' : '?'
const script = createScript(`${url}callback=${cbName}`)
script.onload = function () {
script.onload = null
if (script.parentNode) {
script.parentNode.removeChild(script)
}
window[cbName] = null
}
script.onerror = function () {
reject()
}
document.getElementsByTagName('head')[0].appendChild(script)
})
}

// 调用
jsonp('http://localhost:3000').then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})

2、通过CORS 设置Access-Control-Allow-Origin为’*’

3、Nginx反向代理

参考文章

[‘https://segmentfault.com/a/1190000015597029#articleHeader5']

浏览器同源政策及其规避方法[‘http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html']

跨域资源共享 CORS 详解[‘http://www.ruanyifeng.com/blog/2016/04/cors.html']

[‘https://segmentfault.com/a/1190000000718840']