Open shenxuxiang opened 5 years ago
浏览器在 XMLHttpRequest 类上定义了他们的 HTTP API。这个类的每一个实例都表示一个独立的请求/响应,并且这个对象的属性和方法允许指定请求细节和提取响应数据。W3C在 XMLHttpRequest 规范的基础上制定了2级 XMLHttpRequest (下文简称 XHR2 )标准草案,且大部分浏览器都已经支持了。
XMLHttpRequest
XHR2
HTTP 请求的各部分有指定的顺序:请求方法和URL首先到达,然后是请求头,最后是主体。XHMLHttpRequest 实现通常直到调用send()方法才开始启动网络。但 XMLHttpRequest API的设计似乎使每个方法都将写入网络流。这意味着调用 XMLHttpRequest 上的方法的顺序必须匹配 HTTP 请求的架构。例如,setRequestHeader() 方法的调用必须在调用 open() 之前但是在 send() 之后,否则就会跑出异常。下面我门会按照这个顺序来介绍。
XHMLHttpRequest
使用 XMLHttpRequest API的第一件事就是先实例化;
var request = new XMLHttpRequest();
我们也能重用已经存在的 XMLHttpRequest对象,但是这将会中止之前通过该对象发起的任何请求,但一般不会这么用
request.open('POST', url);
第一个参数指定HTTP方法或动作,注意: 不区分大小写,但通常都是使用大写来匹配HTTP协议。
第二个参数是URL,这个URL可以是相对url也可以是绝对的url。
第三个参数用来设置请求的异步还是同步
如果请求一个受保护的URL,把用户和密码作为第4和第5个参数传递。
request.setRequestheader('Content-Type': 'text/plain');
如果对相同的头调用 setRequestHeader() 多次,新值不会取代之前指定的值,相反,HTTP请求将包含这个头的多个副本或这个头将指定多个值。
setRequestHeader()
setRequestHeader() 没有办法设置下面的 header,XMLHttpRequest 将自动添加这些头而防止伪造他们。类似的,XMLHttpRequest 对象自动处理 cookie,连接时间,字符集和编码判断,所以无法向 setRequestHeader() 传递这些头:
header
cookie
当 send() 方法传入 XML 文档时,没有指定 Content-Type,XMLHttpRequest 会自动设置一个合适的头。类似的如果给 send() 传入一个字符串但没有指定 Content-Type,那么 XMLHttpRequest 将会自动添加 text/plain; charset=utf-8 头。
send()
XML
Content-Type
text/plain; charset=utf-8
当使用 GET 方法时,不需要调用 setRequestHeader() 这个方法,因为 GET 请求只能进行 url编码(application/x-www-form-urlencoded),而如果使用 POST 方法且传递的参数是以 ‘&’ 和 ‘=’ 符号进行键值连接时,Content-Type 头必须设置 application/x-www-form-urlencoded。
GET
url编码(application/x-www-form-urlencoded)
POST
application/x-www-form-urlencoded
使用 XMLHttpRequest 发起HTTP请求的最后一步就是指定可选的请求主体并向服务器发送它:
request.send(null);
Content-type
readystatechange
XMLHttpRequest 对象通常异步使用:发送请求后,send() 方法立即返回,直到响应返回。为了在响应准备就绪的时候得到通知,必须监听 XMLHttpRequest 对象上的 readystatechange 事件。
readyState
它是一个整数, 他指定了 HTTP 请求的状态。
HTTP
abort()
open()
status
服务器返回的http状态码,当 readyState 小于 3 的时候读取这一属性会导致一个异常。
statusText
以数字和文字的形式返回 HTTP 状态码。
getRequestHeader()/getAllRequestHeaders()
使用这两个方法都可以查询到响应头。XMLHttpRequest 会自动处理 cookie,他会从 getAllRequestHeaders() 头返回集中过滤掉 cookie 头。而如果给 getRequestHeader() 传递 Set-Cookie 和 Set-Cookie2 则会返回 null。
getAllRequestHeaders()
getRequestHeader()
Set-Cookie
Set-Cookie2
null
responseText
responseText 接受到服务器的相应数据 返回的值是一个json字符串 通过 JSON.parse(xhr.responseText) 可以得到数据对象
JSON.parse(xhr.responseText)
responseXML
responseXML属性可以得到 XML 的 Document形式的数据。
XHR2规范定义了很多有用的事件集,在这个新的事件模型中,XMLHttpRequest 对象在请求的不同阶段触发不同类型的事件,所以它不再需要检查 readyState 属性 。
onloadstart : 当调用 send() 时,触发单个 loadstart 事件
onloadstart
loadstart
onprogress : xhr对象会发生 progress 事件,通常每隔50ms左右触发一次,所以可以使用这个事件给用户反馈请求的进度。如果请求快速完成,他可能不会触发 progress 事件。注意这里的 progress 是下载的进度,xhr2 额外的定义了上传 upload 属性,用来定义上传的相关事件。
onprogress
progress
upload
onload : 当事件完成时,触发 load 事件,load 事件的处理程序应该检查xhr对象的 status 状态码来确定收到的是 200 还是 404
onload
load
ontimeout : 如果请求超时,会触发 timeout 事件。
ontimeout
timeout
onerror : 大多重定向这样的网络错误会阻止请求的完成,但这些情况发生时会触发 error 事件。
onerror
error
onabort : 如果请求中止,会触发 abort 事件。
onabort
abort
onloadend : 对于任何具体的请求,浏览器将只会触发 load/abort/timeout/error 事件中的一个。一旦这些事件触发以后,浏览器将会触发 loadend 事件。
onloadend
load/abort/timeout/error
loadend
loaded
total
Content-Length == total
total == 0
lengthComputable
lengthComputable == true
xhr.onprogress = function(event) { if (event.lengthComputable) { const progress = event.loaded / event.total * 100; } }
XHR2 中新增了一个 upload 属性,这个属性值是一个对象,他定义了 addEventListener() 和 整个 progress 事件集合,比如说 onprogress 和 onload。(但 upload 没有定义 onreadystatechange 属性,upload 仅能触发新的事件类型)。
addEventListener()
onreadystatechange
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 分别监听文件开始上传和上传过程中进度的变化。
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 头。
Blob
type
XHR2 定义了新的 FormData API, 它容易实现多部分请求主体。首先,使用 FormData() 构造函数创建 FormData 对象,然后按需多次调用这个对象的 append() 方法把个体部分(string/File/Blob对象) 添加到请求中。最后把 FormData 对象传递给 send() 方法。send() 方法将对请求定义合适的边界字符串和设置 Content-Type 头。
FormData
FormData()
append()
(string/File/Blob对象)
通过调用 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 支持跨域的请求工作不需要做任务的事情,但有一些安全细节需要了解:
CORS(Cross-Origin Resource Sharing,跨域资源共享)
CORS
xhr.open()
withCredentials
true
XMLHttpRequest API非常的好用,而且目前市面上的主浏览器也基本上都支持。相比 fetch 而言,兼容性肯定是更胜一筹,唯一不足的是不支持 Promise,但是这也难不倒我们程序员,自己封装一层就可以了。更为重要的是 XMLHttpRequest 支持超时设置和中止请求,还有进度事件,这些都是 'fetch' 所不具备的。
fetch
Promise
XMLHttpRequest
浏览器在
XMLHttpRequest
类上定义了他们的 HTTP API。这个类的每一个实例都表示一个独立的请求/响应,并且这个对象的属性和方法允许指定请求细节和提取响应数据。W3C在XMLHttpRequest
规范的基础上制定了2级XMLHttpRequest
(下文简称XHR2
)标准草案,且大部分浏览器都已经支持了。在介绍
XMLHttpRequest
之前,我想先简单说一下HTTP请求组成部分和响应的组成部分一个 HTTP 请求由4部分组成:
服务器返回的 HTTP 响应包含3部分:
HTTP 请求的各部分有指定的顺序:请求方法和URL首先到达,然后是请求头,最后是主体。
XHMLHttpRequest
实现通常直到调用send()方法才开始启动网络。但XMLHttpRequest
API的设计似乎使每个方法都将写入网络流。这意味着调用XMLHttpRequest
上的方法的顺序必须匹配 HTTP 请求的架构。例如,setRequestHeader() 方法的调用必须在调用 open() 之前但是在 send() 之后,否则就会跑出异常。下面我门会按照这个顺序来介绍。实例化
使用
XMLHttpRequest
API的第一件事就是先实例化;我们也能重用已经存在的 XMLHttpRequest对象,但是这将会中止之前通过该对象发起的任何请求,但一般不会这么用
指定请求
第一个参数指定HTTP方法或动作,注意: 不区分大小写,但通常都是使用大写来匹配HTTP协议。
第二个参数是URL,这个URL可以是相对url也可以是绝对的url。
第三个参数用来设置请求的异步还是同步
如果请求一个受保护的URL,把用户和密码作为第4和第5个参数传递。
设置请求头
注意:
如果对相同的头调用
setRequestHeader()
多次,新值不会取代之前指定的值,相反,HTTP请求将包含这个头的多个副本或这个头将指定多个值。setRequestHeader()
没有办法设置下面的header
,XMLHttpRequest
将自动添加这些头而防止伪造他们。类似的,XMLHttpRequest
对象自动处理cookie
,连接时间,字符集和编码判断,所以无法向setRequestHeader()
传递这些头:当
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请求的最后一步就是指定可选的请求主体并向服务器发送它: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新增的事件集
XHR2规范定义了很多有用的事件集,在这个新的事件模型中,
XMLHttpRequest
对象在请求的不同阶段触发不同类型的事件,所以它不再需要检查readyState
属性 。onloadstart
: 当调用send()
时,触发单个loadstart
事件onprogress
: xhr对象会发生progress
事件,通常每隔50ms左右触发一次,所以可以使用这个事件给用户反馈请求的进度。如果请求快速完成,他可能不会触发progress
事件。注意这里的progress
是下载的进度,xhr2 额外的定义了上传upload
属性,用来定义上传的相关事件。onload
: 当事件完成时,触发load
事件,load
事件的处理程序应该检查xhr对象的status
状态码来确定收到的是 200 还是 404ontimeout
: 如果请求超时,会触发timeout
事件。onerror
: 大多重定向这样的网络错误会阻止请求的完成,但这些情况发生时会触发error
事件。onabort
: 如果请求中止,会触发abort
事件。onloadend
: 对于任何具体的请求,浏览器将只会触发load/abort/timeout/error
事件中的一个。一旦这些事件触发以后,浏览器将会触发loadend
事件。注意: 上面的这些事件我们可以通过 xhr.addEventListener() 方法进行监听。
progress事件中有三个属性需要讲解一下:
loaded
: 目前传输的字节数值total
: 传输的数据的整体长度(单位字节),Content-Length == total
,如果不知道内容的长度则total == 0
;lengthComputable
: 如果知道内容的长度则lengthComputable == true
, 否则 falseXHR2
新增的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
分别监听文件开始上传和上传过程中进度的变化。对于文件上传,我们如何设置请求头??
文件类型是更普通的二进制大对象
Blob
类型中的一个字类型。XHR2
允许向send()
方法传入任何Blob
对象。如果没有显示的设置Content-Type
头,这个Blob
对象的type
属性用于设置待上传的Content-Type
头。multipart/form-data请求
XHR2
定义了新的FormData
API, 它容易实现多部分请求主体。首先,使用FormData()
构造函数创建FormData
对象,然后按需多次调用这个对象的append()
方法把个体部分(string/File/Blob对象)
添加到请求中。最后把FormData
对象传递给send()
方法。send()
方法将对请求定义合适的边界字符串和设置Content-Type
头。中止请求和超时
中止请求
通过调用
XMLHttpRequest
对象的abort()
方法来取消正在进行的HTTP
请求。当调用abort()
方法后会触发 xhr 对象的onabort
事件。超时
XHR2
定义了timeout
属性来指定请求自动终止的毫秒数。同时也定义了timeout
事件,当超时发生时触发。demo
HTTP跨域请求
作为同源策略的一部分,
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' 所不具备的。参考