24.2 进度事件
Progress Events是W3C的工作草案,定义了客户端-服务器端通信。
这些事件最初只针对XHR,现在也推广到了其他类似的API。
有以下6个进度相关的事件:
❑ loadstart:在接收到响应的第一个字节时触发。
❑ progress:在接收响应期间反复触发。
❑ error:在请求出错时触发。
❑ abort:在调用abort()终止连接时触发。
❑ load:在成功接收完响应时触发。
❑ loadend:在通信完成时,且在error、abort或load之后触发。
每次请求都会首先触发loadstart事件,之后是一个或多个progress事件,接着是error、abort或load中的一个,最后以loadend事件结束。
24.2.1 load事件
load事件用于替代readystatechange事件。
load事件在响应接收完成后立即触发,这样就不用检查readyState属性了。
onload事件处理程序会收到一个event对象,其target属性设置为XHR实例,在这个实例上可访问所有XHR对象属性和方法。
只要是从服务器收到响应,无论状态码是什么,都会触发load事件。
这意味着还需要检查status属性才能确定数据是否有效。
24.2.2 progress事件
progress事件,在浏览器接收数据期间,会反复触发。
每次触发时,onprogress事件处理程序都会收到event对象,其target属性是XHR对象,且包含3个额外属性:lengthComputable、position和totalSize。
其中,lengthComputable是一个布尔值,表示进度信息是否可用;
position是接收到的字节数;
totalSize是响应的Content-Length头部定义的总字节数。
有了这些信息,就可以给用户提供进度条了。
let xhr = new XMLHttpRequest();
xhr.onload = function(event) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert('请求不成功:' + xhr.status);
}
};
xhr.onprogress = function(event) {
let divStatus = document.getElementById('status');
if (event.lengthComputable) {
divStatus.innerHTML = '收到' + event.position + '的' + event.totalSize + '字节';
}
};
xhr.open('get', 'altevent.php', true);
xhr.send(null);
为了保证正确执行,必须在调用open()之前添加onprogress事件处理程序。
在前面的例子中,每次触发progress事件都会更新HTML元素中的信息。
假设响应有Content-Length头部,就可以利用这些信息计算出已经收到响应的百分比。
24.3 跨源资源共享
通过XHR进行Ajax通信的一个主要限制是跨源安全策略。
默认情况下,XHR只能访问与发起请求的页面在同一个域内的资源。
这个安全限制可以防止某些恶意行为。
不过,浏览器也需要支持合法跨源访问的能力。
跨源资源共享(CORS, Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信。
CORS背后的基本思路是:
使用自定义的HTTP头部允许浏览器和服务器相互了解,以确实请求或响应应该成功还是失败。
对于简单的请求,比如GET或POST请求,没有自定义头部,而且请求体是text/plain类型,这样的请求在发送时会有一个额外的头部叫Origin。
Origin头部包含发送请求的页面的源(协议、域名和端口),以便服务器确定是否为其提供响应。
下面是Origin头部的一个示例:
Origin: http://www.nczonline.net
如果服务器决定响应请求,那么应该发送Access-Control-Allow-Origin头部,包含相同的源;
或者如果资源是公开的,那么就包含”*”。
比如:
Access-Control-Allow-Origin: http://www.nczonline.net
如果没有这个头部,或者有但源不匹配,则表明不会响应浏览器请求。
否则,服务器就会处理这个请求。
注意,无论请求还是响应都不会包含cookie信息。
现代浏览器通过XMLHttpRequest对象原生支持CORS。在尝试访问不同源的资源时,这个行为会被自动触发。
要向不同域的源发送请求,可以使用标准XHR对象并给open()方法传入一个绝对URL
跨域XHR对象允许访问status和statusText属性,也允许同步请求。
出于安全考虑,跨域XHR对象也施加了一些额外限制:
❑ 不能使用setRequestHeader()设置自定义头部。
❑ 不能发送和接收cookie。
❑ getAllResponseHeaders()方法始终返回空字符串。
因为无论同域还是跨域请求都使用同一个接口,所以最好在访问本地资源时使用相对URL,在访问远程资源时使用绝对URL。
这样可以更明确地区分使用场景,同时避免出现 访问本地资源时出现头部或cookie信息访问受限的问题。
24.3.1 预检请求
CORS通过一种叫预检请求(prefighted request)的服务器验证机制,允许使用自定义头部、除GET和POST之外的方法,以及不同请求体内容类型。
在要发送涉及上述某种高级选项的请求时,会先向服务器发送一个“预检”请求。
这个请求使用OPTIONS方法发送并包含以下头部:
❑ Origin:与简单请求相同。
❑ Access-Control-Request-Method:请求希望使用的方法。
❑ Access-Control-Request-Headers:(可选)要使用的逗号分隔的自定义头部列表。
下面是一个假设的POST请求,包含自定义的NCZ头部:
Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ
24.3.2 凭据请求
默认情况下,跨源请求不提供凭据(cookie、HTTP认证和客户端SSL证书)。可通过将withCredentials属性设置为true,来表明请求会发送凭据。
如果服务器允许带凭据的请求,那么可以在响应中包含如下HTTP头部:
Access-Control-Allow-Credentials: true
如果发送了凭据请求而服务器返回的响应中没有这个头部,则浏览器不会把响应交给JavaScript(responseText是空字符串,status是0, onerror()被调用)。
注意,服务器也可以在预检请求的响应中发送这个HTTP头部,以表明这个源允许发送凭据请求。
24.4 替代性跨源技术
24.4.1 图片探测
图片探测是利用标签实现跨域通信的最早的一种技术。
任何页面都可以跨域加载图片而不必担心限制,因此这也是在线广告跟踪的主要方式。
可以动态创建图片,然后通过它们的onload和onerror事件处理程序得知何时收到响应。
这种动态创建图片的技术经常用于图片探测(image pings)。
图片探测是与服务器之间简单、跨域、单向的通信。
数据通过查询字符串发送,响应可以随意设置,不过一般是位图图片或值为204的状态码。
浏览器通过图片探测拿不到任何数据,但可以通过监听onload和onerror事件知道什么时候能接收到响应。
图片探测频繁用于跟踪用户在页面上的点击操作或动态显示广告。
当然,图片探测的缺点是:
只能发送GET请求和无法获取服务器响应的内容。
这也是只能利用图片探测实现浏览器与服务器单向通信的原因。
24.4.2 JSONP
JSONP是“JSON with padding”的简写,是在Web服务上流行的一种JSON变体。
JSONP看起来跟JSON一样,只是会被包在一个函数调用里。
比如:
callback({ "name": "Nick" });
JSONP格式包含两个部分:回调和数据。
回调是在页面接收到响应之后应该调用的函数,通常回调函数的名称是通过请求来动态指定的。
数据就是作为参数传给回调函数的JSON数据。
下面是一个典型的JSONP请求:
http://freegeoip.net/json/?callback=handleResponse
请求的URL是一个地理位置服务。
JSONP服务通常支持以查询字符串形式指定回调函数的名称。
比如这个例子就把回调函数的名字指定为handleResponse()。
JSONP调用是通过动态创建
