什么是跨域问题?
概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24URL 说明 是否允许通信
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
9xhr.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');