跨域请求的几种方式(一)

74 阅读2分钟

Welcome

今日大雨,公交车上看到关注的某前端微信公众号,推送了一条消息。结尾是'你知道几种跨域请求的方式?‘脑子一闪貌似在曾经的面试中也遇到过类似的问题。了解了一下便不再理会了。今天又重新提出来,花了两个小时重新温习理解一下旧的东西。记录下来如有不足还请大神们指出。

最初始的jsonp

  • 原生js
    var script = document.createElement('script');
    script.type = 'text/javascript';
    // 传参并指定回调执行函数为onBack
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
    document.head.appendChild(script);

    // 回调执行函数
    function onBack(res) {
        alert(JSON.stringify(res));
    }
  • ajax原理
 $.ajax({
     url: url,
     dataType: 'jsonp',
     type: 'get',
     sucess: function() {
         .....
     },
     error:
 })

但是有一个缺点就是:只能实现get一种请求,显然不能满足我们的需求。

空的iframe + form (发送请求,没有返回值)

//--------简写并不完整
const requestPost = (url, data) => {
    //首先先创建一个用于发送数据的iframe
    const iframe = document.createElement('iframe');
    iframe.name = 'iframePost';
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    const form = document.createElement('form');
    const node = document.createElement('input');
    form.action = url;
    form.target = iframe.name;
    form.method = 'post',
}

原理 利用form的action和target属性向iframe发送数据,搭配iframe标签的跨域能力,实现跨域并且是提交无刷新。

iframe跨域 (分为主域相同,子域不同时和主域不相同时)

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>

子窗口:

<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    alert('get js data from parent ---> ' + window.parent.user);
</script>

原理:主域相同子域不同的条件下,实现跨域请求变量,两个域都需要设置域名 document.domain=‘xxxx’

  • 主域不相同

  1. location.hash +iframe跨域 实现原理:想要A域与B域相互跨越通信,通过只能中间页C来实现。三个页面,不同域之间利用location.hash传值,相同域直接js访问通信 具体实现:A域(a.html)->B域(b.html)->A域(c.html),A与B不同域通过hash值单向传递,B与C不同域只能单向从B向C传递,但是A与C同域,C可通过parent.parent访问页面的所有对象。
a.html:www.yuhior1.com/a.html
<iframe id="iframe" src="http://www.yuhior2.com/b.html" style="display: none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');
    setTimeout(function() {
        iframe.src = iframe.src + '#user = admin';
    }, 1000);
    //提供同域c.html的回调方法
    function oncallback(res) {
        alert('data from c.html---->' + res);
    }
</script>
b.html:www.yuhior2.com/b.html
<iframe id="iframe" src="http://www.yuhior1.com/c.html" style="display: none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');
    //监听a.html传过来的hash值,然后在传给c.html
    window.onhashchange = function() {
        iframe.src = iframe.src + location.hash;
    };
</script>
c.html:www.yuhior1.com/c.html
<script>
    //监听b.html传过来的hash值
    window.onhashchange = function() {
        //再通过操作同域下a.html的js回调将结果返回
        window.parent.parent.onCallback('hello' + location.hash.replace('#user=', ''));
    };
</script>
  1. window.name+iframe跨域 实现原理:window.name的不同之处在于:name值在不同的页面甚至不同的域名,加载后依旧存在,并且可以支持非常长的name值大约为2MB 具体实现原理:假设index.html页面需要请求远端服务器的数据,我在该页面下建一个iframe,它的src属性指向服务器文件地址(iframe标签的跨域能力),服务器文件里设置好window.name的值(也就是该iframe的contentWindow的name),然后在index.html里读取iframe的window.name的值。但是仅仅这样不能成功,因为index.html页面和该页面里边的iframe框架src不同域,则也就无法操作框架里边的东西,自然也就获取不到iframe的name值了,所以采用上边location.hash +iframe的思想 使用一个中间页面 ,在index.html同源下新建一个proxy.html 空页面

上代码:

<script type="text/javascript">
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    var state = 0;
    //防止onload事件之后,重置src ,重新载入页面,再触发onload事件
    iframe.onload = function() {
        if (state === 1) {
            var data = JSON.parse(iframe.contentWindow.name);
            console.log(data);
            iframe.contentWindow.document.write('');
            iframe.contentWindow.close();
            document.body.removeChild(iframe);
        } else if (state === 0) {
            state = 1;
            iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';
        }
    };

    iframe.src = 'http://localhost:8080/data.php';
    document.body.appendChild(iframe);
</script>
未完待续...... * * *