使用HTML5中postMessage实现Ajax中的POST跨域问题

澳门新葡亰网址 1

HTML5中提供了在网页文档之间相互接收与发送信息的功能。使用这个功能,只要获取到网页所在窗口对象的实例,不仅仅同源(域+端口号)的web网页之间可以互相通信,甚至可以实现跨域通信。

这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。

浏览器支持程度:IE8+,firefox4+,chrome8+  opera10+

JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤为重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不同的子域名之间的页面也不能通过JavaScript代码互相访问。对于Ajax的影响在于,通过XMLHttpRequest实现的Ajax请求,不能向不同的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。

1.
首先,要想接收从其他的窗口发过来的消息,就必须对窗口对象的message事件进行监听,如下代码:

然而,当进行一些比较深入的前端编程的时候,不可避免地需要进行跨域操作,这时候“同源策略”就显得过于苛刻。本文就这个问题,概括了跨域所需要的一些技术。

window.addEventListener(“message”, function(){},false);

下面我们分两种情况讨论跨域技术:首先讨论不同子域的跨域技术,然后讨论完全不同域的跨域技术。

2.
其次,需要使用window对象的postMessage方法向其他窗口发送消息,该方法定义如下所示:

(一)不同子域的跨域技术。

otherWindow.postMessage(message, targetOrigin);

我们分两个问题来分别讨论:第一个问题是如何跨不同子域进行JavaScript调用;第二个问题是如何向不同子域提交Ajax请求。

该方法使用2个参数,第一个参数为所发送的消息文本,但也可以是任何javascript对象,第二个参数是接收消息的对象窗口的url地址(比如:http:127.0.0.1:8080/)
, 但是我们也可以在url地址字符串中使用通配符”*”,
指定全部的域下,但是我们还是建议使用特定的域名下,otherWindow为要发送窗口对象的引用。

先来解决第一个问题,假设example.com域下有两个不同子域:abc.example.com和def.example.com。现在假设在def.example.com下面有一个页面,里面定义了一个JavaScript函数:

Demo演示:

复制代码 代码如下:

假如现在我在hosts文件下 ,绑定2 个域名如下:

function funcInDef() {
    …..
}

127.0.0.1       abc.example.com

我们想在abc.example.com下的某个页面里调用上面的函数。再假设我们要讨论的abc.example.com下面的这个页面是以iframe形式嵌入在def.example.com下面那个页面里的,这样我们可能试图在iframe里做如下调用:

127.0.0.1        longen.example.com

复制代码 代码如下:

现在假如在abc.example.com域下有一个abc.html页面,在longen.example.com域下有def.html页面,现在我是希望这2个不同域名下的页面能互相通信,abc.html代码如下:

window.top.funcInDef();

<form>  
      <p>  
        <label for="message" style="color:red;font-size:24px;">给iframe子窗口发一个信息:</label>  
        <input type="text" name="message" value="send" id="message" />  
        <input type="submit" value="submit" id="submit"/>  
      </p>  
</form>  
<h4>目标iframe传来的信息:</h4>  
<p id="test">暂无信息</p> 

 <iframe id="iframe"  
    src="http://longen.example.com/webSocket/def.html" style="display:none"></iframe>

好,我们注意到,这个调用是被前面讲到的“同源策略”所禁止的,JavaScript引擎会直接抛出一个异常。
为了实现上述调用,我们可以通过修改两个页面的domain属性的方法做到。例如,我们可以将上面在abc.example.com和def.example.com下的两个页面的顶端都加上如下的JavaScript代码片段:

JS代码如下:

复制代码 代码如下:

var win = document.getElementById("iframe").contentWindow;

document.getElementById("submit").onclick = function(e){
    e.preventDefault();
    win.postMessage(document.getElementById("message").value,"http://longen.example.com"); 
}  

window.addEventListener("message",function(e){
     e.preventDefault();
     document.getElementById("test").innerHTML = "从" + e.origin + "那里传过来的消息:n" + e.data;
},false);

document.domain = “example.com”;

Def.html代码如下:

这样,两个页面就变为同域了,前面的调用也可以正常执行了。

HTML代码:

这里需要注意的一点是,一个页面的document.domain属性只能设置成一个更顶级的域名(除了一级域名),但不能设置成比当前域名更深层的子域名。例如,abc.example.com的页面只能将它的domain设置成example.com,不能设置成sub.abc.example.com,当然也不能设置成一级域名com。

<form>  
      <p>  
        <label for="message">给父窗口abc.html发个信息:</label>  
        <input type="text" name="message" value="send" id="message" />  
        <input type="submit" />  
      </p>  
 </form>  
 <p id="test2">暂无信息。</p>

上面的例子讨论的是两个页面属于iframe嵌套关系的情况,当两个页面是打开与被打开的关系时,原理也完全一样。

JS代码如下:

下面我们来解决第二个问题:如何向不同子域提交Ajax请求。
通常情况下,我们会用与下面类似的代码来创建一个XMLHttpRequest对象:

var parentwin = window.parent; 
window.addEventListener("message",function(e){
       document.getElementById("test2").innerHTML = "从父窗口传来的域" +e.origin + ",和内容数据:" + e.data;  
       parentwin.postMessage('HI!你给我发了"'+e.data+'"。',"http://abc.example.com");
},false);

复制代码 代码如下:

当我点击abc.html页面后,可以看到效果如下,从def.html返回内容了。如下:

