如何解决跨域

2020-04-16  本文已影响0人  鱼香肉丝没有渔

什么是JSONP

首先提一下 JSON 这个概念,JSON 是一种轻量级的数据传输格式,被广泛应用于当前 Web 应用中。JSON 格式数据的编码和解析基本在所有主流语言中都被实现,所以现在大部分前后端分离的架构都以 JSON 格式进行数据的传输。

JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。

首先抛出浏览器同源策略这个概念,为了保证用户访问的安全,现代浏览器使用了同源策略,即不允许访问非同源的页面,详细的概念大家可以自行百度。这里大家只要知道,在ajax中,不允许请求非同源的URL就可以了,比如 www.a.com 下的一个页面,其中的ajax请求是不允许访问 www.b.com 中任何一个页面的。

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

8、ajax 的核心是通过 XmlHttpRequest 获取非本页内容,而 jsonp 的核心则是动态添加 <script> 标签来调用服务器提供的 js 脚本。

9、jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。

总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变!

JSONP原理

ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。

JSONP具体实现
1、首先看下ajax中如果进行跨域请求会如何。

前端代码在域 localhost 下面,使用 ajax 发送了一个跨域到 local.sns.jutouit.com 的 get 请求

<!DOCTYPE html>
<html>
<head>
    <title>GoJSONP</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<script type="text/javascript">
    function jsonhandle(data){
        alert("age:" + data.age + "  name:" + data.name + "  sex:"+data.sex);
    }
</script>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $.ajax({
            type : "get",
            async: false,
            url : "http://local.sns.jutouit.com/radius.php?id=1",
            type: "json",
            success : function(data) {
                jsonhandle(data);
            }
        });
    });
</script>
</body>
</html>

当访问前端代码 http://localhost/jsonp/demo.html 时 chrome报以下错误

image

意思就是无法请求到 local.sns.jutouit.com 的资源,原因就是禁止了跨域请求资源。

2、下面使用 JSONP,将前端代码中的 ajax 请求去掉,添加了一个script标签,标签的 src 指向了另一个域 local.sns.jutouit.com 下的 remote.js 脚本

<!DOCTYPE html>
<html>
<head>
    <title>GoJSONP</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<script type="text/javascript">
    function jsonhandle(data){
        alert("age:" + data.age + "  name:" + data.name + "  sex:"+data.sex);
    }
</script>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
</script>
<script type="text/javascript" src="http://local.sns.jutouit.com/remote.js"></script>
</body>
</html>

这里调用了跨域的 remote.js 脚本,remote.js 代码如下:

jsonhandle({
    "age" : 25,
    "name": "Yibin",
    "sex": "男"
});

这段远程的 js 代码执行了上面定义的函数,弹出了提示框如下:

image

3、这样就简单地解决了跨域获取数据的问题,但是 js 写死了,不够灵活,以下换成动态生成 html 元素并动态设置 js 的 src 属性等。

<!DOCTYPE html>
<html>
<head>
    <title>GoJSONP</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<script type="text/javascript">
    function jsonhandle(data){
        alert("age:" + data.age + "  name:" + data.name + "  sex:"+data.sex);
    }
</script>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        var url = "http://local.sns.jutouit.com/test.php?id=1&callback=jsonhandle";
        var obj = $('<script><\/script>');
        obj.attr("src",url);
        $("body").append(obj);
    });
</script>

test.php 代码如下(更灵活的方法可以通过定义方法来获取指定数据,下面数据是写死的,测试方便而已。):

<?php
$data = array(
    'age' => 24,
    'name' => '小华',
    'sex' => '女'
);
$callback = $_GET['callback'];
echo $callback."(".json_encode($data).")";
return;

意思同上一步是一样的,等价于 jsonhandle(array(...)); 也就是方法的定义。供前端页面调用。执行结果如下:

image

4、jQuery 提供了方便使用 JSONP 的方式,虽然类似 ajax 的请求,但是本质是不一样的,且只能使用 get 请求,代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>GoJSONP</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $.ajax({
            type : "get",
            async: false,
            url : "http://local.sns.jutouit.com/test.php?id=1",
            dataType: "jsonp",
            jsonp:"callback", //请求php的参数名
            jsonpCallback: "jsonhandle",//要执行的回调函数(参数值)
            success : function(data) {
                alert("age:" + data.age + "  name:" + data.name + "  sex:"+data.sex);
            }
        });
    }); 
</script>
</body>
</html>

test.php 代码跟上面的一样。效果图如下:

image

使用CORS(跨资源共享)


CORS.1.png

在后端数据中加入


image.png
上一篇下一篇

猜你喜欢

热点阅读