跨域,前端面试必问题之一,jsonP基本上也是最基础的回答之一。jsonP是怎么玩的呢?
因为同源策略的原因,不同的域之间是不能共享数据的,例如cookie等信息。
何为同源?
即协议(http),域名(www.xxx.com),端口(80)一致。
htttp://www.xxx.com
htttp://www.xxx.com/index.html (同源)
htttps://www.xxx.com/index.html (不同源,协议不同)
htttp://xxx.com/index.html (不同源,域名不同)
htttp://www.xxx.com:8080/index.html (不同源,端口不同)
jsonP作为前端最为常见的跨域方案是怎么玩的呢?
首先我们可以简单的想一想在HTML中有哪些元素天生就是可以跨域的:
1,<img>
2,<iframe>
3,<link>
4,<script>
没错,这些标签都是可以引入外部资源的,他们统一的特点就是拥有src/href属性,其实是这些属性在跨域,而不是标签本身在跨域,看到这里可能你也想到了一些CSS的样式属性,比如background-image属性和border-image属性(关于这种CSS属性网上还能找到一些利用跨域它们的跨域能力实现CSS攻击的文章),也就是说凡是可以引入外部资源的属性或者标签都是可以实现跨域的。
我们在www.aaa.com中如果想要请求www.bbb.com中的数据,正常情况下ajax请求都是失败的,但是www.aaa.com中却可以加载www.bbb.com中的JS文件,这样,我们的跨域请求就能得以实现了。
jsonP跨域其实也就是利用<script>标签中的src属性在玩耍。
具体实现:
首先我们需要两个<script>
第一个<script>
<script>
function getData( data ){ // 定义一个获取数据的函数
console.log( data );
}
</script>
第二个<script>
想bbb网站发送数据请求,并且在?号后传值callback
<script src="http://www.bbb.com/index.php?callback=getData"></script>
这时,如果bbb网站的后台是有处理这个callback的话可以能会是这样的(php已经忘记怎么写了,写出大致思路)
$callback = $_GET['callback']; // 得到前台传来的回调函数
if( $callback ){ // 假设callback有值的话
echo $callback( { 'data':'hello' } ); // 将数据塞到函数内部,输出出去
}
这时我们写的第一个<script>标签内部的getData()函数就已经接受到了后台传的数据了。
这样我们的jsonP跨域请求已经完成。
jsonP的优缺点:
优点:
显而易见的简单方便,也不会有类似于浏览器兼容不了的毛病,直接响应数据,快速实现双向通信。
缺点:
1, jsonP是从其他域中加载代码执行,这时我们对外域的安全需要有一定的认识,如果它们不安全导致的问题可能会很严重,所以这时我们除了放弃jsonP没有其他办法。。。
2,它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
知道了这些,我们可以玩一个模拟百度搜索的小案例试试水。
想要在外部调用百度的搜索功能我们需要知道百度放出来的接口是怎么用的。
具体如下:
https://www.baidu.com/su?wd=关键字&cb=回调函数名
<script src="https://www.baidu.com/su?wd=jsonP&cb=getData"></script>
HTML代码:
<input type="text" id="txt" list="datalist">
<datalist id="datalist"></datalist>
JS代码:
// 当数据返回时的回调函数
function getData(data){
var datalist = document.getElementById('datalist');
console.log(data);
for( var i=0;i<data.s.length;i++ ){
var option = document.createElement('option');
option.value = data.s[i];
option.innerText = data.s[i];
datalist.appendChild(option);
}
}
// 当键盘每一次被按下的时候都会新建一个<script>标签插入body中
document.getElementById('txt').onkeydown = function() {
var script = document.createElement('script');
script.src = 'http://www.baidu.com/su?wd='+document.getElementById('txt').value+'&cb=getData';
document.body.appendChild(script);
};