XMLHttpRequest剖析
Posted lyralee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XMLHttpRequest剖析相关的知识,希望对你有一定的参考价值。
1.什么是XMLHttpRequest?
首先XMLHttpRequest是一个构造函数,是位于Window对象上的一个构造函数。
所以要使用需要通过new命令创建一个实例。
const xhr = new XMLHttpRequest();
由于位于Window对象,所以实例化的xhr对象其实就是一个浏览器内置的对象。
它提供了对http协议的完全的访问,用于JS进行http请求和http响应。
2.兼容性
几乎所有的主流浏览器都支持,IE需要通过下面的办法兼容
new ActiveXObject("Microsoft.XMLHTTP")
3.发展历史
XMLHttpRequest有两个阶段,一个是Level1,一个是Leve2
level1的缺点:
- 受同源策略(Same Origin Policy)影响, 不能发送跨域请求
- 不能发送二进制(音频,视频,图片等)文件,只能发送文本
- 发送和接收数据时,没有实时进度信息,只能判断是否完成
level2解决了以上缺点,并添加了新功能:
- 只要服务器允许,可以进行跨域访问
- 可以发送和获取二进制数据(音频,视频,图片等都是二进制文件)
- 可以获取请求发送数据的实时进度信息
- 新增FormData数据,可以发送表单数据(multipart/formdata)
- 可以设置过时时间timeout
4.只读属性
1.readyState --HTTP 请求的状态.
从对象实例化为0开始到结束为4,依次递增。
状态 | 名称 | 描述 |
0 | UNSENT | 初始化。new了实例或者已经被abort()方法重置 |
1 | OPENED | 调用了open方法,但是还未调用send方法。请求还未发送。 |
2 | HEADERS_RECEIVED | web服务器接收到请求头,send发送成功。未收到响应。 |
3 | LOADING | 响应头已经接收。响应体开始接收,未完成。 |
4 | DONE | 响应接收完成。 |
每次状态改变都会触发onreadystatechange事件。
LOADING状态可能会触发多次,因为每接收一个数据包就会触发一次。
2.status--http请求响应中~的状态码
进入响应前和响应失败都是0;响应中如果未特殊指定,都为200。
var xhr = new XMLHttpRequest(); console.log(‘UNSENT‘, xhr.status); xhr.open(‘GET‘, ‘/server‘, true); console.log(‘OPENED‘, xhr.status); xhr.onprogress = function () { console.log(‘LOADING‘, xhr.status); }; xhr.onload = function () { console.log(‘DONE‘, xhr.status); }; xhr.send(null); /** * 输出如下: * * UNSENT(未发送) 0 * OPENED(已打开) 0 * LOADING(载入中) 200 * DONE(完成) 200 */
3. statusText -- http响应中~的状态名称
例如:xhr.statusText --- OK
4. response--返回响应(XHR2)
可以和responseType一起取代原来的responseText, responseXML等;
5.可写属性
1.responseType --- 指定响应返回的数据格式(XHR2)
- "": 默认。 返回字符串
- "text": 返回字符串。
- "arraybuffer": 返回ArrayBuffer类型的数据(二进制)。
- "blob": 返回Blob数据。
- "document": 返回XML文档。
- "json": 返回JSON数据。
xhr.responseType = ‘json‘;
2.timeout--设置请求超时时间
默认0,表示不设置请求超时。
只能用于异步请求,同步请求不允许。
3.withCredentials--携带验证信息
默认false;
同域请求时,浏览器会自动将cookie添加到请求头中,但是跨域请求不会。
因为CORS规定,跨域时,不允许携带任何认证信息,除非withCredentials=true;
首先,为了能够跨域,服务器端需要设置
Access-Control-Allow-Origin: *// 或者具体的域名
如果请求需要认证信息,浏览器端需要设置:
xhr.withCredentials = true
服务器端需要设置
Access-Control-Allow-Origin: //具体的origin,一定不能设置成* Access-Control-Allow-Credentials: true
6.方法
1.open(method, url [, async, username, password])方法
初始化请求参数
method: 请求方式主要有GET,POST,也可以是HEAD(请求资源的头部信息,和GET返回的头部一样)---CORS安全
url: 请求路由
async: 默认true,异步请求。为false时,同步, 已被废弃deprecated。
基本都用异步请求。同步请求几乎不用,一是因为会出现阻塞,网页挂起的情况;二是不能使用timeout,不能进行跨域,也没有进度信息。
const xhr = new XMLHttpRequest(); xhr.open("POST", url, true); // 异步 xhr.send(); // readyState === 1 异步请求send立即返回 const xhr = new XMLHttpRequest(); xhr.open("POST", url, false); // 同步 xhr.send(); // readyState === 4 同步等所有的进程结束再返回
username,password: 认证权限
2. setRequestHeader(name, value)
只能在open和send之间调用。既可以设置系统默认的一些头部信息(如ContentType),也可以设置自定义的头部信息(‘X-USER’)。
一旦设置,就无法撤销。设置同样的name,也是append,而不会覆盖。
var client = new XMLHttpRequest(); client.open(‘GET‘, ‘demo.cgi‘); client.setRequestHeader(‘X-Test‘, ‘one‘); // 自定义请求头 client.setRequestHeader(‘X-Test‘, ‘two‘); // 自定义请求头 client.send(); // X-TEST: one, two
name: 请求头的名称。忽略大小写。
有些请求头只能由浏览器控制,用户不能操作。否则 Refused to set unsafe header "..."
`Accept-Charset` `Accept-Encoding` `Access-Control-Request-Headers` `Access-Control-Request-Method` `Connection` `Content-Length` `Cookie` `Cookie2` `Date` `DNT` `Expect` `Host` `Keep-Alive` `Origin` `Referer` `TE` `Trailer` `Transfer-Encoding` `Upgrade` `Via`
用户可以操作的请求头有:
Content-Type: 设置发送的数据类型,即send(body)中请求体的类型。以下全部为POST请求
- "multipart/formdata; boundry=something": 当传递FormData类型时,默认是该类型。
// 示例: multipart/form-data; boundary=----WebKitFormBoundary2OgAPgMEOp8S1XFR
... data = new FormData(); data.append(‘name‘, ‘lyra‘); data.append(‘age‘,18); xhr.send(data); // 还可以从网页中获取表单的值 <form id="form"> <input type=‘text‘ name="age" value="" /><!--必须有name--> </form> data = new FormData(formData); data.append(‘name‘,‘lyra‘); xhr.send(data); // 还可以上传文件等二进制文件 <body> <button onclick="loadJSONDoc()">Get JSON</button> <form id="form"> <label for="image">上传图片</label> <input id="file" type=‘file‘ name="image" accept="image/png,image/jpeg" multiple /> </form> <script> var xhr; function loadJSONDoc() { xhr = null; if (window.XMLHttpRequest) {// code for Firefox, Chrome, etc. xhr = new XMLHttpRequest(); console.log(xhr.readyState); // 0 } else if (window.ActiveXObject) {// code for IE xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function() { // 0,1,2,3,4 console.log(xhr.readyState) } if (xhr != null) { let data = null; const fileDOM = document.querySelector(‘input‘); const formData = new FormData(); const files = fileDOM.files; // 元素的files属性获取文件列表 for(let file of files) { formData.append("file", file); } data = formData; xhr.open("POST", ‘/postdata‘, true); // 1 xhr.send(data); } } </script> </body>
- "application/x-www-form-urlencoded;charset=UTF-8": 当传递URLSearchParams类型时,默认是该类型。
// 直接通过URLSearchParams对象 ... data = new URLSearchParams(‘name=5‘); xhr.send(data); // 还可以通过URL对象的searchParams属性 ... data = (new URL(‘http://www.dd.com?name=5‘)).searchParams; xhr.send(data); //PS: 当form表单设置method="POST"时,系统默认的请求头Content-Type的值是: application/x-www-form-urlencoded // 当method不设置,或者设为“GET”时,为url传参,传参形式是: ?name=value&name1=value1... //没有Content-Type请求头
- "text/html;charset=UTF-8": 当传递的数据为document类型时,默认是该类型。
data = document;
xhr.send(data);
- "text/plain; charset=UTF-8": 当传递的数据是对象,数字,字符串,布尔值时,默认都是该类型。
??//单独的传"name=lyra"属于字符串,不是URLSearchParams对象。
data = {a: 1}; //data = "22"; //data = 22; //data = true xhr.send(data);
- "application/json; charset=UTF-8": 浏览器不会默认该类型,需要手动添加。传参数据需要JSON.stringify()处理。
data = JSON.stringify({a: 0}); xhr.setRequestHeader(‘Content-Type‘,‘application/json;charset=UTF-8‘);
xhr.send(data);
3.send([body])
发送http请求。
如果是GET方法,没有请求体body或者传null。
POST方法需要传请求体body。
4.getAllResponseHeaders()
获取除了Set-Cookie和Set-Cookie2之外的所有响应头信息。
并且每个头信息单独占一行。例如:
Cache-Control: max-age=31536000 Content-Length: 4260 Content-Type: image/png Date: Sat, 08 Sep 2012 16:53:16 GMT
如上所示,返回的响应头信息,每一行都是以(‘ ‘)分割,并且该规则所有操作系统都一样。
name和value之间以冒号+空格隔开": " ,是一个固定的规范。
所以想要获取一个name,value是对象的形式,可以如下操作:
xhr.onreadystatechange = function() { // 只有在readyState >=3 的时候,才能获取正确的结果。 if (xhr.readyState ===3 || xhr.readyState === 4) { const allHeaders = xhr.getAllResponseHeaders(); // 注意需要清空两头的" ",否则会多出"":undefined const resultObj = allHeaders.trim().split(" ").reduce((memo, next) => { let [name, value] = next.split(": "); if (memo[name]) { // 如果请求头重复 memo[name] = `${memo[name]}; ${value}`; } else { memo[name] = value; } return memo }, {}) console.log(resultObj); } }
5.getResponseHeader(name)
获取特定名称的响应头信息,除了Set-Cookie和Set-Cookie2
xhr.getResponseHeader(‘Content-Type‘);
6.abort()
终止请求。xhr.abort();
7. XHR相关事件
1.onreadystatechange:
当readyState状态改变时触发;但重置时,即变为0,不触发。
2. onloadstart:
当掉用send()方法时,触发该事件;
3. onprogress:
分为文件上传进度: xhr.upload.onprogress send()之后,readyState=2之前触发
下载进度 xhr.onprogress readyState=3触发
4.onload:
readyState = 4触发
5. onloadend:
请求结束(成功或者失败)触发
6.onabort:
调用xhr.abort()触发
7.onerror:
请求失败触发;网络问题或者请求路由有误。
8.ontimeout:
从onloadstart开始,超过设置的timeout时间触发;timeout不为0。
8.xhr的上传对象
xhr上有个upload属性,可以用来描述文件上传的状态。
它有除了onreadystatechange之外,xhr有的其他7个事件。
以上是关于XMLHttpRequest剖析的主要内容,如果未能解决你的问题,请参考以下文章