什么是跨域问题?
概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| URL 说明 是否允许通信 http://www.a.com/a.js http://www.a.com/b.js 同一域名下 允许 ***** http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一域名下不同文件夹 允许 ***** http://www.a.com:8000/a.js http://www.a.com/b.js 同一域名,不同端口 不允许 ***** http://www.a.com/a.js https://www.a.com/b.js 同一域名,不同协议 不允许 ***** http://www.a.com/a.js http://70.32.92.74/b.js 域名和域名对应ip 不允许 ***** http://www.a.com/a.js http://script.a.com/b.js 主域相同,子域不同 不允许 ***** http://www.a.com/a.js http://a.com/b.js 同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问) ***** http://www.cnblogs.com/a.js http://www.a.com/b.js 不同域名 不允许
|
特别需要注意的是:
第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
解决跨域的几种方案
1.服务器 proxy
域A的页面JS需要访问域B下的链接获取数据,由于服务器端不存在跨域问题,该方案即在域A的服务器端建立一个Proxy程序(可能是ASP、servlet等任何服务端程序),域A的页面JS直接调用本域下的 Proxy程序,proxy程序负责将请求发送给域B下的链接并获取到数据,最后再通过Proxy将数据返回给页面JS使用。
经过的访问流程就是: 域A下JS —> 域A 下Proxy — > 域B下的链接
2.通过jsonp跨域
要解释JSONP的来由,先要说一下浏览器的“同源策略(SOP:Same Origin Policy)”。 简而言之,就是浏览器限制脚本程序只能和同协议、同域名、同端口的脚本进行交互,这包括共享和传递变量等。cookie的传递也是遵从同样策略。这就造成一些涉及到多个服务器的应用在整合时一些麻烦。跨域访问的问题造成A站点的Ajax代码无法访问B站点的数据。
如何解决跨域访问呢?那就要借助浏览器的一个特性:尽管浏览器不允许页面中的脚本程序跨域读取数据,但却允许HTML引用跨域的资源,如图片,CSS和脚本程序。对于脚本程序的引用比较特殊,它被浏览器解析以后,就和本地的脚本程序别无二致且可立即进行解释并执行。如在B站点的一个js文件,一个简单的提示框:alert(“This is Victor!”);。在A站点引用这个js,这个脚本就会在A站点的应用中执行,显示一个alert信息。由于站外脚本的引用是通过script标签来实现的,而脚本程序又可通过DOM的方式可以对HTML页面的所有标签进行控制(包括动态的创建script标签),这就可以实现通过调用站外程序对本地资源进行更改了。另外,通过script标签的使用,就可从服务端直接返回可执行的JavaScript函数调用或者JSON数据。
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。因此我们可以通过动态创建script标签,js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。例如:
客户端js代码:
1 2 3 4 5 6
| <script type="text/javascript"> function dosomething(jsondata){ //处理获得的json数据 } </script> <script src="http://example.com/data.php?callback=dosomething"></script>
|
客户端jquery代码
1 2 3 4 5
| <script type="text/javascript"> $.getJSON('http://example.com/data.php?callback=?,function(jsondata)'){ //处理获得的json数据 }); </script>
|
jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
服务器端php代码
1 2 3 4 5
| <?php $callback = $_GET['callback'];//得到回调函数名 $data = array('a','b','c');//要返回的数据 echo $callback.'('.json_encode($data).')';//输出 ?>
|
最终,输出结果为:dosomething([‘a’,’b’,’c’]);
服务器端jsp代码
1 2 3 4 5 6 7 8 9 10
| <%@ page contentType="text/html;charset=UTF-8" language="java"%> <%@page import="java.io.PrintWriter"%> <% //服务器端接到回调函数名字输出回调函数,客户端根据回调函数进行解析取得函数中json对象 response.setContentType("text/html; charset=utf-8"); String callback=request.getParameter("callback"); PrintWriter ss = response.getWriter(); ss.print(callback+"([ { name:\"跨域访问成功!\"},{ name:\"跨域访问失败!\"}])"); %>
|
这里列举的两种方案各有优缺点:
Proxy方案优点是可以适用用于几乎所有的跨域访问,而且只需要要一个域中进行开发,另一个域可以提供任何类型格式的数据。缺点是这种方案经过了中间Proxy,所以延迟可能稍微大一点,并且会加重本域服务器的负荷,开发工作量也稍微大一点。
jsonp它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
补充
ajax整个请求过程
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 52 53 54 55 56 57 58
| //打开浏览器 /* 1.创建一个ajax对象 ie6以下new ActiveXObject('Microsoft.XMLHTTP') */ var xhr = null; /*if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); }*/ try { xhr = new XMLHttpRequest(); } catch (e) { xhr = new ActiveXObject('Microsoft.XMLHTTP'); }
//alert( xhr.readyState ); //在地址栏输入地址 /* open方法 参数 1.打开方式 2.地址 3.是否异步 异步:非阻塞 前面的代码不会影响后面代码的执行 同步:阻塞 前面的代码会影响后面代码的执行 */ xhr.open('get','1.txt',true); //提交 发送请求 //alert(1); xhr.send(); //alert( xhr.readyState );
//alert(1)
//alert( xhr.responseText );
//等待服务器返回内容 /* readyState : ajax工作状态 responseText : ajax请求返回的内容就被存放到这个属性下面 on readystate change : 当readyState改变的时候触发 status : 服务器状态,http状态码 */ xhr.onreadystatechange = function() { if ( xhr.readyState == 4 ) { if ( xhr.status == 200 ) { alert( xhr.responseText ); } else { alert('出错了,Err:' + xhr.status); } } }
|
其中需要注意的是:
1 2 3 4 5 6 7 8 9
| xhr.open('get','2.get.php?username='+encodeURI('刘伟')+'&age=30&' + new Date().getTime(),true); //1.缓存 在url?后面连接一个随机数,时间戳2.乱码 编码encodeURI
xhr.open('post','2.post.php',true); //post方式,数据放在send()里面作为参数传递 xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded');//申明发送的数据类型 //post没有缓存问题 //无需编码 xhr.send('username=刘伟&age=30');
|