面试题2——跨域传值

220 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天juejin.cn/post/712312…

跨域传值

方式:

  1. JSONP
  2. window.name+iframe
  3. 服务器代理
  4. CORS
  5. psotMessage
  6. webSocket

JSONP

  • 实现原理:利用html标记src属性的特征,再利用json对象作为传输数据的格式,这种方式就叫jsonp;该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住json数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
  • 浏览器接收到jsonpcallback值字符串以后,就会把想要的数据从数据库拿出来,后台会把数据和接收到的数据做一个拼接,拼接格式为jsonpcallback.”(“data)”,(解释data”)”,(解释data为php页面创建变量的方法)会把这个返回的数据放到script之间,相当于函数调用,浏览器在读到script这行代码时,就马上调用这个函数,就出结果了,结果是什么类型看接口文档,从而进行处理就可以了。
  • 注意:Jsonp只允许发送get请求。因为它传数据就是以?进行拼接的

window.name+iframe

  • 实现原理:通过iframe标记将跨域的页面引入当前窗口,并且通过contentWindow属性获取iframe中页面的window对象,从而读取其name属性中存储的数据,从而实现跨域传值。
  • 注意
    1. 给iframe这个标记 绑定onload事件,必须等iframe加载完之后再去读取。
    2. iframe只能做跨页面之间的传值,src引入的只能是一个html页面,不会从服务器拿数据,因为在html中,iframe本身这个标记就是只能引入一个html页面
    3. 比较小的数据适合用iframe传值
    4. 得用live server打开这个文件,如果iframe纯粹为了传值,就把它隐藏display:none;
page1页面
<body>
    <iframe src="mypage2.html" style="display:none" id="fra"></iframe>
</body>
<script>
    document.getElementById('fra').onload=function(){
        var ifwindow=this.contentWindow;//获取浮动框架引入页面的window对象
        var data=ifwindow.name;//通过name属性获取引入页面对象的数据,
        page2的数据要写成字符串,要不然的话就会返回page2的类型
        console.log(data);
    }
</script>
page2页面
<script>
    var data='{"name":"tom","age":"20","sex":"男"}';
    window.name=data;
</script>

PPT:当页面在浏览器展示的时候,我们总能在控制台拿到一个全局变量window,该变量有一个name属性,其有以下特征;

  1. 每个窗口都有一个独立的window.name
  2. 在一个窗口的生命周期(打开到被关闭前),窗口载入的所有页面同时共享一个window.name,每个页面对window.name都有独写的权限
  3. Window.name一直存在当前窗口,即使是有新的而页面载入也不会改变window.name的值
  4. Window.name可以存储不超过2M的数据,数据格式按需自定义(IE和firefox下可以大至32M左右)

服务器代理

  • 实现原理:浏览器发送请求给同源的代理服务器(它俩肯定是同源的),代理服务器把请求转发给跨域服务器(它俩不存在同源不同源的问题),跨域服务器响应请求给代理服务器,代理服务器转发响应给浏览器。语法依然是ajax,怎么知道发送给了代理服务器?文档接口给的url请求地址就是那个代理服务器的地址,代理服务器把请求发送给服务器就不需要我们管了。

CORS(跨域资源共享)

  • CORS允许ajax跨域,但不能说ajax是跨域的,因为ajax本身是不能跨域的。
  • CORS跨域资源共享的过程: 向服务器发送请求时,会把接口文档里面给的url地址的请求头,增加一个当前html的url,发送到的服务器,服务器就会比较一下,服务器的白名单里有没有携带的当前页面url(origin字段),有的话会返回四个以下信息,没有的话就不会返回对象,但是会出一个不是same orign这样的提示

PPT说明:

  1. CORS是一个W3C标准,全称是“跨域资源共享“(cross-origin rescource sharing),它允许浏览器向跨源(协议、域名、端口号)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
  2. CORS需要浏览器和服务器同时支持,它的通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS通信与同源的AJAX通信没有任何差别,代码完全一样,浏览器一旦发现ajax请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
  3. 因此,CORS通信关键是服务器,只要服务器实现了CORS接口,就可以跨源通信。

浏览器将CORS请求分为了两类,简单请求和非简单请求。

  • 说明:对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个orgin字段。

GET/cors HTTP /1.1

Origin :http:// api . bob .com

Host:  api . alice . com

Accept-Language : en-US

Conoection: keep-alive

