【红宝书】第21章Ajax与Comet
AJAX是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
1.XMLHttpRequest对象
1.1XHR的用法
let xhr = new XMLHttpRequest()
open()方法不会真正发送请求,而只是启动一个请求以备发送,接收3个参数:要发送的请求类型、请求URL、是否异步发送请求
xhr.open("get","example.php",false)
发送特定请求,调用send()方法。接收1个参数,即要作为请求主体发送的数据。若没有数据则必须传入null。调用send()之后,请求就会被分派的服务器。
xhr.send(null)
若请求是同步的,JS代码就会等到服务器响应之后再继续执行。
在收到响应后,响应的数据会自动填充XHR对象的属性,属性如下
1.responseText:作为响应主题被放回的文本
2.responseXML:如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着相应数据的XML DOM文档。
3.status:响应的HTTP状态
4.statusText:HTTP状态的说明
接收响应先检查status属性判断是够成功。一般将200作为成功标志。此时responseText属性内容已就绪,若内容类型正确responseXML也能访问。状态码304表示请求的资源并没有被修改,可以直接使用浏览器中缓存的版本。
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("request was unsuccessful:" + xhr.status)
}
多数情况发送异步请求,才能让JS继续执行而不必等待响应。此时可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段。可取值如下
0:未初始化,尚未调用open()方法
1:启动。已调用open(),为调用send()
2:发送。已调用send(),但尚未接收到响应
3:接受。已经接收到部分响应数据。
4:完成。已经接收到全部响应数据,而且可以在客户端使用。
主要readyState属性的值有一个值变换一个值,就会触发一次readystatechange事件。可以利用该事件检测每次状态变化后的readyState值。必须在调用open前指定该事件才能确保跨浏览器兼容性
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("request was unsuccessful:" + xhr.status)
}
}
}
xhr.open("get", "examlpe.text", false)
xhr.send(null)
在接受到响应内容之前可以调用absort()方法取消异步请求:
xhr.abort()
调用该方法XHR对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求后,还应该对XHR对象进行解引用操作。由于内存原因,不建议重用XHR对象。
1.2HTTP头部信息
每个HTTP请求和响应都会带有相应的头部信息。
默认情况发送XHR请求的同时会发送下列头部信息
Accept:浏览器能够处理的内容类型
Accept-Charset:浏览器能够显示的字符集
Accept-Encoding:浏览器能够处理的压缩编码
Accpet-Language:浏览器当前设置的语言
Connection:浏览器与服务器之间连接的类型
Cookie:当前页面设置的任何Cookie
Host:发出请求的页面所在的域
Referer:发出请求的页面URL
User-Agent:浏览器的用户代理字符串
可以使用setRequestHeader(),在open()之后send()之前调用
xhr.open("get", "examlpe.text", false)
xhr.setRequestHeader("MyHeader","MyValue")
xhr.send(null)
可以通过调用XHR对象的getResponseHeaders()方法传入头部字段名称可以取得相应的响应头部信息。
调用getAllResponseHeaders()取得所有头部信息的长字符串
let myHeader = xhr.getResponseHeader("MyHeader")
let allHeaders = xhr.getAllResponseHeaders()
1.3get请求
常用于向服务器查询某些信息。可以将查询字符串参数追加到URL末尾。
查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,所有名-值对都必须有和号&分隔
function addURLParam(url, name, value) {
url += url.indexOf('?') == -1 ? '?' : '&'
url += encodeURIComponent(name) + "=" + encodeURIComponent(value)
return url
}
let url = "examlpe.php"
url =addURLParam(url,"name","lpr")
xhr.open("get",url,false)
1.4post请求
常用于向服务器发送应该被保存的数据。服务器对post请求和提交Web的请求并不会一视同仁。
使用XHR模仿表单提交,要将Content-Type信息设置为application/x-www-form-urlencoded
xhr.open("post","examlpe.php",false)
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
let form = document.getElementById("user-info")
xhr.send(serialize(form)) //post数据的格式与查询字符串格式相同,如果需要将页面中表单的数据进行序列化然后在通过XHR发送到服务器,使用serialize()函数来创建这个字符串
2.XMLHttpRequest2级
XMLHttpRequest1级只是把已有的XHR对象的实现细节描述出来,而XHLHttpRequest2级则进一步发展XHR。
2.1FormData
因现代Web频繁使用表单数据序列化的功能,XMLHttpRequest2级为此定义了FormData类型。为序列化表单以及创建与表单格式相同的数据提供便利
let data = new FormData()
data.append("name","value")
xhr.send(data)
或
let data = new FormData(document.forms[0])
2.2超时设定
XHR对象timeout属性,表示请求在等待响应多少毫秒之后就会终止。设置数值后如果在规定时间内浏览器还没有接收到响应,就会触发timeout事件,进而调用ontimeout事件处理程序。
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("request was unsuccessful:" + xhr.status)
}
}
}
xhr.open("get", "examlpe.text", false)
xhr.timeout = 1000
xhr.ontimeout = function(){
alert("Request did not return in a second")
}
xhr.send(null)
如果在超时终止请求之后在访问status属性就会导致错误。请求终止后readyState可能已经变成4,意味着会调用onreadystatechange事件处理程序。
为避免浏览器报告错误可以将检查status属性的语句封装在try-catch中。
2.3overrideMimeType()方法
重写服务器返回的MIME类型。比如服务器返回的MIME类型是text/plain,但数据中实际包含是XML。根据MIME类型即使数据是XML,responseXML属性仍然是null,通过该方法可以保证把响应当作XML而非纯文本来处理。
xhr.open("get", "text.php", false)
xhr.overrideMimeType("text/xml")
xhr.send(null)
3.进度事件
定义了与客户端服务器通信有关的事件。有以下6个进度事件
loadstart:在接收到响应数据的第一个字节时触发
progress:在接受响应期间持续不断地触发
error:在请求发生错误时触发
abort:在因为调用abort()方法而终止链接时触发。
load:在接受到完整响应数据时触发。
loadend:在通信完成或触发error、abort或load事件后触发。
每个请求都会触发loadstart,然后是一个或多个progress,然后触发error、abort或load事件中的一个,最后触发loadend事件结束。
3.1load事件
用以代替readystatechange事件。响应接收完毕后触发load事件,因此没有必要去检查readyState属性了。而onload事件处理程序会接收一个event对象,其target属性指向XHR对象实例,因为可以访问到XHR对象的所有方法和属性。然后并非所有浏览器都为这个事件实现了适当的事件对象。
let xhr = createXHR()
xhr.onload= function(){
if((xhr.status>=2&&xhr.status<300)||xhr.status==304){
alert(xhr.responseText)
}else{
alert("request was unsuccessful:" + xhr.status)
}
}
xhr.open(method,url,true)
xhr.send(null)
3.2progress事件
progress事件会在浏览器接收新数据期间周期性的触发。onprogress事件处理程序会接收到一个event,其包含的属性有
target:XHR对象
lengthComputable:表示进度信息是够可用的布尔值
position:表示已接受的字节数
totalSize:根据Content-Length相应头部确定的预期字节数。
有了这些信息我们可以为用户创建一个进度指示器
let xhr = createXHR()
xhr.onload = function () {
if ((xhr.status >= 2 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText)
} else {
alert("request was unsuccessful:" + xhr.status)
}
}
xhr.onprogress = function (event) {
let divStatus = document.getElementById("status")
if (event.lengthComputable) {
divStatus.innerHTML = "Received" + event.position + "of" + event.totalSize + "bytes"
}
}
xhr.open(method, url, true)
xhr.send(null)
4.跨域源资源共享
跨域安全策略预防恶意行为。但是实现合理的跨域请求也是至关重要的。
CORS(Cross-Origin Resource Sharing,跨域源资源共享)定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应的成功和失败。
在一个请求发送时附加额外的Origin头部,其中包含请求页面的源信息(协议、域名、和端口),以便服务器根据这个头部信息来决定是否给予响应。
Origin:http://www.xxxx.com
如果服务器接收,就在Accss-Control-Allow-Origin头部中回发相同的源信息,如果是公共资源可以回发*
Access-Control-Allow-Origin:http://www.xxxx.com
1.IE实现
2.在Firefox、Safari、Chrome等浏览器对CORS的实现
在open()方法中传入绝对URL即可
xhr.open(method, 'http://wwww.xxxxx.com/page', true)
3.Preflighted Reqeust
4.带凭据的请求
默认情况跨域请求不提供凭据(cookie、Http认证及科技护短SSL证明等)。通过将withCredentials属性设为true,可以指定某个请求应该发送凭据。吐过服务器接收带凭据的请求会用一下HTTP头部来响应
Access-Control-Allow-Credentials:true
5.跨浏览器
5.其他跨域技术
1.图像Ping
2.JSONP
优:1.能够直接访问响应文本2.支持在浏览器与服务器之间双向通信
劣:1.不安全,从其他域中加载代码可能在响应中夹带一些恶意代码2.确定JSONP请求是否失败不容易
3.comet
服务器向页面推送数据,比如处理体育比赛的分数和股票报价
有两种方式:长轮询和流
长轮询
长轮询是传统轮询的翻版,即浏览器定时向服务器发送请求,看有没有更新的数据。
长轮询是页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发。发送完数据之后,浏览器关闭连接,随机又发起一个到服务器的心情求。这一过程在页面打开期间一直持续不断。
无论是短轮询还是长轮询都是先发起对服务器的请求,然后再接收响应。短轮询是立即发送响应,无论数据是否有效。长轮询是等待发送响应,通过XHR和setTimeout()就能实现。
HTTP流
在页面整个生命周期内只使用HTTP连接,就是浏览器像服务器发送请求,而服务器保持连接打开,然后周期性的向浏览器发送数据。
4.SSE
服务器发送事件是围绕只读comet交互退出的API或模式
5.Web Sockets
目标是在一个单独的持久连接上提供全双工、双向通信。无法使用标准的HTTP服务器实现Web Sockets。
Web Socketst自定义协议,未加密的连接是ws://,加密的是wss://
使用自定义协议的好处:
1.能够在客户端和服务器之间发送非常少量的数据而不必担心HTTP那样字节集的开销
2.传递的数据报很小,适合移动应用。
使用自定义协议的缺点:
1.指定协议的事件比置顶JS API的事件还要长。
2.可能存在一致性和安全性的问题
Web Sockets API
let socket = new WebSocket("ws://www.example.com/server.php") // 必须传入绝对的URL
实例化websocket对象后,浏览器就会创建连接。
其也有一个readyState属性
WebSocket.OPENING:正在建立连接
WebSocket.OPEN:已经建立连接
WebSocket.CLOSING:正在关闭连接
WebSocket.CLOSE:已经关闭连接
关闭websocket连接
socket.close()
发送数据,只能发送纯文本数据,复杂数据要序列化
let socket = new WebSocket("ws://www.example.com/server.php")
socket.send('msg')
let obj = {}
socket.send(JSON.stringify(obj))
当服务器向客户端发来消息时,就会触发message事件,把返回的数据保存在event.data中
socket.onmessage = function (event) {
let data = event.data
}
还有以下事件
open:在成功建立连接时触发
error:在发生错误时触发,连接不能持续
close:在连接关闭时触发
socket.onopen = function () {
alert("Connection established.")
}
socket.onerror = function () {
alert("Connection error")
}
socket.onclose = function (event) {
event.wasClean 布尔值,表示连接是否明确关闭
event.code 服务器返回的数值状态码
event.reason 字符串,服务器发回的消息
alert("Connection closed")
}
SSE和WebSockets的使用
使用哪个技术考虑下以下因素
1.是否有自由度建立和维护Web Sockets服务器。(SSE是通过常规HTTP通信)
2.到底需不需要双向通信。如果用例只需读取服务器数据(如比赛成绩),那么SSE;如果用例必须双向通信(如聊天室),那么websocket。
注:组合XHR和SSE也能实现双向通信。
安全
CSRF跨站点请求伪造:未授权系统有权访问某个资源的情况。
确保通过XHR访问的URL安全,通常做法就是验证发送请求者是否有权访问相应的资源。
1.要求以SSL连接来访问可以通过XHR请求的资源
2.要求每一次请求都附带经过相应算法计算得到的验证
下列措施对CSRF无效
1.要求发送post而不是get请求--很容易改变
2.检查来源URL以确定是否可行--来源记录很容易伪造
3.基于cookie信息进行验证--同样很容易伪造
同源政策是对XHR的主要约束,它为通信设置了"相同的域相同的端口相同的协议"这一限制。试图访问上述限制之外的资源,都会引发安全错误,除非采用被认可的跨域解决方案CORS。

浙公网安备 33010602011771号