iframe跨域的基本前提是,一个页面可以嵌套非同源站点的html文件,以及某一个域名下的html页面可以通过脚本向同域名服务器发出ajax请求。当一个域名为domain1下的页面A想要向domain2发出ajax请求时,由于同源策略的限制无法直接请求到数据,但是可以在页面A中动态添加一个display设置为none的iframe,该iframe的src为domain2下的html页面B,由页面B来发出ajax请求,因为页面B是domain2下的所以可以成功发出请求。当B页面拿到数据之后再传递给A页面。

可以利用HTML5的postMessage来向A页面传递数据。
将通过一个demo来具体说明如何操作,该demo涉及到两个域名 http://localhost:3000 以及 http://127.0.0.1:3001。向 http://localhost:3000/a.html中嵌入 http://127.0.0.1:3001/b.html页面,在 http://127.0.0.1:3001/b.html发出本域名下的ajax请求。

先举一个简单的例子。

a.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<iframe src="http://127.0.0.1:3001/b.html"></iframe>
<script>window.addEventListener('message',e => {console.log(e.data);})
</script>
</body>
</html>

b.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<script>$.ajax({url: 'http://127.0.0.1:3001/data.json'}).done(data => {window.parent.postMessage(data,'*');})
</script>
</body>
</html>

a页面中直接嵌入b页面,b页面加载后执行脚本,向同源服务器发出请求,拿到数据后发送给a页面。
这里强调一下:
1.代码中写的是window.parent而非window
2.代码中的第二个参数写为'*',也可以指定精确的目标origin

当然实际开发中一个页面要发出很多个请求,并且根据用户操作不同可能要发出不一样的请求,所以需要进一步的封装。

a页面通过b页面发出请求,这需要将发送请求的url以及请求的类型和请求参数等信息传递到b页面,可以直接通过嵌入的iframe的src来传递,然后b页面根据location.search的值来获取以上的信息。

我们的目标是构建一个形如iframeAjax(params,cb)的函数。
其中params中,包括了b页面的url,ajax请求的最终目标url,ajax请求的种类,以及ajax请求的参数。
格式如下:

let {targetUrl, queryUrl, type, data = {}} = params

刚才已经提到过,a页面通过设置iframe的src来向b页面传递各种参数。
我们将type以及queryUrl合并到data中:

Object.assign(data, {type, queryUrl});

接下来就可以构建完整的iframe的src

let url = targetUrl + '?' + serialize(data);    //虽然叫url但实际是iframe的src

然后我们封装一个建立frame的方法。

function createIframe(url, cb) {let iframe = document.createElement('iframe');iframe.style.display = 'none';iframe.src = url;document.body.appendChild(iframe);function handleIframe(e) {cb(e.data);document.body.removeChild(iframe);window.removeEventListener('message', handleIframe);}window.addEventListener('message', handleIframe);
}

在这个方法中,我们创建了一个iframe设置其display为none使其不可见,设置了其src,并且把它添加到了页面上。又给window添加了事件监听,当收到b页面传来的数据之后,调用数据处理的函数cb并且传入数据作为其参数。当cb执行完毕之后,移除iframe,并移除事件监听。

b页面中我们的任务是,拿到location.search的并正确的解析为一个对象,该对象包含了{type,queryUrl,data} 其中type是ajax的类型,queryUrl是ajax的目标url,data是请求的参数。

通过一个parseSearch的方法解析location.search。

function parseSearch() {let search = location.search.slice(1);let keyValuePairArr = search.split('&');let obj = {};keyValuePairArr.forEach(pair => {let [key, value] = pair.split('=');obj[decodeURIComponent(key)] = decodeURIComponent(value);});return obj;
}

我们还需要一个方法来移除其中的type和queryUrl,只剩下ajax请求的参数。

function getParams(obj) {delete obj.type;delete obj.queryUrl;return obj;
}

然后我们就可以解析location.search得到信息对象,并且发出ajax请求来拿到服务器的数据了。

$.ajax({type: obj.type,url: obj.queryUrl,data: getParams(obj)}).done(data => {window.parent.postMessage(data, '*');
});

