资讯专栏INFORMATION COLUMN

高程3总结#第21章Ajax与Comet

MudOnTire / 359人阅读

摘要:页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。

Ajax与Comet XMLHttpRequest对象

IE5是第一款引入XHR对象的浏览器,在IE5中,XHR对象是通过MSXML库中的一个ActiveX对象实现的

//适用于 IE7 之前的版本
function createXHR(){
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}

这个函数会尽力根据IE中可用的MSXML库的情况创建最新版本的XHR对象

IE7+、Firefox、Opera、Chrome、Safari都支持原生的XHR对象,这些浏览器中创建XHR对象,可以使用XMLHttpRequest构造函数

var xhr=new XMLHttpRequest();

如果还必须要支持IE的更早版本,可以在createHXR()函数中加入对原生XHR对象的支持

function createXHR(){
if (typeof XMLHttpRequest != "undefined"){
  return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){
  if (typeof arguments.callee.activeXString != "string"){
    var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
                    "MSXML2.XMLHttp"],
        i, len;
    for (i=0,len=versions.length; i < len; i++){
      try {
        new ActiveXObject(versions[i]);
        arguments.callee.activeXString = versions[i];
        break;
      } catch (ex){
        //跳过
      }
    }
  }
  return new ActiveXObject(arguments.callee.activeXString);
} else {
  throw new Error("No XHR object available.");
}
}

这个函数中新增的代码首先检测原生XHR对象是否存在,如果存在则返回它的新实例,如果原生对象不存在,则检测ActiveX对象,如果这两种对象都不存在,就抛出一个错误,然后就可以使用下面的代码在所有浏览器中创建XHR对象了

var xhr=createXHR();

XHR的用法

在使用XHR对象时,要调用的第一个方法是open(),接收3个参数:要发送的请求的类型(get或者post)、请求的URL和表示是否异步发送请求的布尔值

xhr.open("get","example.php",false);

这行代码会启动一个针对example.php的GET请求

URL相对于执行代码的当前页面

open()方法并不会真正发送请求,而是启动一个请求以备发送

要想发送特定的请求,调用send()方法

xhr.open("get","example.txt",false);
xhr.send(null);

send()方法接收一个参数,要作为请求主体发送的数据,如果不需要通过请求主体发送数据,必须传入null

响应的数据会自动填充XHR对象的属性

responseText,作为响应主体被返回的文本

responseXML,如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着响应数据的XML DOM文档

status,响应的HTTP状态

statusText,HTTP状态的说明

XHR对象的readyState属性表示请求响应过程的当前活动阶段

0,未初始化,尚未调用open()方法

1,启动,已经调用open()方法,但尚未调用send()方法

2,发送,已经调用send()方法,但尚未接收到响应

3,接收,已经接收到部分响应数据

4,完成,已经接收到全部响应数据,而且已经可以在客户端使用了

必须在调用open()之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性

var xhr = createXHR();
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", "example.txt", true);
xhr.send(null);

在接收到响应之前还可以调用abort()方法来取消异步请求

HTTP头部信息

在发送XHR请求的同时,还会发送下列头部信息

Accept,浏览器能够处理的内容类型

Accept-Charset,浏览器能够显示的字符集

Accept-Encoding,浏览器能够处理的压缩编码

Accept-Language,浏览器当前设置的语言

Connection,浏览器与服务器之间连接的类型

Cookie,当前页面设置的任何Cookie

Host,发出请求的页面所在的域

Referer,发出请求的页面的URI

User-Agent,浏览器的用户代理字符串

setRequestHeader()方法,可以设置自定义的请求头部信息,这个方法接收两个参数:头部字段名称和头部字段的值。要成功发送头部信息,必须在调用open()方法之后且调用send()方法之前调用setRequestHeader()

var xhr = createXHR();
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", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);

GET请求

GET是最常见的请求类型,最常用于向服务器查询某些信息,必要时可以将查询字符串参数追加到URI的末尾,以便将信息发送给服务器

使用GET请求经常会发生一个错误,就是查询字符串的格式问题。

查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾,而且所有的名-值对都必须由&分隔

xhr.open("get","example.php?name1=value1&name2=value2",true)

向现有URL的末尾添加查询字符串参数

function addURLParam(url, name, value) {
  url += (url.indexOf("?") == -1 ? "?" : "&");
  url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
  return url;
}

addURLParam()函数接收3个参数:要添加参数的URL、参数的名称和参数的值

POST请求

POST请求,通常用于向服务器发送应该被保存的数据

function submitData(){
  var xhr = createXHR();
  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("post", "postexample.php", true);
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  var form = document.getElementById("user-info");
  xhr.send(serialize(form));
}

XMLHttpRequest 2级 FormData

