浏览器在 XMLHttpRequest
类上定义了他们的 HTTP API。这个类的每个实例都表示一个独立的请求/响应,而且这个对象的属性和方法容许指定请求细节和提取响应数据。W3C在 XMLHttpRequest
规范的基础上制定了2级 XMLHttpRequest
(下文简称 XHR2
)标准草案,且大部分浏览器都已经支持了。javascript
XMLHttpRequest
以前,我想先简单说一下HTTP请求组成部分和响应的组成部分HTTP 请求的各部分有指定的顺序:请求方法和URL首先到达,而后是请求头,最后是主体。XHMLHttpRequest
实现一般直到调用send()方法才开始启动网络。但 XMLHttpRequest
API的设计彷佛使每一个方法都将写入网络流。这意味着调用 XMLHttpRequest
上的方法的顺序必须匹配 HTTP 请求的架构。例如,setRequestHeader() 方法的调用必须在调用 open() 以前可是在 send() 以后,不然就会跑出异常。下面我门会按照这个顺序来介绍。java
使用 XMLHttpRequest
API的第一件事就是先实例化;程序员
var request = new XMLHttpRequest();
复制代码
咱们也能重用已经存在的 XMLHttpRequest对象,可是这将会停止以前经过该对象发起的任何请求,但通常不会这么用数据库
request.open('POST', url);
复制代码
第一个参数指定HTTP方法或动做,注意: 不区分大小写,但一般都是使用大写来匹配HTTP协议。json
第二个参数是URL,这个URL能够是相对url也能够是绝对的url。跨域
第三个参数用来设置请求的异步仍是同步浏览器
若是请求一个受保护的URL,把用户和密码做为第4和第5个参数传递。缓存
request.setRequestheader('Content-Type': 'text/plain');
复制代码
若是对相同的头调用 setRequestHeader()
屡次,新值不会取代以前指定的值,相反,HTTP请求将包含这个头的多个副本或这个头将指定多个值。安全
setRequestHeader()
没有办法设置下面的 header
,XMLHttpRequest
将自动添加这些头而防止伪造他们。相似的,XMLHttpRequest
对象自动处理 cookie
,链接时间,字符集和编码判断,因此没法向 setRequestHeader()
传递这些头:服务器
Accept-Charset | Accept-Encoding | Connection | Content-Length | Cookie |
Cookie2 | Content-Transfer-Encoding | Date | Expect | Host |
Keep-Live | Referer | TE | Trailer | Transfer-Encoding |
Upgrage | User-Agent | Via |
当 send()
方法传入 XML
文档时,没有指定 Content-Type
,XMLHttpRequest
会自动设置一个合适的头。相似的若是给 send()
传入一个字符串但没有指定 Content-Type
,那么 XMLHttpRequest
将会自动添加 text/plain; charset=utf-8
头。
当使用 GET
方法时,不须要调用 setRequestHeader()
这个方法,由于 GET
请求只能进行 url编码(application/x-www-form-urlencoded)
,而若是使用 POST
方法且传递的参数是以 ‘&’ 和 ‘=’ 符号进行键值链接时,Content-Type
头必须设置 application/x-www-form-urlencoded
。
使用 XMLHttpRequest
发起HTTP请求的最后一步就是指定可选的请求主体并向服务器发送它:
request.send(null);
复制代码
setRequestHeader()
指定的 Content-type
头。XMLHttpRequest
属性readystatechange
XMLHttpRequest
对象一般异步使用:发送请求后,send()
方法当即返回,直到响应返回。为了在响应准备就绪的时候获得通知,必须监听 XMLHttpRequest
对象上的 readystatechange
事件。
readyState
它是一个整数, 他指定了 HTTP
请求的状态。
abort()
方法重置。open()
方法已调用,请求链接已经创建。可是 send()
方法未调用,请求数据未发送。send()
方法已调用,HTTP
请求已发送到 Web 服务器。接收到头信息HTTP
响应已经彻底接收status
服务器返回的http状态码,当 readyState 小于 3 的时候读取这一属性会致使一个异常。
statusText
以数字和文字的形式返回 HTTP 状态码。
getRequestHeader()/getAllRequestHeaders()
使用这两个方法均可以查询到响应头。XMLHttpRequest
会自动处理 cookie
,他会从 getAllRequestHeaders()
头返回集中过滤掉 cookie
头。而若是给 getRequestHeader()
传递 Set-Cookie
和 Set-Cookie2
则会返回 null
。
responseText
responseText
接受到服务器的相应数据 返回的值是一个json字符串 经过 JSON.parse(xhr.responseText)
能够获得数据对象
responseXML
responseXML属性能够获得 XML 的 Document形式的数据。
XHR2规范定义了不少有用的事件集,在这个新的事件模型中,XMLHttpRequest
对象在请求的不一样阶段触发不一样类型的事件,因此它再也不须要检查 readyState
属性 。
onloadstart
: 当调用 send()
时,触发单个 loadstart
事件
onprogress
: xhr对象会发生 progress
事件,一般每隔50ms左右触发一次,因此可使用这个事件给用户反馈请求的进度。若是请求快速完成,他可能不会触发 progress
事件。注意这里的 progress
是下载的进度,xhr2 额外的定义了上传 upload
属性,用来定义上传的相关事件。
onload
: 当事件完成时,触发 load
事件,load
事件的处理程序应该检查xhr对象的 status
状态码来肯定收到的是 200 仍是 404
ontimeout
: 若是请求超时,会触发 timeout
事件。
onerror
: 大多重定向这样的网络错误会阻止请求的完成,但这些状况发生时会触发 error
事件。
onabort
: 若是请求停止,会触发 abort
事件。
onloadend
: 对于任何具体的请求,浏览器将只会触发 load/abort/timeout/error
事件中的一个。一旦这些事件触发之后,浏览器将会触发 loadend
事件。
loaded
: 目前传输的字节数值total
: 传输的数据的总体长度(单位字节),Content-Length == total
,若是不知道内容的长度则 total == 0
;lengthComputable
: 若是知道内容的长度则 lengthComputable == true
, 不然 falsexhr.onprogress = function(event) {
if (event.lengthComputable) {
const progress = event.loaded / event.total * 100;
}
}
复制代码
XHR2
新增的 upload
属性XHR2
中新增了一个 upload
属性,这个属性值是一个对象,他定义了 addEventListener()
和 整个 progress
事件集合,好比说 onprogress
和 onload
。(但 upload
没有定义 onreadystatechange
属性,upload
仅能触发新的事件类型)。
onloadstart
: 和 XMLHttpRequest
中的 loadstart
事件同样。
onprogress
: 和 XMLHttpRequest
中的 progress
事件同样。
onload
: 和 XMLHttpRequest
中的 load
事件同样。
ontimeout
: 和 XMLHttpRequest
中的 timeout
事件同样。
onerror
: 和 XMLHttpRequest
中的 error
事件同样。
onabort
: 和 XMLHttpRequest
中的 abort
事件同样。
onloadend
: 和 XMLHttpRequest
中的 loadend
事件同样。
upload
属性上定义的事件主要用在上传文件时。咱们可使用 upload
上的 onloadstart,onprogress
分别监听文件开始上传和上传过程当中进度的变化。
const input = document.getElementsByTagName('input')[0];
input.addEventListener('change', function() {
var file = this.files[0];
if (!file) return;
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.send(file);
}, false);
复制代码
文件类型是更普通的二进制大对象 Blob
类型中的一个字类型。XHR2
容许向 send()
方法传入任何 Blob
对象。若是没有显示的设置 Content-Type
头,这个 Blob
对象的 type
属性用于设置待上传的 Content-Type
头。
XHR2
定义了新的 FormData
API, 它容易实现多部分请求主体。首先,使用 FormData()
构造函数建立 FormData
对象,而后按需屡次调用这个对象的 append()
方法把个体部分(string/File/Blob对象)
添加到请求中。最后把 FormData
对象传递给 send()
方法。send()
方法将对请求定义合适的边界字符串和设置 Content-Type
头。
经过调用 XMLHttpRequest
对象的 abort()
方法来取消正在进行的 HTTP
请求。当调用 abort()
方法后会触发 xhr 对象的 onabort
事件。
XHR2
定义了 timeout
属性来指定请求自动终止的毫秒数。同时也定义了 timeout
事件,当超时发生时触发。
// 封装一个request方法
const request = (url, formData, cb) => {
// 初始化
const xhr = new XMLHttpRequest();
// 定义请求的方法/动做和url
xhr.open('POST', url);
// 设置超时时间,单位是毫秒
xhr.timeout = 2000;
xhr.ontimeout = function() {
console.log('timeout');
};
// 开始上传
xhr.upload.onloadstart = function() {
console.log('开始上传');
};
// 上传的进度
xhr.upload.onprogress = function(event) {
// 只有当 lengthComputable 为true是,loaded 才有值
if (event.lengthComputable) {
const value = Math.ceil((event.loaded / event.total) * 100);
cb && cb({
status: 'loading',
progress: value,
data: null,
});
}
};
// 监听事件完成, 完成并不必定表明请求成功,因此须要判断 status 状态码
xhr.onload = function() {
const resp: Response = {
status: 'success',
progress: 100,
data: null,
};
if (xhr.status === 200) {
resp.data = JSON.parse(xhr.responseText);
cb && cb(resp);
} else {
resp.status = 'error';
cb && cb(resp);
}
};
xhr.onerror = function() {
cb && cb({
status: 'error',
progress: 0,
data: null,
});
};
xhr.onabort = function() {
console.log('onabort');
};
xhr.onloadend = function() {
console.log('上传结束');
};
xhr.send(formData);
return xhr;
};
复制代码
做为同源策略的一部分,XMLHttpRequest
对象一般仅能够发起和文档具备相同服务器的 HTTP
请求。这个限制关闭了安全里漏洞,但同时也阻止了大量可以使用的跨域请求。好在 XHR2
经过在 HTTP
响应中选择发送合适的 CORS(Cross-Origin Resource Sharing,跨域资源共享)
容许跨域访问网站。在平常开发中使用跨域请求并不须要进行的额外的其余设置,只要浏览器支持 CORS
跨域请求就行。 虽然实现 CORS
支持跨域的请求工做不须要作任务的事情,但有一些安全细节须要了解:
xhr.open()
方法传入第四和第五个参数(用户名和密码)时,将不会经过跨域请求发送cookie
的。若是须要携带 cookie
,那么能够在调用 send()
方法以前设置 XMLHttpRequest
的withCredentials
属性为 true
CORS
跨域请求,能够直接经过检测 XMLHttpRequest
的 withCredentials
的属性是否存在便可。XMLHttpRequest
的跨域请求一样包含简单请求和非简单请求,非简单请求又会进行预检请求,具体 CORS
的相关知识能够查看以前的分享的文章点击这里。XMLHttpRequest
API很是的好用,并且目前市面上的主浏览器也基本上都支持。相比 fetch
而言,兼容性确定是更胜一筹,惟一不足的是不支持 Promise
,可是这也难不倒咱们程序员,本身封装一层就能够了。更为重要的是 XMLHttpRequest
支持超时设置和停止请求,还有进度事件,这些都是 'fetch' 所不具有的。