再次巩固一下整个流程,domain1下的a页面向domain2发出动态ajax请求的中间过程:
在a页面下封装b页面完整url,ajax请求的type,ajax请求的最终url,以及ajax请求的数据等信息。通过设置iframe的src将这些信息带到b页面,b页面解析这些信息,最后向服务器发出ajax请求并将结果通过postMessage的API带回给a页面,a页面收到数据后处理数据,处理完毕移除iframe,移除事件监听。

除了利用postMessage API之外,还可以通过iframe + window.name来实现跨域ajax请求。
当a页面中有一个b页面的iframe时,两个页面共享window.name。在b页面拿到数据后赋值给window.name。但是由于不使用postMessage API,数据无法传输给a页面。我们可以将location.href转为domain1(a页面所在的域名)下的c页面,由c页面调用a页面的回调方法。

先举一个简单的例子:

a页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<iframe src="http://127.0.0.1:3001/b.html"></iframe>
<script>function print(data) {console.log(data);}
</script>
</body>
</html>

b页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<script>$.ajax({url: 'data.json'}).done(data => {window.name = JSON.stringify(data);location.href = 'http://localhost:3000/c.html';})
</script>
</body>
</html>

c页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>parent.print(JSON.parse(window.name));
</script>
</body>
</html>

很简单的例子,a页面中定义一个方法print,并嵌入一个frame,frame为b页面,b页面向服务器拿到数据后,赋值给window.name,然后再打开c页面,在c页面调用父页面a中的print方法,并将拿到的数据作为参数传入。
不知道为啥,ajax请求返回的对象不经处理直接赋值最终输出结果是[object Object]。因此在这里先stringify之后再parse。

封装之后的代码如下:

aa页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>function serialize(params) {let paramArr = [];for (let [key, value] of Object.entries(params)) {paramArr.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);}return paramArr.join('&');}function iframeAjax(params, cb) {let funcName = randomFuncName();window[funcName] = cb;let {midUrl, targetUrl, queryUrl, type, data = {}} = params;Object.assign(data, {targetUrl, queryUrl, type, funcName});let url = midUrl + '?' + serialize(data);createIframe(url);}function randomFuncName() {return 'iframe' + Math.floor(Math.random() * 100000 + 100000);}function createIframe(url) {let iframe = document.createElement('iframe');iframe.style.display = 'none';iframe.src = url;document.body.appendChild(iframe);}iframeAjax({midUrl: 'http://127.0.0.1:3001/bb.html',targetUrl: 'http://localhost:3000/c.html',queryUrl: 'http://127.0.0.1:3001/async-post',type: 'post',data: {name: '黄天浩'}}, function (data) {console.log(data);});
</script>
</body>
</html>

