24.6 Beacon API
为了把尽量多的页面信息传到服务器,很多分析工具需要在页面生命周期中尽量晚的时候,向服务器发送遥测或分析数据。
因此,理想的情况下是通过浏览器的unload事件发送网络请求。这个事件表示用户要离开当前页面,不会再生成别的有用信息了。在unload事件触发时,分析工具要停止收集信息并把收集到的数据发给服务器。
这时有一个问题:unload事件对浏览器意味着,没有理由再发送任何结果未知的网络请求(因为页面都要被销毁了)。
例如,在unload事件处理程序中创建的任何异步请求,都会被浏览器取消。
为此,异步XMLHttpRequest或fetch()不适合这个任务。
分析工具可用同步XMLHttpRequest强制发送请求,但这样做会导致用户体验问题。
浏览器会因为要等待unload事件处理程序完成而延迟导航到下一个页面。
为解决这个问题,W3C引入了补充性的Beacon API。
这个API给navigator对象增加了一个sendBeacon()方法。
这个方法接收一个URL和一个数据有效载荷参数,并会发送一个POST请求。
可选的数据有效载荷参数有ArrayBufferView、Blob、DOMString、FormData实例。
如果请求成功进入了最终要发送的任务队列,则这个方法返回true,否则返回false。
这个方法虽然看起来只不过是POST请求的一个语法糖,但它有几个重要的特性:
❑ sendBeacon()并不是只能在页面生命周期末尾使用,而是任何时候都可以使用。
❑ 调用sendBeacon()后,浏览器会把请求添加到一个内部的请求队列。浏览器会主动地发送队列中的请求。
❑ 浏览器保证在原始页面已经关闭的情况下也会发送请求。
❑ 状态码、超时和其他网络原因造成的失败完全是不透明的,不能通过编程方式处理。
❑ 信标(beacon)请求会携带调用sendBeacon()时所有相关的cookie。
24.7 Web Socket
Web Socket(套接字)的目标:
通过一个长时连接实现与服务器全双工、双向的通信。
在JavaScript中创建Web Socket时,一个HTTP请求会发送到服务器以初始化连接。服务器响应后,连接使用HTTP的Upgrade头部从HTTP协议切换到Web Socket协议。这意味着Web Socket不能通过标准HTTP服务器实现,而必须使用支持该协议的专有服务器。
因为Web Socket使用了自定义协议,所以URL方案(scheme)稍有变化:
不能再使用http://或https://,而要使用ws://和wss://。
前者是不安全的连接,后者是安全连接。
在指定Web Socket URL时,必须包含URL方案,因为将来有可能再支持其他方案。
使用自定义协议(而非HTTP协议)的好处:
客户端与服务器之间可以发送非常少的数据,不会对HTTP造成任何负担。
使用更小的数据包,让Web Socket非常适合 带宽和延迟问题比较明显的移动应用。
使用自定义协议的缺点:
定义协议的时间比定义JavaScript API要长。
Web Socket得到了所有主流浏览器支持。
24.7.1 API
要创建一个新的Web Socket,需要实例化一个WebSocket对象,并传入提供连接的URL
let socket = new WebSocket('ws://www.example.com/server.php');
注:必须给WebSocket构造函数传入一个绝对URL。
同源策略不适用于Web Socket,因此可以打开到任意站点的连接。
至于是否与来自特定源的页面通信,则完全取决于服务器。(在握手阶段就可以确定请求来自哪里。)
浏览器会在初始化WebSocket对象之后立即创建连接。
与XHR类似,WebSocket也有一个readyState属性表示当前状态,但这个值与XHR中相应的值不一样:
❑ WebSocket.OPENING(0):连接正在建立。
❑ WebSocket.OPEN(1):连接已经建立。
❑ WebSocket.CLOSING(2):连接正在关闭。
❑ WebSocket.CLOSE(3):连接已经关闭。
WebSocket对象没有readystatechange事件,而是有与上述不同状态对应的其他事件。
readyState值从0开始。
任何时候都可以调用close()方法关闭Web Socket连接。
socket.close();
调用close()之后,readyState立即变为2(连接正在关闭),并会在关闭后变为3(连接已经关闭)。
24.7.2 发送和接收数据
打开Web Socket后,可通过连接发送和接收数据。
要向服务器发送数据,使用send()方法并传入一个字符串、ArrayBuffer或Blob
服务器向客户端发送消息时,WebSocket对象上会触发message事件。
这个message事件与其他消息协议类似,可以通过event.data属性访问到有效载荷.
24.7.3 其他事件
WebSocket对象在连接生命周期中有可能触发3个其他事件:
❑ open:在连接成功建立时触发。
❑ error:在发生错误时触发。连接无法存续。
❑ close:在连接关闭时触发。
WebSocket对象不支持DOM Level 2事件监听器,因此需要使用DOM Level0风格的事件处理程序来监听这些事件
24.8 安全
大规模Ajax应用程序需要考虑的安全问题非常多,但在通用层面上一般需要考虑以下几个问题:
首先,任何Ajax可以访问的URL,也可以通过浏览器或服务器访问。
例如下面这个URL:
/getuserinfo.php?id=23
请求这个URL,访问者可将23改为24或56,甚至其他任何值。
getuserinfo.php文件必须知道访问者是否拥有访问相应数据的权限。否则,服务器就会大门敞开,泄露所有用户的信息。
在未授权系统可以访问某个资源时,可以将其视为跨站点请求伪造(CSRF,cross-site request forgery)攻击。
未授权系统会按照处理请求的服务器的要求伪装自己。
Ajax应用程序,无论大小,都会受到CSRF攻击的影响,包括:
无害的漏洞验证攻击,和恶意的数据盗窃或数据破坏攻击。
关于安全防护Ajax相关URL的一般理论认为:需要验证请求发送者拥有对资源的访问权限。可通过如下方式实现:
❑ 要求通过SSL访问能够被Ajax访问的资源。
❑ 要求每个请求都发送一个按约定算法计算好的令牌(token)。
注:以下手段对防护CSRF攻击是无效的:
❑ 要求POST而非GET请求(很容易修改请求方法)。
❑ 使用来源URL验证来源(来源URL很容易伪造)。
❑ 基于cookie验证(同样很容易伪造)。
