Professional Documents
Culture Documents
XMLHTTP Request
XMLHTTP Request
参考文献
JavaScript 权威指南:第 18 章 脚本化 HTTP
MDN:XMLHttpRequest
MDN:AJAX
MDN:HTTP 访问控制(CORS)
XMLHttpRequest
概念
使用
1) 创建 XMLHttpRequest 实例
let xhr = new XMLHttpRequest();
2)指定连接
xhr.open('GET', './info.json');
open 接收三个参数:方法、URL、是否异步。
方法,不区分大小写,但通常使用大写字母。
是否异步,也就是请求会被异步处理,如果设置为 false 表示同步请求,这就意味着在请求返
回之前,浏览器将处于阻塞状态,用户无法进行其他操作。
3)设置请求头
如果是 GET 请求,参数需要附加在 URL 上,如果是 POST 请求,通常会涉及到请求头
“Content-Type”在指定请求主体的 MIME 类型。
xhr.setRequestHeader('Content-Type', 'application/json');
如果对相同的头调用多次 setRequestHeader(),新值不会取代之前的旧值,这个请求头将包
含多个值。
4)发送请求
如果是 GET 请求,send 应该传递 null 或者省略该参数,POST 请求的请求数据需要使用 send
传递。其中根据传递数据格式的不同,需要设定与此对应的“Content-Type”。
xhr.send(body);
5)取得响应
监听方法:
o onreadystatechange:每一次 readyState 属性改变都会触发该事件
readyState
0:open 未调用
1:open 已调用
2:接收到头信息
3:接收到响应主体
4:响应完成
状态:
o status:状态码(数字),如 200,500
o statusText:状态文本,如"OK"
获取响应头:结果将过滤 cookie 头
o getResponseHeader()
o getAllResponseHeaders()
响应主体
o response
o responseText
o responseXML
}else{
console.log(xhr.requestText);
}
}
}
xhr.open('GET', './info.json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();
从代码中可以看到对不同类型响应数据的处理:
响应数据为 XML、XHTML 时
o 可以通过 requestXML 获取一个解析过的 Document 对象
响应数据为 JSON 等字符串数据时
o 可以通过 requestText 获取文本类型的数据
XMLHttpRequest 请求本地文件,显式跨域问题?
XMLHttpRequest 需要和 HTTP 和 HTTPS 协议一起使用。
当通过相对路径调用本地文件时,这些 URL 将相对于 file://URL 而不是 http://URL。
而同源策略通常会阻止使用绝对 http://URL。
所以在访问本地文件时,通常需要把文件上传到服务器,或者使用本地服务器。
编码请求主体
1) 表单编码
编码函数实现:
function encodeFormData(data){
if(!data) return "";
let pairs = [];
for (let name in data) {
if(!data.hasOwnProperty(name)) continue;
if(typeof data[name] === "function") continue;
let value = data[name].toString();
name = encodeURIComponent(name.replace("%20", "+"));
value = encodeURIComponent(value.replace("%20", "+"));
pairs.push(name + "=" + value);
}
return pairs.join("&");
}
请求实现:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(encodeFormData({ 'name': 'lin', 'age': '18', 'sex': '0' }))
Jquery 实现了对表单的序列化方法:serialize():
$("button").click(function(){
$("div").text($("form").serialize());
});
2) JSON 编码
JSON 的传输非常简单,只需要使用 JSON.stringify 进行序列化即可。
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({ 'name': 'lin', 'age': '18', 'sex': '0' }));
3) XML 编码
XMLHttpRequest 的 send 方法,可以传入 XML document 对象,而且不需要设定 Content-
Type,因为 XMLHttpRequest 对象会自动设置一个合适的头。
如果给 send 方法传入一个字符串,但没有指定 Content-Type 头时,XMLHttpRequest 默认
添加“text/plain; charset=UTF-8”。
字符串类型主体,通常会涉及三种编码类型:
text/plain
application/json
application/x-www-form-urlencoded
所以需要额外知道编码的类型,但对于其他可辨识的编码类型,可以忽略对 Content-Type 的
设置。
4) 上传文件(Blob)
XMLHttpRequest 的 send 方法,可以传入 File 对象来实现文件上传。
fileInput.addEventListener("change", function(){
let file = this.files[0];
if(!file) return;
let xhr = new XMLHttpRequest();
xhr.open("POST", "/upload/path");
xhr.send(file);
}, false);
5) FormData
当 HTML 表单同时包含文件上传元素和其他元素时,浏览器必须以“multipart/form-data”的
形式使用 POST 来提交表单,这种编码包括使用长边界字符串把请求主体分离成多个部分。
XHR2 定义了 FormData API,来实现多部分请求主体。需要先创建一个 FormData 实例,然
后通过实例的 append 方法添加数据,它可以是字符串、File 或 Blob 对象,然后将 FormData
对象传递给 send 方法。
同样,不需要手动设置“Content-Type“。
let formData = new FormData();
for (let name in data) {
if (!data.hasOwnProperty(name)) continue;
if (typeof data[name] === "function") continue;
formData.append(name, data[name]);
}
xhr.send(formData);
HTTP 进度事件
XMLHttpRequest 对象在请求的不同阶段触发不同类型的事件,在这些事件中不再需要检查
readyState。
onload:请求完成(完成的请求不一定是成功的请求,需判断状态码 status)
onloadend:请求结束(触发 load、abort、error、timeout 事件后执行)
onloadstart:请求接收到响应数据
onprogress:请求进程
onabort:请求中止
onerror:请求错误
ontimeout:请求超时
onload 表示请求完成,但不一定请求成功,那么 onerror 时在什么时候执行?
下载进度事件
与 progress 事件关联的事件对象有三个有用的属性:
loaded:表示目前传输的字节数值
total:表示自"Content-Length"头传输的数据的整体长度
lengthComputable:表示是否知道内容长度
具体的实现如下:
xhr.onprogress = function(e){
if(e.lengthComputable){
progress.innerHTML = Math.round(100 * e.loaded / e.total) + "% Complete";
}
}
上传进度事件
XMLHttpRequest 对象拥有 upload 属性,它是一个对象,定义了 addEventListener()方法和
整个 progress 事件集合。xhr.onprogress 监控响应的下载进度,xhr.upload.onprogress 监
控请求的上传进度。
xhr.upload.onprogress = function(e){
if(e.lengthComputable){
progress.innerHTML = Math.round(100 * e.loaded / e.total) + "% Complete";
}
}
中止请求和超时
中止请求
方法:调用实例的 abort()方法触发 abort 事件
原因:请求超时或者响应数据已不被需要
场景:用户检索,当用户在服务器响应之前又输入新的检索内容,此时的响应数据已
经过时
超时
方法:设置实例的 timeout 属性指定超时时间(ms),超时自动触发 timeout 事件
原因:响应时间超过 timeout 设定的时间
xhr.timeout = 3000;
xhr.ontimeout = function () {
console.log('请求超时');
xhr.abort();
}
xhr.onabort = function () {
console.log('请求中止');
}
跨域 HTTP 请求
在<form>和<iframe>元素中使用跨域 URL,浏览器显示最终的跨域文档,但因为同源策略,
浏览器不允许原始脚本查找跨域文档的内容。使用 XMLHttpRequest,文档内容都是通过
responseText 属性暴露,所以同源策略不允许 XMLHttpRequest 进行跨域请求。 --
[JavaScript 权威指南.P503]
理解:同源策略限制脚本获取跨域文档的内容,这一点同样适用于 XHR,在 XHR 中
responseText 既是对应的跨域文档内容,因此 XHR 也会收到同源策略的限制,不允许直接进
行跨域请求。
API
属性
XMLHttpRequest.onreadystatechange 当 readyState 改变时,调用的处理函数
XMLHttpRequest.responseText 返回 DOMString
XMLHttpRequest.responseType 定义响应类型
XMLHttpRequest.status 数字,代表请求的响应状态,200,304...
XMLHttpRequest.timeout 数字,表示最大请求时间(ms)
XMLHttpRequest.upload 对象,表示上传进度
XMLHttpRequest.withCredentials 布尔值,指定跨域请求是否携带授权信息
方法
XMLHttpRequest.abort() 中止请求
XMLHttpRequest.getAllResponseHeaders() 获取所有响应头
XMLHttpRequest.getResponseHeader() 获取指定响应头
XMLHttpRequest.open() 初始化一个请求
XMLHttpRequest.send() 发送请求
事件
abort 请求停止时触发,可使用 onabort 属性
progress 接受到更多数据时,周期性触发,可使用
onprogress 属性
readyState
值 状态 描述
4 DONE 下载操作已完成。