JavaScript
跨域问题解决方案
写在开头的:
JSONP
是使用
JSON
格式
+
动态
script
,在客户端解决跨域访问问题的一种方案。
【最后详细解释了
JSONP
的实现过程】
跨域问题背景:
1.
跨域访问,简单来说就是
A
网站的
javascript
代码试图访问
B
网站,包括提交内容和获取内容;比如想从
A
网站的页面中执行另外一个
B
网站内某页面中的
JS
对象、或者想在
A
网站的页面中用
JS
去解析
B
网站内某页面的
dom
元素等;出现这种跨域访问问题的应用场景一般是
iframe
中嵌入不同域的页面、或者向不同域发送
Ajax
请求等;
2.
同源策略:由于安全原因,跨域访问是被各大浏览器所默认禁止的;但是浏览器并不禁止在页面中引用其他域的
JS
文件,并可以自由执行引入的
JS
文件中的
function
(包括操作
cookie
、
Dom
等等)。根据这一点,可以方便地通过创建
script
节点的方法来实现完全跨域的通信;【至关重要!
JSONP
的原理关键】
3.
是否跨域的判断规则为对三者进行比较:域名、协议、端口;三者中若有一个不相同,则会出现跨域问题;我们经常说的跨域问题一般指域名不同,因为这种场景出现的几率最高而且有一些办法可以解决;比如前面提到的
taobao.com
域下的二级域名跨域问题;
4.
如果是协议和端口造成的跨域问题,想从
Web
端来解决是完全不可能的,只能通过服务端
Proxy
的方案来解决;
但是有时候我们希望能够做一些合理的跨域访问的事情,那么怎么办呢?
理论依据:
依据一:
在浏览器中不能直接来跨域访问,而在服务器端没有跨域安全限制。这样的话,可以在服务端完成跨域访问,而在客户端来取得结果就可以了。
依据二:
同源策略不阻止动态脚本元素插入,脚本访问可以跨域。
Web
页面上调用
js
文件时则不受是否跨域的影响(不仅如此,凡是拥有
"src"
这个属性的标签都拥有跨域的能力,比如
<script>
、
<img>
、
<iframe>
);
解决它们之间跨域的方案:
方案
1
:
document.domain+
隐藏的
iframe
注意:
对于主域相同而子域不同的例子,才可以通过设置
document.domain
的办法来解决。具体的做法是可以在
http://www.a.com/a.html
和
http://script.a.com/b.html
两个文件中分别加上
document.domain
= ‘a.com’
;然后通过
a.html
文件中创建一个
iframe
,去控制
iframe
的
contentDocument
,这样两个
js
文件之间就可以
“
交互
”
了。当然这种办法只能解决主域相同而二级域名不同的情况。
Example
:
www.a.com
上的
a.html
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc =
ifr.contentDocument || ifr.contentWindow.document;
//
在这里操纵
b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
script.a.com
上的
b.html
document.domain = 'a.com';
方案
2
:利用
iframe+location.hash
这个办法比较绕,但是可以解决完全跨域情况下的脚步置换问题。
原理:
利用
location.hash
来进行传值。在
url
:
http://a.com#helloword
中的
‘#helloworld’
就是
location.hash
,改变
hash
并不会导致页面刷新,所以可以利用
hash
值来进行数据传递,当然数据容量是有限的。
思路:
假设域名
a.com
下的文件
cs1.html
要和
cnblogs.com
域名下的
cs2.html
传递信息,
cs1.html
首先创建自动创建一个隐藏的
iframe
,
iframe
的
src
指向
cnblogs.com
域名下的
cs2.html
页面,这时的
hash
值可以做参数传递用。
cs2.html
响应请求后再将通过修改
cs1.html
的
hash
值来传递数据(由于两个页面不在同一个域下
IE
、
Chrome
不允许修改
parent.location.hash
的值,所以要借助于
a.com
域名下的一个代理
iframe
;
Firefox
可以修改)。同时在
cs1.html
上加一个定时器,隔一段时间来判断
location.hash
的值有没有变化,一点有变化则获取获取
hash
值。
Example
:
先是
a.com
下的文件
cs1.html
文件:
function startRequest(){
var ifr =
document.createElement('iframe');
ifr.style.display =
'none';
ifr.src =
'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data =
location.hash ? location.hash.substring(1) : '';
if (console.log)
{
console.log('Now the data is '+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000);
cnblogs.com
域名下的
cs2.html:
//
模拟一个简单的参数处理操作
switch(location.hash){
case '#paramdo':
callBack();
break;
case '#paramset':
//do
something……
break;
}
function callBack(){
try {
parent.location.hash = 'somedata';
} catch (e) {
// ie
、
chrome
的安全机制无法修改
parent.location.hash
,
//
所以要利用一个中间的
cnblogs
域下的代理
iframe
var ifrproxy =
document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src =
'http://a.com/test/cscript/cs3.html#somedata';
//
注意该文件在
"a.com"
域下
document.body.appendChild(ifrproxy);
}
}
a.com
下的域名
cs3.html
//
因为
parent.parent
和自身属于同一个域,所以可以改变其
location.hash
的值
parent.parent.location.hash =
self.location.hash.substring(1);
当然这样做也存在很多缺点,诸如数据直接暴露在了
url
中,数据容量和类型都有限等
……
方案
3
:服务器
Proxy
域
A
的页面
JS
需要访问域
B
下的链接获取数据,该方案在域
A
的服务器端建立一个
Proxy
程序
(
可能是
ASP
、
servlet
等任何服务端程序
)
,域
A
的页面
JS
直接调用本域下的
Proxy
程序,
proxy
程序负责将请求发送给域
B
下的链接并获取到数据,最后再通过
Proxy
将数据返回给页面
JS
使用。
经过的访问流程就是:
域
A
下
JS -->
域
A
下
Proxy -- >
域
B
下的链接。
方案
4
:通过
Script
标签
:
对于浏览器来说,
script
标签的
src
属性所指向资源就跟
img
标签的
src
属性所指向的资源一样,都是一个静态资源,浏览器会在适当的时候自动去加载这些资源,而不会出现所谓的跨域问题。
这样我们就可以通过该属性将要访问的数据对象引用进当前页面而绕过
js
跨域问题。通过
script
标签远程引用其他域名下的脚本文件的。
而且,
script
标签的
src
属性不一定必须是一个存在的
js
文件,也可以是一个
http handler
的
url
,只要这个
http handler
返回的是一个
text/javascript
类型的响应就可以了。
JSONP
就是基于这种原理产生的:
1、
JSON
是
js
原生支持的数据格式;
2、
客户端在对
JSON
文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像
AJAX
;
3、
为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作
JSONP
,该协议的一个要点就是允许用户传递一个
callback
参数给服务端,然后服务端返回数据时会将这个
callback
参数作为函数名来包裹住
JSON
数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
JSONP
的具体实现:
不管
jQuery
也好,
extjs
也罢,又或者是其他支持
jsonp
的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下
jsonp
在客户端的实现:
1
、我们知道,哪怕跨域
js
文件中的代码(当然指符合
web
脚本安全策略的),
web
页面也是可以无条件执行的。
远程服务器
remoteserver.com
根目录下有个
remote.js
文件代码如下:
alert('
我是远程文件
');
本地服务器
localserver.com
下有个
jsonp.html
页面代码如下:
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml">
<
head
>
<
title
></
title
>
<
script
type
="text/javascript"
src
="http://remoteserver.com/remote.js"></
script
>
</
head
>
<
body
>
</
body
>
</
html
>
页面将会弹出一个提示窗体,显示跨域调用成功。
2
、现在我们在
jsonp.html
页面定义一个函数,然后在远程
remote.js
中传入数据进行调用。
jsonp.html
页面代码如下:
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml">
<
head
>
<
title
></
title
>
<
script
type
="text/javascript">
var
localHandler =
function
(data){
alert('
我是本地函数,可以被跨域的
remote.js
文件调用,远程
js
带来的数据是:
'
+
data.result);
};
</
script
>
<
script
type
="text/javascript"
src
="http://remoteserver.com/remote.js"></
script
>
</
head
>
<
body
>
</
body
>
</
html
>
remote.js
文件代码如下:
localHandler({"result":"
我是远程
js
带来的数据
"});
运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程
js
调用成功,并且还接收到了远程
js
带来的数据。跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程
js
知道它应该调用的本地函数叫什么名字呢?毕竟是
jsonp
的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?
3
、聪明的开发者很容易想到,只要服务端提供的
js
脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端
“
我想要一段调用
XXX
函数的
js
代码,请你返回给我
”
,于是服务器就可以按照客户端的需求来生成
js
脚本并响应了。
看
jsonp.html
页面的代码:
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml">
<
head
>
<
title
></
title
>
<
script
type
="text/javascript">
//
得到航班信息查询结果后的回调函数
var
flightHandler =
function
(data){
alert('
你查询的航班结果是:票价
'
+
data.price +
'
元,
'
+
'
余票
'
+
data.tickets +
'
张。
');
};
//
提供
jsonp
服务的
url
地址(不管是什么类型的地址,最终生成的返回值都是一段
javascript
代码)
var
url =
"http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
//
创建
script
标签,设置其属性
var
script = document.createElement('script');
script.setAttribute('src', url);
//
把
script
标签加入
head
,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</
script
>
</
head
>
<
body
>
</
body
>
</
html
>
不再直接把远程
js
文件写死,而是编码实现动态查询,而这正是
jsonp
客户端实现的核心部分,本例中的重点也就在于如何完成
jsonp
调用的全过程。
我们看到调用的
url
中传递了一个
code
参数,告诉服务器我要查的是
CA1998
次航班的信息,而
callback
参数则告诉服务器,我的本地回调函数叫做
flightHandler
,所以请把查询结果传入这个函数中进行调用。
OK
,服务器很聪明,这个叫做
flightResult.aspx
的页面生成了一段这样的代码提供给
jsonp.html
(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):
flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});
我们看到,传递给
flightHandler
函数的是一个
json
,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,
jsonp
的执行全过程顺利完成!
4
、到这里为止的话,相信你已经能够理解
jsonp
的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。
jQuery
封装了
jsonp
调用代码(我们依然沿用上面那个航班信息查询的例子,假定返回
jsonp
结果不变):
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
>
<
title
>
Untitled
Page</
title
>
<
script
type
="text/javascript"
src
=jquery.min.js"></
script
>
<
script
type
="text/javascript">
jQuery(document).ready(
function
(){
$.ajax({
type: "get",
async:
false
,
url:
"http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
dataType: "jsonp",
jsonp: "callback",
//
传递给请求处理程序或页面的,用以获得
jsonp
回调函数名的参数名
(
一般默认为
:callback)
jsonpCallback:"flightHandler",
//
自定义的
jsonp
回调函数名称,默认为
jQuery
自动生成的随机函数名,也可以写
"?"
,
jQuery
会自动为你处理数据
success:
function
(json){
alert('
您查询到航班信息:票价:
' + json.price + '
元,余票:
' + json.tickets +
'
张。
');
},
error:
function
(){
alert('fail');
}
});
});
</
script
>
</
head
>
<
body
>
</
body
>
</
html
>
是不是有点奇怪?为什么我这次没有写
flightHandler
这个函数呢?而且竟然也运行成功了!这就是
jQuery
的功劳了,
jquery
在处理
jsonp
类型的
ajax
时(还是忍不住吐槽,虽然
jquery
也把
jsonp
归入了
ajax
,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供
success
属性方法来调用。
分享到:
相关推荐
Javascript跨域访问解决方案 个人在网上搜集的资料,用于传输信息,不提倡下载
ssm跨域问题解决方案
Ajax跨域问题及其解决方案.docx
该文档介绍了vue和普通web页面中iframe实现跨域的解决方案,解决了主页面中无法调用iframe方法的问题
js跨域问题解决方案.
解决tomcat在IP 和端口不同时引起的跨域问题,解决方案,通过编写crossDomain.xml文件
file协议导致的跨域问题以及解决方案.docx
js跨域访问解决方案总结,不同域名下cookie相互操作
JQuery跨域访问解决方案 JQuery
js跨域解决方案
解决ajax跨域Java解决方案,通过代理实现ajax跨域操作,避免拒绝访问等跨域操作问题
前端跨域问题解决
signalR跨域及解决方案 Access-Control-Allow-Origin' header is present之 为什么会跨域及解决方案
跨域访问解决方案,关于跨域访问cookie的资料
JS跨域访问解决方案总结.。总结了记住JS跨域访问解决方案。
个人用的比较好的解决办法,ASP.NET跨域SSO解决方案
vue跨域问题解决方案.docx
NULL 博文链接:https://sun123start.iteye.com/blog/2150778
web开发的跨域问题解决方案示例
在本篇文章里小编给各位分享了关于nginx怎么解决跨域问题的方法和实例代码,需要的朋友们参考下。