factories = [function() {
    return new XMLHttpRequest();
},
function() {
    return new ActiveXObject(“Msxml2.XMLHTTP”);
},
function() {
    return new ActiveXObject(“Microsoft.XMLHTTP”);
}];
function newRequest() {
    for (var i = 0; i & lt; factories.length; i++) {
        try {
            var factory = factories[i];
            return factory();
        } catch(e) {}
    }
    return null;
}

澳门新葡亰网址 1

上面的代码中引用ActiveXObject,是为了兼容IE6系列浏览器。每次我们调用newRequest函数,就获得了一个刚刚创建的Ajax对象,然后用这个Ajax对象来发送HTTP请求。例如,下面的代码向abc.example.com发送了一个GET请求:

我们需要知道如下几条信息:

复制代码 代码如下:

  1. 通过对window对象的message事件进行监听,可以接收消息。
  2. 通过访问message事件的origin属性,可以获取消息的发送源。
  3. 通过访问message事件的data属性,可以取得消息内容。
  4. 使用postMessage方法发送消息。
  5. 澳门新葡亰网址,通过访问message事件的source属性,可以获取消息发送源的窗口对象(准确的说,应该是窗口的代理对象)。

var request = newRequest();
request.open(“GET”, “” );
request.send(null);

有了上面的基本知识点,我们可以延伸为实现ajax POST跨域的问题。

假设上面的代码包含在一个abc.example.com域名下的页面里,则这个GET请求可以正常发送成功,没有任何问题。然而,如果现在要向def.example.com发送请求,则出现跨域问题,JavaScript引擎抛出异常。
解决的办法是,在def.example.com域下放置一个跨域文件,假设叫crossdomain.html;然后将前面的newRequest函数的定义移到这个跨域文件中;最后像之前修改document.domain值的做法一样,在crossdomain.html文件和abc.example.com域下调用Ajax的页面顶端,都加上:

二:使用postMessage 知识点解决 ajax中POST跨域问题。

复制代码 代码如下:

原理:原理也很简单,假如我们的域名abc.example.com下的abc.html页面需要发ajax请求(跨域,域名为longen.example.com)下,那么我们还是先跨页面文档的形式,和上面一样,我们可以现在longen.example.com下
建立一个页面,比如叫def.html. 那么我们现在还是在 abc.html
页面嵌入一个隐藏域iframe
src路径指向longen.example.com域下def,html页面。过程还是和跨文档类似,只是现在在def.html页面中
在window.onmessage 事件内写ajax请求即可,如下代码:

document.domain = “example.com”;

abc.example.com下的abc.html页面如下:

为了使用跨域文件,我们在abc.example.com域下调用Ajax的页面中嵌入一个隐藏的指向跨域文件的iframe,例如:

html代码和上面一样,下面是JS代码:

复制代码 代码如下:

var win = document.getElementById("iframe").contentWindow;

document.getElementById("submit").onclick = function(e){
      e.preventDefault();
      win.postMessage(document.getElementById("message").value,"http://longen.example.com/"); 
}  

window.addEventListener("message",function(e){
    e.preventDefault();
    alert(typeof e.data)
    var json = JSON.parse(e.data);
     console.log(json);
    alert(json.url)
},false);

<iframe name=”xd_iframe” style=”display:none”
src=”;
</iframe>

def.html代码如下:

这时abc.example.com域下的页面和跨域文件crossdomain.html都在同一个域(example.com)下,我们可以在abc.example.com域下的页面中去调用crossdomain.html中的newRequest函数:

JS代码如下:

复制代码 代码如下:

//获取跨域数据  
window.onmessage = function(e){  
     $.ajax({
          url: 'http://longen.example.com/webSocket/test.php',
          type:'POST',
          dataType:'text',
          //data: {msg:e.data},
          success: function(res) {
               var parentwin = window.parent;  
               parentwin.postMessage(res,"http://abc.example.com");//跨域发送数据  
          }
      });
 };

var request = window.frames[“xd_iframe”].newRequest();

test.php代码如下:

这样获得的request对象,就可以向

<?php 
    $data=array(  
     url =>1,
      name =>'2',
      'xx-xx'=>"xx"
 );
 echo json_encode($data);
?>

(二)完全不同域的跨域技术。

如上实现方式 就可以实现ajax post跨域了。

如果顶级域名都不相同,例如example1.com和example2.com之间想通过JavaScript在前端通信,则所需要的技术更复杂些。

在讲解不同域的跨域技术之前,我们首先明确一点,下面要讲的技术也同样适用于前面跨不同子域的情况,因为跨不同子域只是跨域问题的一个特例。当然,在恰当的情况下使用恰当的技术,能够保证更优的效率和更高的稳定性。

简言之,根据不同的跨域需求,跨域技术可以归为下面几类:
1、JSONP跨域GET请求
2、通过iframe实现跨域
3、flash跨域HTTP请求
4、window.postMessage
下面详细介绍各种技术。
1. JSONP。 利用在页面中创建<script>节点的方法向不同域提交HTTP请求的方法称为JSONP,这项技术可以解决跨域提交Ajax请求的问题。JSONP的工作原理如下所述:
假设在

复制代码 代码如下:

var eleScript= document.createElement(“script”);
eleScript.type = “text/javascript”;
eleScript.src = “”;
document.getElementsByTagName(“HEAD”)[0].appendChild(eleScript);

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图