User-Agent: Mozilla / 5.0( 它们在F12里网络里响应头信息查看)

  • 上面的头信息中,origin字段用来说明,本次请求来自于哪个源(协议、域名、端口号),服务器根据这个值,决定是否同意这次请求。
  • 如果origin指定的源,不在跨域服务器许可范围(白名单)内,服务器会返回一个正常HTTP的回应,浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意:这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200.
  • 如果origin指定的域名不在许可范围内,服务器返回的响应,会多出以下几个头信息字段。(其中三个与CORS请求相关的字段,都以Access-Control开头)

1)         Access-Control-Allow-Origin:

说明:该字段是必须的,它的值要么是请求时origin字段的值,要么一个*,表示接收任意域名的请求。

2)         Access-Contorl-Allow-Credentials:是否允许携带cookie

说明:该字段可选。它的值是一个布尔值,表示是否允许发送cookie,默认情况下,cookie不包括在CORS请求中,设为true,即表示服务器明确许可,cookie可以包含在请求中,一起发给服务器,这个值也只能设为true,如果服务器不要浏览器发送的cookie,删除该字段即可。

3)         Access-Conrol-Expose-Headers:允许header类型

说明:该字段可选。CORS请求时,XMLHttpRequest对象的个体getResponseHeader()方法只能拿到6个基本字段:Cache-Control、,Content-Lanuage、Content-Type、Expires、Last-Modifed、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Header里面指定。

GetRequesponseHeader(“FooBar“)可以返回FooBar字段的值。

 

上面说到,CORS请求默认不发送cookie和HTTP认证信息,如果把cookie发到服务器,一方面需要服务器同意,指定Access-Control-Allow-Credentials字段。

另一方面,开发者必须在ajax请求中打开withCredentials属性。否则,即使服务器统一发送cookie,浏览器也不会发送,或者服务器要求设置cookie,浏览器也不会处理。

Cookie

如果要发送cookie,并且连接的跨域服务器允许发,就在发送请求的页面写:ajax对象●withCreadentials=true这个功能;(也就是在原生js里ajax请求方法的那几步里添加上这个)(正常情况ajax发送请求时都自动携带cookie,并且服务器也允许发送,但跨域就需要设置以下,ajax不会自动携带,给ajax添加该属性=true,前提也是跨域服务器允许的情况下才能携带)

需要注意的是,如果要发送cookie,Access-Control-Allow-Origin就不能设置为*,必须指定明确的、与请求网页一致的域名。同时,cookie依然遵循同源策略,只有服务器域名设置的cookie才会上传,其他域名的cookie并不会上传,且(跨域)原网页代码中的document.cookie也无法读取服务器域名下的cookie。

 

预检请求是在向服务器发送请求时,会问服务器,你让不让我连接你,让的话在发送请求,不让直接报错,(该方法主要也指的是CORS跨域资源共享的method方式是get或者post以外的方法。)

一旦服务器通过了“预检请求“,以后每次浏览器正常的CORS请求,就跟简单请求一样,会有一个origin头信息字段,服务器的回应,也都会有一个Access-Control-Allow-origin头信息。

postMessage

  • 说明:是h5提出的新的方法,也需要iframe去实现。PostMessage它是window的方法。它可以直接实现跨域的。
  • 接收消息的窗口可以自由处理MessageEvent事件
  • postMessage只发送一个消息给引入的页面,该页面就能接收消息了
  • 必须通过事件监听来监听message事件,只要message发生改变时,就会触发该函数

实现步骤

  1. 用iframe先引入window(跨域)页面,引入b页面到a页面
  2. 在a页面里面获取b页面window对象(contentWindow获取)
  3. 在a页面内让b页面调用postMessage这个方法(第一个参数为发送的消息,第二个为a页面的url(url写端口号就行,路径根据需求))
  4. 只要第三步的postMessage方法一调用,也就是说一向b页面传输数据,就会触发b页面的messge这个事件(message事件由事件监听来做),携带的数据会存入事件对象event里。
a页面
<body>
    <iframe src="bpostMessage.html" id="fra"></iframe>
    <button id="btn">点击发送消息</button>
</body>
<script>
    var fra=document.getElementById('fra');
    fra.onload=function(){
        // 先获取引入页面的window对象
        var win=fra.contentWindow;
        console.log(win);
        // 通过postMessage()方法传输数据,只要该方法一调用,就会触发b页面的message事件
        document.getElementById('btn').onclick=function(){
            win.postMessage('dd','http://127.0.0.1:5500/ApostMessage.html');
        }
    }
</script>
b页面
<body>
    <div id="one">哈哈哈</div>