bb页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<script>function parseSearch() {let search = location.search.slice(1);let keyValuePairArr = search.split('&');let obj = {};keyValuePairArr.forEach(pair => {let [key, value] = pair.split('=');obj[decodeURIComponent(key)] = decodeURIComponent(value);});return obj;}function getData(obj) {let dataObj = {};let dataKeys = Object.keys(obj).filter(key => key !== 'type' && key !== 'queryUrl' && key !== 'targetUrl' && key !== 'funcName');dataKeys.forEach(key => {dataObj[key] = obj[key];delete obj[key];});return dataObj;}let obj = parseSearch();let data = getData(obj);$.ajax({type: obj.type,url: obj.queryUrl,data}).done(data => {window.name = JSON.stringify(data);//window.name = data;location.href = obj.targetUrl + '?funcName=' + obj.funcName;});
</script>
</body>
</html>

c页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>function getFuncName() {let search = location.search;let reg = /\?funcName=(.+)/;return reg.exec(search)[1];}let funcName = getFuncName();parent[funcName](JSON.parse(window.name));//parent[funcName](window.name);delete parent[funcName];let iframes = [...parent.document.getElementsByTagName('iframe')];iframes.forEach(iframe => {parent.document.body.removeChild(iframe);})
</script>
</body>
</html>

不同于使用postMessage,当通过window.name通信时,由于传入的回调函数是在页面c执行,因此需要把回调函数的函数名作为参数传过来,并且也要把页面c的url地址作为参数传入。数据仍然是通过bb页面获得,bb页面仅需向c页面传递funcName即可,c页面拿到函数名,并通过window.name传递从服务器拿到的数据,在c页面执行aa页面的回调函数,回调函数执行完毕后移除aa页面iframe以及该函数。

注意:不能在bb页面直接调用parent页面aa的函数 由于aa与bb是非同源的,因此会报错。必须由一个与aa页面同源的页面cc页面,来执行在aa页面中注册的函数。

以上是我所知道的iframe解决跨域ajax请求的两种方法。

iframe解决跨域ajax请求的方法相关推荐

  1. vue开发环境和生产环境里面解决跨域的几种方法

    vue开发环境和生产环境里面解决跨域的几种方法 参考文章: (1)vue开发环境和生产环境里面解决跨域的几种方法 (2)https://www.cnblogs.com/pass245939319/p/ ...

  2. 什么是跨域及解决跨域都有哪些方法?

    一.同源策略 浏览器为了隔离潜在的恶意文件,使用同源策略,限制从一个源加载的文档或脚本和另一个源的资源进行交互(不同源之间的文档,资源的交互); 通俗的理解:浏览器规定,A 网站的 JavaScrip ...

  3. ajax中cors解决跨域,AJAX 跨域 CORS 解决方案

    两种跨域方法 在 Javascript 中跨域访问是比较常见的事情 就像现在比较流行写单页应用,而单页应用在访问 API 的时候就会有跨域的问题 要解决跨域的问题,其实也并不复杂,有两种方案可以选择 ...

  4. 前端解决跨域的九种方法

    什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的. 广义的跨域: 1.资源跳转:A链接.重定向.表单提交 2.资源嵌入: <link>.<scr ...

  5. SpringMvc 3.x跨域+ajax请求

    一.Cors,实现Js跨域访问Tomcat下资源(步骤如下) web.xml配置 <filter> <filter-name>CorsFilter</filter-nam ...

  6. 什么是同源策略?解决跨域的三种方法?

    1.同源策略 同源策略是一种约定和规范好的安全策略,是浏览器最核心最基本的安全保障.同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据. 满足的条件: (1)协议要相同:HTTP.HTT ...

  7. ajax跨域请求.json文件,使用JSONP对JSON文件进行跨域Ajax请求

    小编典典 您的服务必须返回jsonp,这基本上是javascript代码.您需要从ajax请求中为服务提供回调函数,然后返回的是函数调用. 下面是一个工作示例. ajax请求: $.ajax({ cr ...

  8. jquery跨域Ajax请求

    sonp原理:  首先在客户端注册一个callback, 然后把callback的名字传给服务器. 此时,服务器先生成 json 数据. 然后以 javascript 语法的方式,生成一个functi ...

  9. vue cli3解决跨域的两种方法

    请去下面网站非常清晰:https://www.jianshu.com/p/eb3de95cfc82

最新文章

  1. java oracle in 10000_java支持ORACLE的in不能超过1000
  2. 海思3531D上编译FFmpeg源码操作步骤
  3. SAP MM 经过审批之后的PR单据被MRP RUN 之后Overwrite问题之对策
  4. Camtasia Studio 7 试用笔记
  5. 委派模式的定义及应用场景
  6. 基于Spring Cloud及K8S构建微服务应用
  7. js、jQuery实现文字上下无缝轮播、滚动效果
  8. android合入第三方库,Android中inflate和merge结合使用
  9. SwitchHosts修改hosts利器
  10. learun.framework v7.0.6 — . net快速开发框架
  11. 老毛桃u盘装系统linux,老毛桃U盘PE重装系统教程
  12. help指令和man指令的区别
  13. 不需要个人信息的云服务器,那些云服务器不需要实名
  14. Windows---命令打开截图工具,.bat文件执行
  15. postgres汉字转换为拼音
  16. 虹科分享 | 基于流的流量分类的工作原理 | 网络流量监控
  17. C89和C99标准对比
  18. 基于javaweb+SSM的校园外卖点餐系统(java+SSM+JSP+maven+mysql)
  19. 计算机网络——IP数据报分片
  20. 19. Redis的使用

热门文章

  1. 收缩 tempdb 数据库
  2. zoj2760(最大流)
  3. Android projects on Github
  4. [原]Java 正则 多子串 匹配 替换
  5. php对象的底层机制
  6. 推荐NHibernate新书:NHibernate 3.0 CookBook[附下载]
  7. AndroidStudio使用第三方jar包报错(Error: duplicate files during packaging of APK)
  8. CANOE入门(一)
  9. jrtplib 分包处理
  10. 深入理解 Laravel Eloquent(三)——模型间关系(关联)