XMLHttpRequest对象
目前状态:
XHR对象的API被普遍认为比较难用,自Fetch API 诞生以后迅速称为了XHR 更现代的替代标准 。实际开发中,应该尽可能使用fetch()。
作用:
XHR 对象为开发者提供了原生的浏览器通信能力
使用XHR
所有现代浏览器都通过XMLHttpRequest 构造函数原生支持XHR对象:
let xhr = new XMLHttpRequest();
// 要使用xhr对象首先调用open()方法
/*
open 方法参数说明:
第一个参数为 请求类型 可选值为(get,post,put 等)
第二个参数为请求的URL
第三个参数为 表示请求是否异步 (布尔值)
*/
xhr.open("get","example.php",false);
上边这行代码可以向example.php 发送一个同步的GET请求。还需要说明几点:
- 这里的URL是相对于代码所在页面的,当然也可以使用绝对URL.
- 调用open()不会实际发送请求,只是为了发送请求做好准备。
同源策略 :只能访问同源URL,也就是域名相同、端口相同、协议相同。如果请求的URL与发送请求的页面再任何方便有所不同,则会抛出安全错误。
要发送定义好的请求,必须向下面这样调用send()方法:
xhr.open("get","example.txt",false);
xhr.send(null);
send()方法接收一个参数,是作为请求体发送的数据。如果不需要发送请求体,则必须传null,因为这个参数在某些浏览器中是必需的。 调用send()之后,请求就会发送到服务器。
因为这个请求时同步的, 所以JavaScript 代码会等待服务器响应之后再继续执行。收到响应后,XHR 对象的以下属性会被填充上数据。
- responseText: 作为响应体返回的文本。
- responseXML:如果响应内容类型时“text/xml” 或“application/xml”,那就是包含响应数据的XMLDOM文档。
- status: 响应的HTTP状态.
- statusText:响应的HTTP状态描述。
收到响应后,第一步要检查status属性以确保响应成功返回。一般来说,HTTP状态码为2xx表示成功。此时,responseText 或 responseXML(如果内容类型正确) 属性中会有内容。如果HTTP 状态码是304,则表示资源未修改过,是从浏览器缓存中直接拿取的。当然这也意味着响应有效。为确保收到正确的响应,应该检查这些状态,如下所示:
xhr.open('get','example.txt',false);
xhr.send(null);
/*
为确定下一步该执行什么操作,最好检查status 而不是statusText属性,
因为后者已经被证明在跨浏览器的情况下不可靠
无论是什么响应内容类型,responseText 属性始终会保存响应体,而responseXML则对于非XML数据是null
*/
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
}else{
alert("请求失败:"+xhr.status);
}
异步同步
虽然可以向前面例子一样发送同步请求,但是多数情况下最好使用异步请求,这样可以不阻塞JavaScript代码继续执行。XHR 对象有一个readyState属性,表示当前处在请求/响应过程的那个阶段。这个属性有如下可能的值。
- 0:未初始化(Uninitialized) 。尚未调用open() 方法。
- 1:已打开(Open)。 已调用open() 方法,尚未调用send() 方法。
- 2:已发送(Sent)。 已调用send()方法 ,尚未收到响应。
- 3:接收中(Receiving)。已经收到部分响应。
- 4:完成(Complete)。 已经收到所有响应,可以使用了。
每次readyState 从一个值变成另一个值,都会触发readystatechange事件。可以借此机会检查readyState 的值。一般来说,我们唯一关心的readyState 值是4,表示数据已就绪。为保证跨浏览器兼容,onreadystatechange事件处理程序应该在调用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("请求失败:"+xhr.status);
}
}
};
xhr.open('get','example.txt',true);
xhr.send(null);
//在收到响应之前 如果向取消异步请求,可以调用abort() 方法:
xhr.abort();
HTTP头部
每个HTTP请求和响应都会携带一些头部字段,这些字段可能对开发者有用。XHR对象会通过一些方法暴露与请求和响应相关的头部字段。
默认情况下,XHR请求会发送以下头部字段。
- Accept:浏览器可以处理的内容类型
- Accept-Charset: 浏览器可以显示的字符集
- Accept-Encoding: 浏览器可以处理的压缩编码类型
- Accept-Language: 浏览器使用的语言
- Connection: 浏览器与服务器的连接类型。
- Cookie: 页面中设置的Cookie。
- Host: 发送请求的页面所在的域
- Referer: 发送请求的页面URI.注意,这个字段在HTTP规范中拼错了,所以考虑到兼容性也必需将错就错。(正确拼写应该是Referrer)
- User-Agent:浏览器的用户代理字符串
不同浏览器发送的确切头部字段可能各不相同,但这些通常都是会发送的。如果需要发送额外的请求头部,可以使用setRequestHeader()方法。这个方法接收两个参数:头部字段的名称和值。为保证请求头部被发送,必需在open() 之后、send()之前调用setRequestHeader(),例子:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if((xhr.status >= 200 && xhr.status<300 ) || xhr.status == 304){
alert(xhr.responseText);
}else{
alert(xhr.status);
}
}
xhr.open('get','example.php',true);
xhr.setRequestHeader('MyHeader','MyValue');
xhr.send(null);
可以使用getResponseHeader() 方法从XHR对象获取响应头部,只要传入要获取头部的名称即可。
可以使用getAllResponseHeaders()方法从XHR对象获取所有响应的头部信息。不需要参数。
POST请求
用于向服务器发送应该要保存的数据。每个post请求都应该在请求体中携带提交的数据,而GET请求则不用。POST请求的请求体可以包含非常多的数据,而且数据可以是任意格式。要初始化POST请求,open方法的第一个参数要传“post” 比如:
xhr.open("post","example.php",true);
接下来就是要给send()方法传入要发送的数据。因为XHR最初主要设计用于发送XML,所以可以传入序列化之后的XML DOM文档作为请求体。当然,也可以传入任意字符串。
默认情况下,对服务器而言,POST请求与提交表单是不一样的。服务器逻辑需要读取原始POST数据才能取得浏览器发送的数据。不过,可以使用XHR模拟表单提交。为此,第一步需要把Content-Type头部设置为“application/x-www-formurlencoded” ,这是提交表单时使用的内容类型。第二步是创建对应格式的字符串。POST数据此时使用与查询字符串相同的格式。如果网页中确实有一个表单需要序列化并通过XHR发送到服务器可以使用serialize()函数来创建响应的字符串
xhr.open("post",'example.php',true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
let form = document.getElmentById('user-info');
xhr.send(serialize(form));
POST请求相比GET请求要占用更多资源。从性能方面说,发送相同数量的数据,GET 请求比POST请求要快两倍。
XMLHttpRequest Level 1 and XMLHttpRequest Level 2
XMLHttpRequest Level 1 只是把已经存在的XHR对象的实现细节明确了一下。
XMLHttpRequest Level 2 进一步发展了XHR对象。
FormData 类型
现代Web 应用程序中 经常需要对表单数据进行序列化,因此XMLHttpRequest Level 2 新增了FormData 类型。FormData 类型便于表单序列化,也便于创建与表单类似格式的数据然后通过XHR发送。下面的代码创建了一个FormData对象,并填充了一些数据:
let data = new FormData();
data.append("name",'张三');
append方法接收两个参数:键和值,相当于表单字段名称和该字段的值。可以像这样添加任意多个键/值对数据。此外,通过直接给FormData 构造函数传入一个表单元素,也可以将表单中的数据作为键/值对填充进行:
let data = new FormData(document.forms[0]);
// 也可以直接传给XHR对象的send()方法
let xhr = new XMLHttpRequest();
xhr.onreadystatechange= function(){
if((xhr.status >= 200 && xhr.status <300)||xhr.status == 304){
alert(xhr.responseText);
}else{
alert(xhr.status);
}
}
xhr.open('post','example.php',true);
let form = document.getElementById("user-info");
xhr.send(new FormData(form));
使用FormData的另一个方便之处是不再需要给XHR对象显式设置任何请求头部了。XHR对象能够识别作为FormData 实例传入的数据类型并自动配置响应的头部。
超时
IE8 给XHR对象增加了一个timeout 属性,用于表示发送请求后等待多少毫秒,如果响应不成功就中断请求。之后所有浏览器都在自己的XHR实现中增加了这个属性。在给timeout 属性设置了一个时间且在该时间过后没有收到响应时,XHR对象就会触发timeout事件,调用ontimeout事件处理程序。这个特性后来也被添加到了XMLHttpRequest Level 2 规范。例子:
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
try{
if((xhr.status >=200 && xhr.status <300 )|| xhr.status == 304){
alert(xhr.responseText);
}else{
alert(xhr.status);
}
}catch(ex){
//假设由ontimeout 处理
console.log('catch',ex)
}
}
}
xhr.open('get','timeout.php',true);
xhr.timeout = 1000;
xhr.ontimeout = function(){
alert("请求超时")
}
xhr.send(null);
这个例子演示了使用timeout设置超时。给timeout 设置1000毫秒意味着,如果请求没有在1秒内返回则会中断。此时则会触发ontimeout事件处理程序,readyState 仍然会变成4,因此也会调用onreadystatechange事件处理程序。不过,如果在超时之后访问status属性则会发生错误,为做好防护,可以把检查status属性的代码封装在try/catch 语句中。
overrideMimeType()方法
Firefox 首先引入了overrideMimeType()方法用于重写XHR响应的MIME类型。这个特性后来也被添加到了XMLHttpRequest Level 2。因为响应返回的MIME 类型决定了XHR对象如何处理响应,所以如果有办法覆盖服务器返回的类型,那么是由帮助的。
假设服务器实际发送了XML数据,但响应头设置的MIME类型是text/plain。结果就会导致虽然数据是XML,但responseXML属性值是null.此时调用overrideMimeType()可以保证将响应当成XML而不是纯文本来处理:
let xhr = new XMLHttpRequest();
xhr.open("get",'text.php',true);
xhr.overrideMimeType("text/xml");
xhr.send(null);
这个例子强制让XHR把响应当成XML而不是纯文本来处理。为了正确覆盖响应的MIME类型,必需在调用send()之前调用overrideMimeType().
以上内容来源与 JavaScript高级教程设计第四版 ,仅作为学习记录使用,如涉及版权相关问题请联系我