FormData为序列化表单以及创建与表单格式相同的数据,提供了便利

var data=new FormData();
data.append("name","Nicholas");

这个append()方法接收两个参数,键和值

创建了FormData的实例后,可以将它直接传给XHR的send()方法

var xhr = createXHR();
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("post","postexample.php", true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));

超时设定

IE8为XHR对象添加了一个timeout属性,表示请求在等待响应多少毫秒之后就会终止,在给timeout设置一个数值后,如果在规定的时间内浏览器还没有接收到响应,那么就会触发timeout事件,进而会调用ontimeout事件处理程序

var xhr = createXHR();
xhr.onreadystatechange = function(){
  if (xhr.readyState == 4){
    try {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
        alert(xhr.responseText);
      } else {
        alert("Request was unsuccessful: " + xhr.status);
      }
    } catch (ex){
      //假设由 ontimeout 事件处理程序处理
    }
  }
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000; // 将超时设置为 1  秒钟(仅适用于 IE8+ )
xhr.ontimeout = function(){
  alert("Request did not return in a second.");
};
xhr.send(null);

overrideMimeType()方法

Firefox最早引入了overrideMimeType()方法,用于重写XHR响应的MIME类型

通过调用overrideMimeType()方法,可以保证把响应当做XML而非纯文本来处理

var xhr = createXHR();
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);

进度事件

6个进度事件

loadstart,在接收到响应数据的第一个字节时触发

progress,在接收响应期间持续不断地触发

error,在请求发生错误时触发

abort,在因为调用abort()方法而终止连接时触发

load,在接收到完整的响应数据时触发

loadend,在通信完成或者触发error、abort或load事件后触发

