【红宝书】第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。

 

  

  

posted @ 2020-01-16 11:18  把我当做一棵树叭  阅读(161)  评论(0)    收藏  举报