前端通信进阶
在几年前,天空一声巨响,ajax 闪亮登场. 前端宝宝们如获至宝~ 已经表单提交神马的,真的太 心累了. 有了ajax之后,网页的性能可大幅提升,告别刷新,告别如水的流量. 不过,长江后浪推前浪,一代更比一代强. 由于ajax被同域限制着,导致,多服务器配置,云服务资源的存储 没办法充分利用. 所以,业界想到另外一种方法--JSONP. JSONP实际上和ajax没有半点关系,唯一相同的就是都是异步执行,而且JSONP完美解决了CD(cross domain)问题.
ok~ 进入主题吧~ AJAX相信这个应该不用过多的讲解了吧.
这里,我就直接上代码了. var sendAjax = (function() { var getXHR = (function() { var xhr; if(window.XHRHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveObject("Microsoft.XMLHTTP"); } return xhr; })(); return function(url,opts){ //url为目标地址 var xhr = getXHR(),data; xhr.onreadystatechange = function(){ if(xhr.readyState===4||xhr.status===200){ data = JSON.parse(xhr.responseText); //将data解析为json对象 opts.callback(data); } } xhr.setRequestHeader('Content-Type','application/json'); xhr.open(opts.method,url); //写入参数 xhr.send(JSON.stringify(opts.data)); //将参数json字符化 } })(); //调用执行 sendAjax('www.example.com',{ callback:function(data){ //... },data:{ name:'JIMMY',age:18 } }) 这样差不多就完成了一个ajax的简单模型。当然,我们也可以使用jquery提供的$.ajax函数,只是他里面做了更多的兼容性和功能性. JSONPJSONP 就是 JSON with Padding... 我真的不知道这个名字的含义到时有什么卵用... JSONP原理JSONP 其实是和< script> 标签 有很大的关系. JSONP最大的优势就是实现异步跨域的作用,他到底是怎么做到的呢?
<script> function processJSON (json) { // Do something with the JSON response }; </script> <script src='http://www.girls.hustonline.net? callback=processJSON&name=jimmy&age=18'></script> 上面的写法有点不符合前端风味. 说明一下,其实processJSON,其实就相当于一个回调函数而已. 在script--src里面的内容我们来瞧一瞧. 使用jsoncallback 来指定回调函数名字,并且传入一些参数
这就是前端发送JSONP的全部. 那应该怎么执行呢?或者说,返回的内容是什么呢? processJSON({ message:"I've already received" }); 然后,浏览器收到后,直接执行即可. 这里,我们来模拟一下服务器端盖怎样执行一个JSONP的函数. const util = require('util'),http = require('http'),url = require('url'); let data = JSON.stringify({ message:"I've already received" }); http.createServer(function(req,res) { req = url.parse(req.url,true); if (!req.query.callback) res.end(); console.log(`name is ${req.query.name} and his age is ${req.query.age}`); res.writeHead(200,{ 'Content-Type': 'application/javascript' }) res.end(req.query.callback + "('" + data + "')") }).listen(80) ok~ 上面基本上就可以完成一个简单的JSONP函数执行。 当然,express 4.x 里面也有相关的JSONP 操作。 有兴趣的同学可以看一看. var sendJSONP = function(url,callbackName){ var script = docuemnt.createELement('script'); script.src = `${url}&callback=${callbackName}`; document.head.appendChild(script); } var sayName = function(name){ console.log(`your name is ${name}`); } sendJSONP('http://girls.hustonline.net?name=jimmy','sayName'); 上面就是一个精简版的JSONP了。 另外,也推荐使用jquery的getJSON和$.ajax进行请求. $.getJSON("http://girls.hustonline.net?callback=?",function(result){ console.log(result); }); 这里,我们需要关注一下url里面中 $.ajax({ url: 'http://girls.hustonline.net?name=jimmy',dataType: 'jsonp',success: function(name){ console.log(name); } }); 这样,我们就可以利用jquery很简单的发送jsonp了. SSEajax和JSONP 都是 client-fetch的操作. 但是有时候,我们更需要服务器主动给我们发信息. 比如,现在的APP应用,完全可以实现服务器发送,然后Client再处理. 而,SSE就是帮助我们向webapp靠近.
简单demo先看一个client端,一个比较简单的demo var source = new EventSource('/dates'); //指定路由发送 source.onmessage = function(e) { //监听信息的传输 var data = JSON.parse(e.data),origin = e.origin; }; source.onerror = function(e) { //当连接发生error时触发 console.log(e); }; source.onopen = function(e) { //当连接正式建立时触发 console.log(e); }; SSE主要就是创建一个EventSource对象. 里面的参数就是发送的路由,不过目前还不支持CORS,所以也被限制在同源策略下.
上面几个方法比较重要的还是message方法. message主要用来进行信息的接受,回调中的event 包含了返回的相关数据.
服务器返回数据格式上文说过,SSE 是以event-stream格式进行传输的. 但具体内容是怎样的呢? data: hi data: second event id: 100 event: myevent data: third event id: 101 : this is a comment data: fourth event data: fourth event continue 上面就是一个简单的demo. 每一段数据我们称之为事件,每一个事件经过空行分隔.
其实上面最重要的两个字段就是data,id. 所以,我们一般获取的话就可以使用 res.write("id: " + i + "n"); res.write("data: " + i + "nn"); 通过使用'nn'进行两次换行操作--即,产生空行即可. 使用自定义事件服务器端不仅可以返回指定数据,还可以返回指定事件.不过默认情况下都是 event: myevent data: third event id: 101 这里出发的就是 myevent事件。 即,这就是触发自定义事件的方式. var source = new EventSource('/someEvents'); source.addEventListener('myevent',function(event){ //doSth },false); 服务端使用SSE由于使用的是HTTP协议,所以对于服务端基本上没什么太大的改变. 唯一注意的就是,发送数据使用res.write()即可,断开的时候使用res.end(); res.writeHead(200,{ "Content-Type": "text/event-stream","Cache-Control": "no-cache","Access-Control-Allow-Origin": "*" //允许跨域 }); var num =0; var f = function(){ if(num===10){ res.end(); }else{ res.write("id: " + num + "n"); res.write("data: " + num + "nn"); num++; } setTimeout(f,1000); } f(); Ok~ 这里有一个demo,大家可以打开控制台看一下. 会发现,有一个连接一直处于Content-Download状态. 该连接就是一个SSE。 websocketwebsocket 不同于其他的HTTP协议,他是独立于HTTP存在的另外一种通信协议。比如,像这样的一个路径
我们来看一个简单的websocket demo var socket = new WebSocket('ws://localhost:8080/'); socket.onopen = function () { console.log('Connected!'); }; socket.onmessage = function (event) { console.log('Received data: ' + event.data); socket.close(); }; socket.onclose = function () { console.log('Lost connection!'); }; socket.onerror = function () { console.log('Error!'); }; socket.send('hello,world!'); 可以说上面就是一个健全的websocket 通信了. 和SSE一样,我们需要创建一个WebSocket对象,里面的参数指定连接的路由. 而且,他也是事件驱动的.
websocket 发送数据另外,websocket 最大的特点就是可以双向通信。这里可以使用. socket.send("Hello server!"); socket.send(JSON.stringify({'msg': 'payload'})); var buffer = new ArrayBuffer(128); socket.send(buffer); var intview = new Uint32Array(buffer); socket.send(intview); var blob = new Blob([buffer]); socket.send(blob); 另外还可以使用binaryType指定传输的数据格式,不过一般都用不上,就不说了. websocket 接受数据同理,和SSE差不多,通过监听message事件,来接受server发送回来的数据. 接受其实就是通过 ws.onmessage = function(msg) { if(msg.data instanceof Blob) { processBlob(msg.data); } else { processText(JSON.parse(msg.data)); //接受JSON数据 } } 那server端应该怎样处理websocket通信呢? NodeJS 发送websocket数据简单的websocket demo var WebSocketServer = require('ws').Server,wss = new WebSocketServer({ port: 8080 }); //通过ws+ssl的方式通信. 和HTTPS类似 wss.on('connection',function connection(ws) { ws.on('message',function incoming(message) { console.log('received: %s',message); }); ws.send('something'); }); 可以参考treeHouse 编写的WSdemo 为什么websocket会有子协议由于websocket 本身的协议对于数据格式来说,不是特别的清晰明了,ws可以传输text,blob,binary等等其他格式. 这样对于安全性和开发性能来说,友好度很低。所以,为了解决这个问题,subprotocols 出现了. 在使用时,client和server都需要配置一样的subprotocols. 例如: var ws = new WebSocket('wss://example.com/socket',['appProtocol','appProtocol-v2']); 服务端需要将subprotocols发送过去,在handshakes的过程中,server 会识别subprotocols. 如果,server端也有相同的子协议存在,那么连接成功. 如果不存在则会触发error,连接就被断开了. websocket 协议内容websocket 是有HyBi Working Group 提议并创建的。 主要的内容就是 一张表.
具体内容是:
websocket 能否跨域?首先,答案是。 但,网上有两部分内容:
看到这里我也是醉了. 事实上websocket 是可以跨域的。 但是为了安全起见,我们通常利用CORS 进行 域名保护. how does CORS workCORS 是Cross-Origin Resource Sharing--跨域资源分享. CORS 是W3C 规范中 一项很重要的spec. 一开始,ajax 收到 the same origin policy 的限制 奈何不得。 结果出来了JSONP 等 阿猫阿狗. 这让ajax很不安呀~ 但是,W3C 大手一挥,亲,我给你开个buff. 结果CORS 就出来了。 function createCORSRequest(method,url) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // 检查xhr是否含有withCredentials属性 //withCredentials 只存在于XHR2对象中. xhr.open(method,url,true); } else if (typeof XDomainRequest != "undefined") { // 检查是否是IE,并且使用IE的XDomainRequest xhr = new XDomainRequest(); xhr.open(method,url); } else { // 否则..基本上就不能跨域了 xhr = null; } return xhr; } 然后,就可以直接,xhr.send(body). 那CORS其实就完成了. CORS中的withCredentials该属性就是用来表明,你的request的时候,是否带上你的cookie. 默认情况下是不带的. 如果你要发送cookie给server的话,就需要将withCredentials设置为true了. CORS 的preflight 验证CORS的preflight request,应该算是CORS中里面 巨坑的一个。 因为在使用CORS 的时候, 有时候我命名只发送一次请求,但是,结果出来了两个。 有时候又只有一个,这时候,我就想问,还有谁能不懵逼.
比如,我使用上面定义好的函数createCORSRequest. 来发送一个简单请求 var url = 'http://example.com/cors'; var xhr = createCORSRequest('GET',url); xhr.send(); 我们来看一下,只发送一次简单请求时,请求头和相应头各是什么.(剔除无关的Headers) //Request Headers POST HTTP/1.1 Origin: http://example.com Host: api.bob.com //Response Headers Access-Control-Allow-Origin: http://example.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Vary Content-Type: text/html; charset=utf-8 上面就是一个简单的CORS 头的交互。 另外,说明一个
如果你想暴露更多的头给用户的话就可以使用, var url = 'http://example.com/cors'; var xhr = createCORSRequest('POST',url); xhr.setRequestHeader('Content-Type','application/json'); xhr.send(); 第一次,我们通常叫做preflight req. 他其实并没有发送任何 data过去. 只是将本次需要发送的请求头发送过去,用来验证该次CORS请求是否有效. OPTIONS HTTP/1.1 Origin: http://example.com Content-Type: application/json Access-Control-Request-Method: POST Access-Control-Request-Headers: Custom-Header Host: api.alice.com Accept-Language: en-US Connection: keep-alive
//Response Header <= HTTP/1.1 204 No Content Access-Control-Allow-Methods: GET,POST,PUT,DELETE Access-Control-Max-Age: 86400 Access-Control-Allow-Headers: Custom-Header Access-Control-Allow-Origin: http://foo.com Content-Length: 0 说明一下里面的头
这里,主要要看一下 => POST - HEADERS - Origin: http://example.com Access-Control-Request-Method: POST Content-Type: application/json; charset=UTF-8 <= HTTP/1.1 200 OK - RESPONSE HEADERS - Access-Control-Allow-Origin: http://example.com Content-Type: application/json Content-Length: 58 ok~ 发展图谱不多说了,上图~ fetch 补充fetch 相关补充,可以查阅览 前端 fetch 通信 转载请注明作者和原文地址:https://segmentfault.com/a/11... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |