为什么要跨域
我们都知道在浏览器地址栏输入地址的时候可以随便访问一个页面,但是如果你在
ajax
请求中发出一个xhr
请求那么因为浏览器安全策略只有同源的服务器才能处理。这就是同源策略 要求协议/域名/端口三者完全一致才能访问
-
例如以下的代码例子,我们需要获取服务器上README.md里面的数据
{ status:'sucess', data:{ city:['南京','北京'] } }
本地js代码如下
<script type="text/javascript">
let xhr = new XMLHttpRequest()
const url = 'http://localhost:8888/detail'
const METHOD = 'GET'
xhr.open(METHOD, url)
xhr.setRequestHeader('Content-Type', 'text/html')
xhr.send()
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('xhr text:', xhr.responseText)
}
}
</script>
如果地址栏是下面这样
那么我们是可以访问到这个数据文件的
如果我在另外一个不同源域名下执行同样的代码例如下面这个地址
那么就会出现下面的错误
这个错误的意思说白了就是服务器禁止不同源的页面进行ajax请求数据。
非要获取这个数据我们应该怎么做
我们注意到有的网站会提供cdn服务我们可以从不同的服务器添加下面的代码<script src="http://www.baidu.com"></script>
这就为我们跨域获取数据提供了可能
node服务器端代码如下
var path = require('path')
var express = require('express')
const app = express()
app.use(express.static(path.join(__dirname, 'jsonp')))
console.log('> Starting dev server...')
app.get('/detail', (req, res) => {
let cb = req.query.cb ? req.query.cb : null
// console.log('succuess')
let readStream = fs.createReadStream(`README.md`)
let data = ''
readStream.on('data', chunk => {
data += chunk
})
readStream.on('end', () => {
// console.log(data)
res.send(`${cb}(${data})`)
})
})
var server = app.listen(8888)
console.log('server is running at port 8888')
1.基本原理我们可以动态生成script标签,把请求的url添加到script标签的src属性上。把数据包裹在一个客户端声明的本地的回调函数中,这样可以动态加载一个跨域服务器数据。
function jsonp(obj) {
let scriptDOM = document.createElement('script')
scriptDOM.setAttribute('src', `${obj.url}?cb=${obj.cb}`)
window['callback'] = obj.success
document.querySelector('body').appendChild(scriptDOM)
scriptDOM.onload = function() {
window['callback'] = null
this.parentNode.removeChild(this)
}
scriptDOM.onerror = e => {
console.log('jsonp error!', e)
}
}
jsonp({
url: 'http://localhost:8888/detail',
cb: 'callback',
success: data => {
if (data) {
console.log('jsonp data', data)
}
}
})
2.在本地声明这个动态script中的回调函数名称,并且定义该函数,然后通过url参数传递到服务器。服务器把数据包裹成这个函数的参数传递回来,跨域就实现了。
为什么要这么做?以前我也很困惑。你获取到的数据有可能是个json也有可能是个纯文本,而script标签内的内容只能是合法的js语句,如果你没有用回调函数包裹,直接从服务器传递回来原生数据。有可能是一段恶意代码,或者是非法的js字符,那么浏览器就会报错或者产生不可预料的显示结果。并且你事先不知道浏览器传递过来的代码跟你写的有没有冲突,是不是完全耦合。只有在本地定义一个函数传递到服务器,这样服务器用该函数包裹代码后传递回来才能跟我们的本地js代码紧密配合。这样服务器传递回来执行的代码就是我们自己定义的函数(数据存在函数的参数里),可以很好跟我们自己的代码配合。这样颇有Vue Angular子组件向父组件传递数据的思想
这样不同域名下的数据我们拿到了结果如下
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。