load事件
var xhr = createXHR();
xhr.onload = function(){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
  alert(xhr.responseText);
} else {
  alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
progress事件
var xhr = createXHR();
xhr.onload = function(event){
if ((xhr.status >= 200 && xhr.status < 300) ||
    xhr.status == 304){
  alert(xhr.responseText);
} else {
  alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.onprogress = function(event){
var divStatus = document.getElementById("status");
if (event.lengthComputable){
  divStatus.innerHTML = "Received " + event.position + " of " +
    event.totalSize +" bytes";
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);

为确保正常执行,必须在调用open()方法之前添加onprogress事件处理程序

跨源资源共享

CORS,跨资源共享,定义了在必须访问跨源资源时,浏览器远服务器应该如何沟通。CORS基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败

IE对CORS的实现

微软在IE8中引入了XDR类型,这个对象与XHR类型,但是能实现安全可靠的跨域通信

cookie不会随请求发布,也不会随响应返回

只能设置请求头部信息中的Content-Type字段

不能访问响应头部信息

只支持GET和POST请求

XDR对象的使用方法与XHR对象非常相似,也是创建一个XDomainRequest的实例,调用open()方法,再调用send()方法,与XHR对象的open()方法不同,XDR对象的open()方法只接收2个参数:请求类型和URL

所有XDR请求都是异步执行的,不能用它来创建同步请求,请求返回之后,会触发load事件,响应的数据也会保存在responseText属性中

var xdr = new XDomainRequest();
xdr.onload = function(){
  alert(xdr.responseText);
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null)

请求返回前调用abort()方法可以终止请求

xdr.abort();//终止请求

与XHR一样,XDR对象也支持timeout属性以及ontimeout事件处理程序

var xdr = new XDomainRequest();
xdr.onload = function(){
  alert(xdr.responseText);
};
xdr.onerror = function(){
  alert("An error occurred.");
};
xdr.timeout = 1000;
xdr.ontimeout = function(){
  alert("Request took too long.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);

为支持POST请求,XDR对象提供了contentType属性,用来表示发送数据的格式

var xdr = new XDomainRequest();
xdr.onload = function(){
  alert(xdr.responseText);
};
xdr.onerror = function(){
  alert("An error occurred.");
};
xdr.open("post", "http://www.somewhere-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");

其他浏览器对CORS的实现

要请求位于另一个域中的资源,使用标准的XHR对象并在open()方法中传入绝对URL

var xhr = createXHR();
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", "http://www.somewhere-else.com/page/", true);
xhr.send(null);

安全限制

不能使用setRequsetHeader()设置自定义头部

不能发送和接收cookie

调用getAllResponseHeaders()方法总会返回空字符串

Preflighted Requests

通过Preflighted Request的透明服务器验证机制支持开发人员使用自定义的头部、GET和POST之外的方法,以及不同类型的主体内容

这种请求使用OPITONS方法,发送下列头部

Origin,与简单的请求相同

Access-Control-Method,请求自身使用的方法

Access-Control-Headers,自定义的头部信息,多个头部以逗号分隔

Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ

服务器可以决定是否允许这种类型的请求,服务器在响应中发送如下头部与浏览器进行沟通

Access-Control-Allow-Origin,与简单的请求相同

Access-Control-Allow-Methods,允许的方法,多个方法以逗号分隔

Access-Control-Allow-Headers,允许的头部,多个头部以逗号分隔

Access-Control-Max-Age,应该将这个Preflight请求缓存多长时间,以秒表示

Access-Control-Allow-Origin: http://www.nczonline.net
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

带凭据的请求

通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据

Access-Control-Allow-Credentials:true

如果发送的是带凭据的请求,但服务器的响应中没有包含这个头部,那么浏览器就不会把响应交给JavaScript,于是responseText中将是空字符串,status的值为0,而且会调用onerror()事件处理程序

跨浏览器的CORS
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
  xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){
  vxhr = new XDomainRequest();
  xhr.open(method, url);
} else {
  xhr = null;
}
return xhr;
}
var request = createCORSRequest("get", "http://www.somewhere-else.com/page/");
if (request){
request.onload = function(){
  //对 request.responseText 进行处理
};
request.send();
}

Firefox、Safari和Chrome中的XMLHttpRequest对象与IE中的XDomainRequest对象类似,都提供了够用的接口,这两个对象共同的属性方法如下

abort(),用于停止正在进行的请求

onerror(),用于替代onreadystatechange检测错误

onload(),用于替代onreadystatechange检测成功

responseText(),用于取得响应内容

send(),用于发送请求

其他跨域技术 图像Ping

一个网页可以从任何网页中加载图像,不用担心跨域不跨域

通过图像Ping,浏览器得不到任何具体的数据,但通过侦听load和error事件,它能知道响应什么时候接收到的

var img = new Image();
img.onload = img.onerror = function(){
  alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";

图像Ping最常用于跟踪用户点击页面或动态广告曝光次数,图像Ping有两个主要的缺点,一是只能发送GET请求,二是无法访问服务器的响应文本。因此图像Ping只能用于浏览器与服务器间的单向通信

JSONP

JSONP由两部分组成:回调函数和数据,回调函数是当响应到来时应该在页面中调用的函数,回调函数的名字一般是在请求中指定的,而数据就是传入回调函数中的JSON数据

function handleResponse(response){
  alert("You’re at IP address " + response.ip + ", which is in " +
        response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

优点在于能够直接访问响应文本,支持在浏览器与服务器之间双向通信

Comet

两种实现Comet的方式

长轮询

短轮询时间线

长轮询把短轮询颠倒了一下。页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据之后,浏览器关闭连接,随即又发起一个到服务器的新请求,这一过程在页面打开期间一直持续不断

在页面的整个生命周期内只使用一个HTTP连接,具体来说就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性的向浏览器发送数据

function createStreamingClient(url, progress, finished){
  var xhr = new XMLHttpRequest(),
      received = 0;
  xhr.open("get", url, true);
  xhr.onreadystatechange = function(){
    var result;
    if (xhr.readyState == 3){
      //只取得最新数据并调整计数器
      result = xhr.responseText.substring(received);
      received += result.length;
      //调用 progress 回调函数
      progress(result);
    } else if (xhr.readyState == 4){
      finished(xhr.responseText);
    }
  };
  xhr.send(null);
  return xhr;
}
var client = createStreamingClient("streaming.php", function(data){
  alert("Received: " + data);
}, function(data){
  alert("Done!");
});

这个createStreamingClient()函数接收三个参数:要连接的URL、在接收到数据时调用的函数以及关闭连接时调用的函数

服务器发送事件

SSE是围绕只读Comet交互推出的API或者模式

var source=new EventSource("myevents.php");

EventSource的实例有一个readyState属性,值为0表示正连接到服务器,值为1表示打开了连接,值为2表示关闭了连接

另外还有三个事件

open,在建立连接时触发

message,在从服务器接收到新事件时触发

error,在无法建立连接时触发

Web Sockets

Web Sockets的目标是在一个多带带的持久连接上提供全双工、双向通信

要创建Web Socket,先实例一个WebSocket对象并传入要连接的URL

var socket = new WebSocket("ws://www.example.com/server.php");

实例化WebSocket对象后,浏览器会马上尝试创建连接,与XHR类似,WebSocket也有一个表示当前状态的readyState属性,这个属性的值与XHR并不相同

WebSocket.OPENING(0),正在建立连接

WebSocket.OPEN(1),已经建立连接

WebSocket.CLOSING(2),正在关闭连接

WebSocket.CLOSE(3),已经关闭连接

WebSocket没有readystatechange事件,不过有其他事件,对应着不同的状态,readyState的值永远从0开始

要关闭Web Socket连接,可以在任何时候调用close()方法

socket.close();

调用close()之后,readyState的值立即变为2,而关闭连接后就会变成3

使用send()方法并传入任意字符串

var socket = new WebSocket("ws://www.example.com/server.php");
socket.send("Hello world!");
//将数据序列化为JSON字符串,然后发送到服务器
var message = {
  time: new Date(),
  text: "Hello world!",
  clientId: "asdfp8734rew"
};
socket.send(JSON.stringify(message));
//当服务器收到消息时,WebSocket对象就会触发message事件,这个message事件与其他传递消息的协议类似,也是把返回的数据保存在event.data属性中
socket.onmessage = function(event){
  var data = event.data;
  //处理数据
};

其他事件

open,在成功建立连接时触发

error,在发生错误时触发,连接不能持续

close,在连接关闭时触发

var socket = new WebSocket("ws://www.example.com/server.php");
socket.onopen = function(){
  alert("Connection established.");
};
socket.onerror = function(){
  alert("Connection error.");
};
socket.onclose = function(){
  alert("Connection closed.");
};

SSE与Web Sockets

考虑是使用 SSE 还是使用 Web Sockets 时,可以考虑如下几个因素。

首先,你是否有自由度建立和维护 Web Sockets服务器?因为 Web Socket 协议不同于 HTTP,所以现有服务器不能用于 Web Socket 通信。SSE 倒是通过常规 HTTP 通信,因此现有服务器就可以满足需求。

第二个要考虑的问题是到底需不需要双向通信。如果用例只需读取服务器数据(如比赛成绩),那么 SSE 比较容易实现。如果用例必须双向通信(如聊天室),那么 Web Sockets 显然更好。在不能选择 Web Sockets 的情况下,组合 XHR 和 SSE 也是能实现双向通信的。

安全

为确保通过 XHR 访问的 URL 安全,通行的做法就是验证发送请求者是否有权限访问相应的资源

要求以 SSL 连接来访问可以通过 XHR 请求的资源。

要求每一次请求都要附带经过相应算法计算得到的验证码。请注意,下列措施对防范 CSRF 攻击不起作用。

要求发送 POST 而不是 GET 请求——很容易改变。

检查来源 URL 以确定是否可信——来源记录很容易伪造。

基于 cookie 信息进行验证——同样很容易伪造

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98093.html

相关文章

  • AjaxComet-JavaScript高级程序设计21读书笔记(1)

    摘要:技术的核心是对象即。收到响应后,响应的数据会自动填充对象的属性,相关的属性有作为响应主体被返回的文本。收到响应后,一般来说,会先判断是否为,这是此次请求成功的标志。中的版本会将设置为,而中原生的则会将规范化为。会在取得时报告的值为。 Ajax(Asynchronous Javascript + XML)技术的核心是XMLHttpRequest对象,即: XHR。虽然名字中包含XML,但...

    imingyu 评论0 收藏0
  • 高程3总结#20JSON

    摘要:语法语法可以表示三种类型的值简单值使用与相同的语法,可以在中表示字符串数值布尔值和。对象对象作为一种复杂数据类型,表示的是一组无序的键值对儿。如果字符串长度超过了个,结果中将只出现前个字符。 JSON 语法 JSON语法可以表示三种类型的值 简单值:使用与 JavaScript 相同的语法,可以在 JSON 中表示字符串、数值、布尔值和 null 。但 JSON 不支持 JavaS...

    Hwg 评论0 收藏0
  • 高程3总结#3基本概念

    摘要:基本概念语法区分大小写,中的一切变量函数名和操作符都区分大小写。要将一个值转换成对应的值,可以调用类型包括整数和浮点数值,基本数值字面量格式是十进制整数,除了十进制外还有八进制十六进制。八进制第一位必须是,十六进制第一位必须是。 基本概念 语法 区分大小写,ECMAScript中的一切(变量、函数名和操作符)都区分大小写。函数名不能使用typeof,因为它是一个关键字,但typeOf...

    Rindia 评论0 收藏0
  • 高程3总结#11DOM扩展

    摘要:操作类名时可以通过属性添加删除和替换类名。如果将可选的参数设置为,则表示尽量将元素显示在视口中部垂直方向。将元素的内容滚动指定的页面高度,具体高度由元素的高度决定。 DOM扩展 选择符API querySelector()方法 querySelector()方法接收一个CSS选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null //取得body元素 var b...

    cucumber 评论0 收藏0
  • 高程3总结#17错误处理调试

    错误处理与调试 错误处理 try-catch语句 try{ //可能会导致错误的代码 }catch(error){ //在错误发生时怎么处理 } 发生错误时可以显示浏览器给出的信息 try{ window.someNonexistentFunction(); }catch(error){ alert(error.message); } 在try-catch语句中是可选的,但...

    fizz 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<