代码改变世界让前端飞程序员

前端初学者们都应该知道得前端通信方式

2018-01-28  本文已影响97人  darrell

一,什么是同源策略及其限制

  1. 概念:同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键安全机制。

  2. 什么是源:协议、域名与端口。这三者任何一个不一样的话,就算是跨域。

  3. 什么是限制:不是一个源的文档,没有权限去操作另一个源的文档。

Cookie、LocalStorage 和 IndexDB无法读取。

Dom无法获得

Ajax请求不能发送

常见跨域场景

URL                                      说明                    是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不同文件或路径           允许
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不同端口                不允许
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不同协议                不允许
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名对应相同ip              不允许
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不同                不允许
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不同域名                         不允许

二,前端端如何通信

  1. Ajax
  1. WebSocket(不受同源策略限制)
  1. CORS

三,如何创建ajax

  1. XMLHttpRequest对象的工作流程
  1. 兼容性处理:IE下面的兼容性处理
  1. 事件的触发条件:事件的触发动作
  1. 事件的触发顺序:每个事件的触发顺序

下面是个简单的实现ajax的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax</title>
</head>
<body>
    <div id="myDiv"></div>
    <script type="text/javascript">
        function ajax(url,method){
            var xmlhttp;  
            if (window.XMLHttpRequest){// 兼容 IE7+, Firefox, Chrome, Opera, Safari  
                xmlhttp=new XMLHttpRequest();  
            }  
            else{// 兼容 IE6, IE5  
                xmlhttp=newActiveXObject("Microsoft.XMLHTTP");  
            } 

            xmlhttp.open(method,url,true);  
            xmlhttp.send();


            xmlhttp.onreadystatechange=function(){  
                if(xmlhttp.readyState==4 &&xmlhttp.status==200){  
                    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;  
                }  
            } 
        }

        ajax(" http://www.easy-mock.com/mock/59ba1ac1e0dc663341a981b5/demo/list",'get');
    </script>
</body>
</html>

注意一:xmlhttp.readyState:一共有5中请求状态,从0 到 4 发生变化。**

0: 请求未初始化

1: 服务器连接已建立

2: 请求已接收

3: 请求处理中

4: 请求已完成,且响应已就绪

注意二:xmlhttp.status响应状态码。这个也是面试比较爱问的,这个必须知道4个以上,比较常见的有:

200 "OK"

403 (禁止) 服务器拒绝请求。

404 (未找到) 服务器找不到请求的网页。

408 (请求超时) 服务器等候请求时发生超时。

500 (服务器内部错误) 服务器遇到错误,无法完成请求。

注意三:xmlhttp.open方法open 的参数要牢记,很多面试官爱问这样的细节

method:请求的类型;GET 或 POST

url:文件在服务器上的位置

async:true(异步)或 false(同步)

注意四:post请求一定要设置请求头的格式内容

xmlhttp.open("POST","ajax_test.html",true);  
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");  
xmlhttp.send("fname=Henry&lname=Ford"); 

注意五:服务器响应处理

responseText 获得字符串形式的响应数据。

responseXML 获得XML 形式的响应数据。

知识介绍 : Ajax详细介绍

四,跨域通信的几种方式

通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

js原生实现方式

<script>
var script = document.createElement('script');
script.type = 'text/javascript';

script.src = 'https://api.asilu.com/geo/&callback=jsonp';//这个是获取当前经纬度的接口
document.head.appendChild(script);//创建并添加script标签到<head>下

// 回调执行函数
function jsonp(res) {
    console.log(res);//打印jsonp返回的信息
}
</script>

jq的Ajax实现方式

<script type="text/javascript">
    $.ajax({
        url: 'https://api.asilu.com/geo/',
        type: 'get',
        dataType: 'jsonp',  // 请求方式为jsonp
        success:function(ret){
            console.log(ret);
        }
    });
</script>

优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

缺点:它只支持GET请求;安全问题(请求代码中可能存在安全隐患);

hash改变,但是其的域名不改变。可以利用iframe。例子 ==> a与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。</font>

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题

  1. 页面和其打开的新窗口的数据传递

  2. 多窗口之间消息传递

  3. 页面与嵌套的iframe消息传递

  4. 上面三个场景的跨域数据传递

在这里你需要通过集成管理工具配置两个不同的域,fjw.me下的b页面,demo.me下面的a页面。(如下图)

image
//A.HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>b.html</title>
</head>
<body>
    <h1>b.html</h1>
    <iframe id="iframe" src="http://fjw.me/"></iframe>

    <script type="text/javascript">
        var iframe = document.getElementById('iframe');

        iframe.onload = function() {
            var data = {
                name: 'aym'
            };
            // 向fjw.me传送跨域数据,通过postMessage发送数据
            iframe.contentWindow.postMessage(JSON.stringify(data), 'http://fjw.me/index.html');
        };

        // 接受fjw.me返回数据
        window.addEventListener('message', function(e) {
            alert('data from domain2 ---> ' + e.data);
        }, false);
    </script>   
</body>
</html>
//B.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    <div>123456</div>
    <script type="text/javascript">
        // 接收demo.me的数据
        window.addEventListener('message', function(e) {
            alert('data from domain1 ---> ' + e.data);

            var data = JSON.parse(e.data);

            if (data) {
                data.number = 16;
                // 处理后再发回demo.me
                window.parent.postMessage(JSON.stringify(data), 'http://demo.me/b.html');
            }
        }, false);
    </script>
</body>
</html>

结果如图:

image image

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

例子如下,本例子是参考官方的一个简易聊天室,地址是http://socket.io/get-started/chat/,目录结构如下图所示。

image
//服务器的后台node.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  }); //监听socket是否连接,如果连接的话,就发送数据,chat-message
  socket.on('disconnect', function(){
    console.log('user disconnected');
  }); //监听socket是否断开,断开时执行相应的方法
});

http.listen(port, function(){
  console.log('listening on *:' + port);
});
//前端客户端
<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
      #messages { margin-bottom: 40px }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
    <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
    <script src="https://code.jquery.com/jquery-1.11.1.js"></script>
    <script>
      $(function () {
        var socket = io();//建立一个socket
        $('form').submit(function(){
          socket.emit('chat message', $('#m').val());//发送socket到服务器。
          $('#m').val('');
          return false;
        });
        socket.on('chat message', function(msg){
          $('#messages').append($('<li>').text(msg));
          window.scrollTo(0, document.body.scrollHeight);
          //接受服务器传回来的数据,新建一个li标签
        });
      });
    </script>
  </body>
</html>

具体效果如下图所示,可以在两个浏览器之间通信。

image

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。参考阮一峰老师的这一篇CORS的文章

文章的地址:跨域资源共享 CORS 详解

5.1, CORS的通信过程

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

5,2, CORS的浏览器限制

目前,所有浏览器都支持该功能,IE浏览器不能低于IE10

5.3, 具体流程

第一步:浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

代码如下:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。如下所示:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

意思分别是:

5.3.1,Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

5.3.2,Access-Control-Allow-Credentials

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

5.3.3,Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

还有我们说到上面说到,CORS请求默认不发送CookieHTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

前端也要设置:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;//这个要开起来

5.4, 优点

CORS与JSONP相比,无疑更为先进、方便和可靠。

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  1. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  1. JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

文章参考

  1. 前端常见跨域解决方案(全)
  1. Ajax 是什么? 如何创建一个Ajax?
  1. Ajax 知识体系大梳理
  1. 跨域资源共享 CORS 详解
上一篇下一篇

猜你喜欢

热点阅读