JavaScript学习笔记整理(14):AJAX
- 创建AJAX对象(实例化XMLHttpRequest对象)
- 发起HTTP请求
- 接收服务器传回的数据
- 更新网页数据
var xhr = new XMLHttpRequest();
if(window.XMLHttpRequest === undefined){
window.XMLHttpRequest = function(){
try{
//如果可用,则使用ActiveX对象的最新版本
return new ActiveXObject('Msxml2.XMLHTTP.6.0');
}catch(e1){
try{
//否则,回退到较旧的版本
return new ActiveXObject('Msxml2.XMLHTTP.3.0');
}catch(e2){
throw new Error('XMLHttpRequest is not supported');
}
}
}
}
- HTTP请求方法或动作(verb)
- 正在请求的URL
- 一个可选的请求头集合,其中可能包括身份验证信息
- 一个可选的请求主体
- 一个数字和文字组成的状态码,用来显示请求的成功和失败
- 一个响应头集合
- 响应主体
xhr.open('GET','example.php');
xhr.setRequestHeader('Content-Type','text/plain');
xhr.send(null);
- status和statusText属性以数字和文本的形式返回HTTP状态码。这些属性保存标准的HTTP值。像200和“OK”表示成功请求,404和“Not Found”表示URL不能匹配服务器上的任何资源。
- 使用getResponseHeader()和getAllResponseHeaders()能查询/获取响应头。XMLHttpRequest会自动处理cookie:它会从getAllResponseHeaders()头返回集合中过滤cookie头,而如果给getResponseHeader()传递“Set-Cookie”和“Set-Cookie2”,则返回null。
- 响应主体可以从responseText属性得到文本形式的,从responseXML属性中得到Document形式的。
xhr.onreadystatechange=function(){}
xhr.open('GET','example.php',false);
function get(url,callback){
var xhr = new XMLHttpRequest(); //创建新请求
xhr.open('GET',url);
xhr.onreadystatechange=function(){
//如果请求完成且成功
if(xhr.readyState === 4 && xhr.status === 200){
//获得响应的类型
var type = xhr.getResponseHeader('Content-type');
if(type.indexOf('xml') !== -1 && xhr.responseXML){
callback(xhr.responseXML); //Document对象响应
}else if(type === 'application/json'){
callback(JSON.parse(xhr.responseText)); //JSON响应
}else{
callback(xhr.responseText); //字符串响应
}
}
};
xhr.send(null); //立即发送请求
}
user=TG&age=18;
application/x-www-form-urlencoded
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
/*
* {
* name:'TG',
* age:18
* }
*/
function encodeFormData(data){
if(!data) return '';
var pairs = [];
for(var name in data){
if(!data.hasOwnProperty(name)) continue;
if(typeof data[name] === 'function') continue;
var 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/json');
xhr.send(JSON.stringify(data));
0,对应常量UNSENT,表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用。
1,对应常量OPENED,表示open()已调用,但send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息。
2,对应常量HEADERS_RECEIVED,表示send()方法已经执行,并且头信息和状态码已经收到。
3,对应常量LOADING,表示正在接收服务器传来的主体(body)部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息。
4,对应常量DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。
200, OK,访问正常
301, Moved Permanently,永久移动
302, Move temporarily,暂时移动
304, Not Modified,未修改
307, Temporary Redirect,暂时重定向
401, Unauthorized,未授权
403, Forbidden,禁止访问
404, Not Found,未发现指定网址
500, Internal Server Error,服务器发生错误
if(xhr.readyState === 4){
//请求完成
if(xhr.status === 200){
//请求成功
}
}
(3)statusText
statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。
(4)timeout
timeout属性等于一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。
对应超时,还有个监听函数:
xhr.ontimeout = function(){
//请求超时
}
(5)response
response属性为只读,返回接收到的数据体(即body部分)。它的类型可以是ArrayBuffer、Blob、Document、JSON对象、或者一个字符串,这由XMLHttpRequest.responseType属性的值决定。
如果本次请求没有成功或者数据不完整,该属性就会等于null。
(6)responseType
responseType属性用来指定服务器返回数据(xhr.response)的类型。
”“:字符串(默认值)
“arraybuffer”:ArrayBuffer对象
“blob”:Blob对象
“document”:Document对象
“json”:JSON对象
“text”:字符串
text类型适合大多数情况,而且直接处理文本也比较方便,document类型适合返回XML文档的情况,blob类型适合读取二进制数据,比如图片文件。
(7)responseText
responseText属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。
如果服务器返回的数据格式是JSON,就可以使用responseText属性。
var data = xhr.responseText;
data = JSON.parse(data);
(8)responseXML
responseXML属性返回从服务器接收到的Document对象,该属性为只读。如果本次请求没有成功,或者数据不完整,或者不能被解析为XML或HTML,该属性等于null。
返回的数据会被直接解析为DOM对象。
(9)事件监听接口
XMLHttpRequest第一版,只能对onreadystatechange这一个事件指定回调函数。该事件对所有情况作出响应。 XMLHttpRequest第二版允许对更多的事件指定回调函数。
onloadstart 请求发出
onprogress 正在发送和加载数据
onabort 请求被中止,比如用户调用了abort()方法
onerror 请求失败
onload 请求成功完成
ontimeout 用户指定的时限到期,请求还未完成
onloadend 请求完成,不管成果或失败
1.6.XMLHttpRequest实例的方法
(1)abort()
abort方法用来终止已经发出的HTTP请求。
(2)getAllResponseHeaders()
getAllResponseHeaders方法返回服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分隔,如果没有受到服务器回应,该属性返回null。
(3)getResponseHeader()
getResponseHeader方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null。
如果有多个字段同名,则它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。
(4)open()
XMLHttpRequest对象的open方法用于指定发送HTTP请求的参数,它的使用格式如下,一共可以接受五个参数。
void open( string method, string url, optional boolean async, optional string user, optional string password);
参数说明:
method:表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。
url: 表示请求发送的网址。
async: 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。
user:表示用于认证的用户名,默认为空字符串。
password:表示用于认证的密码,默认为空字符串。
(5)send()
send方法用于实际发出HTTP请求。如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。
(6)setRequestHeader()
setRequestHeader方法用于设置HTTP头信息。该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));
在上面代码中,首先设置头信息Content-Type,表示发送JSON格式的数据;然后设置Content-Length,表示数据长度;最后发送JSON数据。
(6)overrideMimeType()
该方法用来指定服务器返回数据的MIME类型。该方法必须在send()之前调用。// 强制将MIME改为文本类型
xhr.overrideMimeType('text/plain; charset=x-user-defined');
在XMLHttpRequest版本升级后,一般采用指定的responseType的方法。
xhr.responseType = 'text';
<form id="form" action="upload.php" method="POST">
<input type="file" id="files" name="photos[]"/>
<button type="submit" id="upload">上传</button>
</form>
<input type="file" multiple/>
var fileInput = document.getElementById('files');
var files = fileInput.files;
fileInput.addEventListener('change',function(){
var file = this.files[0];
if(!file) return;
var xhr = new XMLHttpRequest();
xhr.open('POST','upload.php');
xhr.send(file);
},false);
xhr.setRequestHeader('Content-Type', file.type);
var formdata = new FormData();
for(var i = 0;i < files.length; i++){
var file = files[i];
formdata.append('photos[]',file,file.name);
}
xhr.send(formdata);
// Files
formdata.append(name, file, filename);
// Blobs
formdata.append(name, blob, filename);
// Strings
formdata.append(name, value);
3、HTTP进度事件
除了使用readystatechange事件来探测HTTP请求的完成外,在XHR2中,还定义了多个有用的事件。
abort事件:当进度事件被中止时触发。如果发生错误,导致进程中止,不会触发该事件。
error事件:由于错误导致资源无法加载时触发。
load事件:进度成功结束时触发。
loadstart事件:进度开始时触发。
loadend事件:进度停止时触发,发生顺序排在error事件\abort事件\load事件后面。
progress事件:当操作处于进度之中,由传输的数据块不断触发。
timeout事件:进度超过限时触发。
当调用send()时,触发单个loadstart事件。当正在加载服务器的响应时,XMLHttpRequest对象会发现progress事件,通常每隔50毫秒左右,可以使用这些事件给用户反馈请求的进度。当事件完成,会触发load事件。
HTTP请求无法完成有3种情况:
- 请求超时,会触发timeout事件
- 请求终止,会触发abort事件
- 请求发生错误,会触发error事件
注意:对于任何具体请求,浏览器将只会触发load、abort、timeout和error事件中的一个。一旦这些事件中的一个发生后,浏览器应该触发 loadend 事件。
要使用这些事件,有两种方式:
xhr.onload=function(){}
xhr.addEventListener('load',function(){})
3.1 progress事件
因为这些事件是XHR2中才定义的,所以有时需要检查浏览器是否支持progress事件:
if('onprogress' in (new XMLHttpRequest())){
//支持progress事件
}
除了像type和timestamp这样的常用Event对象属性外,与progress事件相关联的事件对象有3个有用的属性:
lengthComputable:返回一个布尔值,表示当前进度是否具有可计算的长度。如果为false,就表示当前进度无法测量。
total:返回一个数值,表示当前进度的总长度。如果是通过HTTP下载某个资源,表示内容本身的长度,不含HTTP头部的长度。如果lengthComputable属性为false,则total属性就无法取得正确的值。
loaded:返回一个数值,表示当前进度已经完成的数量。该属性除以total属性,就可以得到目前进度的百分比。
我们可以利用total和loaded属性来获取当前进度:
xhr.onprogress = function(e){
if(e.lengthComputable){
var percentComplete = e.loaded / e.total;
}
}
3.2 上传进度事件
在XHR2中,也提供了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象有一个upload属性,upload属性值是一个对象,它定义了addEventListener()方法和整个progress事件集合,比如onprogress和onload。
xhr.upload.onprogress = function(e){
var percentComplete = e.loaded / e.total;
}
3.3 中止请求和超时
我们可以通过调用XMLHttpRequest对象的abort()方法来取消正在进行的HTTP请求,调用abort()方法时,会触发abort事件。
在XHR2中,还定义了timeout属性来指定自动中止后的毫秒数。
xhr.timeout = 1000;
4、同源策略
同源策略是对JavaScript代码能够操作哪些Web内容的一条完整的安全限制。当Web页面使用多个<iframe>元素或打开其他浏览器窗口的时候,这一策略通常就会发挥作用。
所谓“同源”指的是”三个相同“。
- 协议相同
- 域名相同
- 端口相同
从不同Web服务器载入的文档具有不同的来源。通过同一主机的不同端口载入的文档具有不同的来源。使用http:协议载入的文档和使用https:协议载入的文档具有不同的来源,即使它们来自同一个服务器。
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
4.1 不严格的同源策略
有三种不严格的同源策略
(1)使用Document对象的domain属性
在默认情况下,属性domain存放的是载入文档的服务器的主机名。
比如:来自home.example.com的文档里的脚本要合法的读取developer.example.com载入的文档的属性(默认情况下是不允许的,会受到同源策略的限制)。
document.domain = 'example.com';
一旦上面两个文档里包含的脚本把domain都设置成了上面相同的值,那么这两个文档就不会受同源策略的约束了,可以相互读取对方的属性。
(2)跨域资源共享(Cross-Origin Resource Sharing)
这种方式是应用到后台中:
Access-Control-Allow-Origin:*,//允许所有域名的脚本访问该资源。
Access-Control-Allow-Origin:http://www.example.com //允许特定的域名访问。
(3)跨文档消息(cross-document messaging)
跨文档消息允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本。
4.2 跨站脚本
跨站脚本(Cross-site scripting,XSS),这个术语用来表示一类安全问题,也就是攻击者向目标Web站点注入HTML标签或脚本。
通常,防止XSS攻击的方式是,在使用任何不可信的数据来动态的创建文档内容之前,从中移除HTML标签。
5、Fetch API
Fetch API是一种新规范,用来取代XMLHttpRequest对象。
5.1 特性检测
if(self.fetch){
//支持
}else{
//不支持
}
在Fetch API中,最常用的就是fetch()函数,它接收一个URL参数(也可以是request对象),返回一个promise来处理response。response参数还带着一个Response对象。
fetch(url).then(function(response){
console.log(response);
});
fetch方法还可以设置第二个参数,用来配置其他值,可选的参数有:
method: 请求使用的方法,如 GET、POST。
headers: 请求的头信息,形式为 Headers 对象或 ByteString。
body: 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchPara ms 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
mode: 请求的模式,如 cors、 no-cors 或者 same-origin。
credentials: 请求的 credentials,如 omit、same-origin 或者 include。
cache: 请求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached.
下面是发出POST请求
fetch(url,{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'name=TG&love=1'
}).then(function(response){})
注意:fetch() 方法的参数与 Request() 构造器是一样的。
fetch(input, init).then(function(response) { ... });
var myRequest = new Request(input, init);
下面是一个Fetch API完整请求的简单例子:
fetch(url).then(function (response) {
return response.json();
}).then(function (jsonData) {
console.log(jsonData);
}).catch(function () {
console.log('出错了');
});
上面代码向指定的URL发出请求,得到回应后,将其转为JSON格式,输出到控制台。如果出错,则输出一条提示信息。
Fetch API引入三个新的对象(也是构造函数):Headers, Request和Response。
5.2 Headers
Headers对象用来构造/读取HTTP数据包的头信息。
reqHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "ProcessThisImmediately",
});
我们还可以使用append方法:
var content = 'Hello World';
var headers = new Headers();
headers.append("Accept", "application/json");
headers.append("Content-Type", "text/plain");
headers.append("Content-Length", content.length.toString());
headers.append("X-Custom-Header", "ProcessThisImmediately");
Headers对象实例还提供了一些方法:
reqHeaders.has("Content-Type") // true
reqHeaders.has("Set-Cookie") // false
reqHeaders.set("Content-Type", "text/html")
reqHeaders.append("X-Custom-Header", "AnotherValue")
reqHeaders.get("Content-Length") // 11
reqHeaders.getAll("X-Custom-Header") // ["ProcessThisImmediately", "AnotherValue"]
reqHeaders.delete("X-Custom-Header")
reqHeaders.getAll("X-Custom-Header") // []
生成Header实例以后,可以将它作为第二个参数,传入Request方法。
var headers = new Headers();
headers.append('Accept', 'application/json');
var request = new Request(URL, {headers: headers});
fetch(request).then(function(response) {
console.log(response.headers);
});
5.3 Request对象
Request对象用来构造HTTP请求。
var req = new Request("/index.html");
req.method // "GET"
req.url // "http://example.com/index.html"
Request对象的第二个参数,表示配置对象,
var uploadReq = new Request("/uploadImage", {
method: "POST",
headers: {
"Content-Type": "image/png",
},
body: "image data"
});
Request对象实例的mode属性,用来设置是否跨域,合法的值有以下三种:same-origin、no-cors(默认值)、cors。当设置为same-origin时,只能向同域的URL发出请求,否则会报错。
5.4 Response对象
Fetch API 的Response接口呈现了对一次请求的响应数据
5.4.1 属性
(1)ok
如果ok属性返回的状态码在200到299之间(即请求成功),这个属性为true,否则为false。因此,我们可以这样判断请求是否成功:
fetch(url).then(function(response){
if(response.ok){
//请求成功
}else{
//请求失败
}
});
(2)status、statusText
status属性返回HTTP的状态码;statusText返回一个字符串,表示服务器发送的状态提示。比如通信成功时,status是200,而statusText是“OK”
(3)url
返回完整的请求地址
(4)type
type属性表示HTTP回应的类型。合法的值有五个basic、cors、default、error、opaque。basic表示正常的同域请求;cors表示CORS机制的跨域请求;error表示网络出错,无法取得信息,status属性为0,
如果需要在CORS机制下发出跨域请求,需要指明状态。
fetch(url,{mode: 'cors'}).then(function(response){})
(5)headers
Headers对象,表示HTTP回应的头信息
(6)body
表示请求的内容。
Request对象和Response对象都有body属性,表示请求的内容。body属性可能是以下的数据类型。
ArrayBuffer
ArrayBufferView (Uint8Array等)
Blob/File
string
URLSearchParams
FormData
注意:上面这些方法都只能使用一次,第二次使用就会报错,也就是说,body属性只能读取一次。Request对象和Response对象都有bodyUsed属性,返回一个布尔值,表示body是否被读取过。
如果希望多次使用body属性,可以使用Response对象和Request对象的clone方法。它必须在body还没有读取前调用,返回一个新的body,也就是说,需要使用几次body,就要调用几次clone方法。
response.clone()
(7)bodyUsed
bodyUsed属性,返回一个布尔值,表示body是否被读取过。
5.4.2 Response对象的方法
(1)text()、json()、FormData()、blob()、arrayBuffer()
在Fetch API中,数据传送是以数据流(stream)的形式进行的。对于大文件,数据是一段一段得到的。
而Fetch API提供了五个数据流读取器。
text():返回字符串
json():返回一个JSON对象
formData():返回一个FormData对象
blob():返回一个blob对象
arrayBuffer():返回一个二进制数组
简单例子:
response.json().then(function(json){
console.log(json);
})
参考资料:
书籍:《JavaScript权威指南》
文章:阮一峰:Fetch API
文档:MDN:Fetch API