</body>
<script>
    window.addEventListener('message',function(e){
        if(e.origin!=="http://127.0.0.1:5500"){
            return;
**        **}
        var one=document.getElementById('one');
        one.innerHTML=e.data
        console.log(e.data);
    },false/true)//false/true可省略,如果为true就是不冒泡,false是冒泡
</script>

<body>
    <input type="text" value="哈哈哈" id="text"> <button id="btn">发送消息</button>
    <iframe src="receiver.html" id="receiver"></iframe>
</body>
<script>
    window.onload=function(){
        //获取引入的window对象
        var receiver=document.getElementById('receiver').contentWindow;
        console.log(receiver)
        //给按钮绑定点击事件
        document.getElementById('btn').addEventListener('click',function(){
            var val=document.getElementById('text').value;
           // 一点击就让引入的页面调用postMessage方法,向另一页面发送数据并携带本页的url
           //通过postMessage()方法传输数据,只要该方法一调用,就会触发b页面的message事件
            receiver.postMessage(val,'http://127.0.0.1:5500/send.html');
        })
    }
</script>
<body>
    <div id="message">hello word</div>
</body>
<script>
    window.onload=function(){//不绑定onload事件也没有影响
        var message=document.getElementById('message');
        //另一页面一调用postMessage方法,就会触发本页面的message事件
        window.addEventListener('message',function(e){
        //判断一下orgin字段是不是本页面指定的源(只写协议域名端口号就行)
        //'http://127.0.0.1:5500'指的是是刚才传数据页面的url的协议、域名、端口号
            if(e.origin!=='http://127.0.0.1:5500'){
                return;
            }
            message.innerHTML=e.data;
        })
    }

事件对象event对象有三个属性:

  1. event●data表示接收到的消息:
  2. event●origin表示postMessage的发送来源,包括协议、域名、端口号
  3. event●source表示发送消息的窗口对象的引用(它的值得到的是发送页面的URL),我们可以用这个引用来建立两个不是同源页面的窗口之间的双向通信。

获取另一个窗口的window对象的语法:

  • targetWindow.postMessage(message,targetOrigin,[ transfer] )
  • targetWindow就是接收消息的窗口的引用。获得该引用的方法包括:
  • window.open
  • window.opener
  • HTMLIFrameElement.contentWindow
  • Window.parent
  • Window.frames+索引值
  • content.Window

webSocket

实现原理

  1. 浏览器通过javascript向服务器发出建立WebSocket连接的请求,连接建立后,客户端和服务器端就可以通过TCP连接直接交换数据。
  2. 当你获取WebSocket连接后,你可以通过send()方法;来向服务器发送数据,并通过onmessage事件来接收服务器返回的数据。 它本身不是做跨域的,但这是它一个很小的功能。它是一种在单个TCP连接上进行全双工通信的协议。它和http是一回事,不过http是半双工的,http服务器不能主动向客户端发送消息。

webSocket说明

1、WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通信的协议。

2、WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,在WebSocketAPI中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

ajax轮询机制

  • 现在,很多网站为了实现推送技术,所用的技术都是ajax轮询。轮询是在特定的时间间隔(比如每一秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器,这种传统的模式带来很明显的缺点,即浏览器要不断的发送请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
  • 如果不用webSocket机制,怎么实现这个轮询?就回答用ajax轮询这段
<body>
    <a href="javascript:WebStest();">运行</a>
</body>
<script>
    function WebStest(){
        if("WebSocket" in window){//先判断一下WebSocket在不在window里,返回值是布尔值,
            //打开一个WebSocket
            var ws=new WebSocket('ws://localhost:9998/echo');//url地址是接口文档给定的
            ws.onopen=function(){
                //open事件执行表示连接成功
                //WebSocket已连接上,使用send()方法传输数据
                ws.send("发送数据")//该数据是接口文档给定的
            }
            ws.onmessage=function(evt)//浏览器接收到服务器返回的数据后触发该事件
            {
                var received_msg=evt.data;
            };
                ws.onclose=function(){
                    //关闭websocket
                    alert("连接已关闭...")
                }
        }else{
            //浏览器不支持websocket
            alert("您的浏览器不支持WebSocket");
        }
    }
</script>

WebSocket使用场景(以下场景都适合用webSocket开发)

1、         社交订阅

2、         多玩家游戏

3、         协同编辑(例如腾讯文档编辑)

4、         股票基金报价

5、         体育实况更新

6、         多媒体聊天

7、         在线教育