jquery ajax使用JSONP解决跨域问题
参考文章:http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
什么是跨域
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。这里把涉及到跨域的一些问题简单地整理一下:
首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:
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上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
接下来简单地总结一下在“前台”一般处理跨域的办法,后台proxy这种方案牵涉到后台配置,这里就不阐述了,有兴趣的可以看看yahoo的这篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls》
一、WebApi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 为测试JSONP跨域而写 [HttpGet] public void GetAll( string callback) { List<User> retList = new List<User>(); Context context = new Context(); var users = context.Users.ToList(); if (users != null && users.Count > 0) { retList = users; } context.Dispose(); //return "callback(" + JsonConvert.SerializeObject(retList) + ")"; HttpContext.Current.Response.Write(callback + "(" + JsonConvert.SerializeObject(retList) + ")" ); HttpContext.Current.Response.End(); } |
以项目A中的一个WebApi方法作为实验中的接口,接口地址http://localhost:616/api/UserService/GetAll,访问方式为GET
二、测试页面:
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
|
< html > < head > < title >test</ title > < script src="~/Scripts/jquery-1.7.1.min.js"></ script > < script type="text/javascript"> function callAPI() { $.ajax({ type: "GET", url: "http://localhost:616/api/UserService/GetAll", dataType: "application/json;charset=utf-8", success: function (result) { alert(result); }, error: function (e) { var test = e; } }); } function callAPIByJSONP() { var test = 1; $.ajax({ type: "GET", async: false, url: "http://localhost:616/api/UserService/GetAll", dataType: "jsonp", jsonp: "callback", jsonpCallback:"handler", success: function (result) { alert(result[0].UserName); }, error: function (e) { var test = e; } }); } </ script > </ head > < body > < input type="button" value="普通调用" onclick="callAPI();" /> < input type="button" value="JSONP调用" onclick="callAPIByJSONP();" /> </ body > </ html > |
测试页面只包括两个按钮,一个是普通的ajax调用WebApi接口,另一个是用JSONP调用WebApi接口。
当使用一般的Ajax调用时会提示下面信息:
No Access-Control-Allow-Orgin,此时不能通过ajax获取其他服务器上的数据。
三、使用JSONP跨域:
JSONP的大致原理是前台请求跨域服务器上的脚本时是不会受跨域的影响的,比如:<script type=text/javascript src='www.abc.com' />。因此只需要跨域服务根据前端的需求动态生成js,这个js包括了执行的方法以及数据,这样就实现了类似于ajax的效果。不过JSONP和ajax有着本质的区别:ajax的核心是通过XmlHttpRequest获取非本页面的内容,而JSONP的核心是动态添加<script>标签来调用服务器动态生成的js脚本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function callAPIByJSONP() { var test = 1; $.ajax({ type: "GET" , async: false , url: "http://localhost:616/api/UserService/GetAll" , dataType: "jsonp" , jsonp: "callback" , jsonpCallback: "handler" , success: function (result) { alert(result[0].UserName); }, error: function (e) { var test = e; } }); } |
注意点:
刚开始后台方法返回值为string类型,返回值为:return callback(回调方法名)+(数据),这样返回的Code为200,statusText为success,但是不会进入到success回调函数里,而是进入error中。
后来在网上查找原因,很多回答都是说返回的数据格式有问题,但是按照他们提供的格式和上面我写的是一样的。最后修改后台方法,使用HttpContext.Current.Response.Write的方式返回数据,问题解决。
// 为测试JSONP跨域而写 [HttpGet] public void GetAll(string callback) { List<User> retList = new List<User>(); Context context = new Context(); var users = context.Users.ToList(); if (users != null && users.Count > 0) { retList = users; } context.Dispose(); //return "callback(" + JsonConvert.SerializeObject(retList) + ")"; HttpContext.Current.Response.Write(callback + "(" + JsonConvert.SerializeObject(retList) + ")"); HttpContext.Current.Response.End(); }
前台获取的数据:
另外,由于JSONP的原理是动态添加<script>,因此JSONP的请求方式是GET,即使指定了type:"POST"最后JSONP向后台服务请求的方式还是GET得方式