AJAX
XMLHttpRequest
构造方法
没有任何参数,只是生成了一个实例
实例属性
状态
属性名 | 描述 |
---|---|
XMLHttpRequest.readyState |
返回一个整数,表示实例对象的状态;0=实例生成但未调用open() 方法、1=open() 方法调用但未调用send() 方法,可使用setRequestHeader() 方法设置请求头信息、2=send() 方法已经调用并且收到了响应头、3=正在接收传来的响应体,若responseType 属性等于text 或者空字符串说明responseText 属性就会包含已经收到的部分信息、4=响应体全部接收完毕或接收失败 |
XMLHttpRequest.timeout |
设置请求超时时间,以毫秒为单位 |
响应行
属性名 | 描述 |
---|---|
XMLHttpRequest.status |
返回响应状态码,请求发出之前是0 |
XMLHttpRequest.statusText |
返回响应状态信息 |
响应体
属性名 | 描述 |
---|---|
XMLHttpRequest.response |
返回成功的响应体,若失败或成功但是数据不完整该属性值为null,但是若responseType 属性等于text 或者空字符串,在请求未结束前包含部分请求体,是只读属性 |
XMLHttpRequest.responseType |
是一个字符串,表示服务器返回的数据类型,在调用open() 方法后send() 方法之前可设置这个属性告诉服务器返回指定数据类型;空字符串和text =字符串、arraybuffer =ArrayBuffer 对象二进制数组、blob =Blob 对象二进制对象、document =Document 对象文档对象、json =JSON对象 |
XMLHttpRequest.responseText |
返回服务器接收的字符串,若responseType 属性等于text 或者空字符串,在请求未结束前包含部分请求体,是只读属性 |
XMLHttpRequest.responseXML |
返回服务器接收的HTML或XML,若不能被解析成XML或 HTML则为null(部分数据不能被正确解析所以也为null),是只读属性 |
XMLHttpRequest.responseURL |
返回返回响应的服务器URL,这个属性不一定和请求的服务器URL一致,因为请求可以在服务端跳转,此外还会将URL中的锚点剔除 |
XMLHttpRequest.withCredentials |
布尔值,表示跨域请求时是否发送请求域的Cookie,默认为false |
监听函数
所有XMLHttpRequest
的监听事件绑定,都必须在send()
方法调用之前
属性名 | 描述 |
---|---|
XMLHttpRequest.onreadystatechange |
readystatechange 事件的监听函数,readyState 属性变化时触发 |
XMLHttpRequestEventTarget.ontimeout |
timeout 事件的监听函数,请求超时触发 |
XMLHttpRequest.upload |
用AJAX上传文件时,该属性是一个拥有一系列监控进度事件的对象,可监控上传进度和状态 |
XMLHttpRequest.onprogress |
progress 事件的监听函数,正在发送和加载数据持续触发 |
XMLHttpRequest.onabort |
abort 事件的监听函数,用户主动触发(即调用调用abort() 方法),因为错误中止不会触发 |
XMLHttpRequest.onerror |
error 事件的监听函数,因为错误导致中止时触发 |
XMLHttpRequest.onload |
load 事件的监听函数,请求成功时触发 |
XMLHttpRequest.onloadstart |
loadstart 事件的监听函数,请求发出时触发 |
XMLHttpRequest.onloadend |
loadend 事件的监听函数,请求完成时触发,不管成功或失败 |
实例方法
方法名 | 描述 |
---|---|
XMLHttpRequest.open(method,URI,[flag,user,passwd]) |
设置请求行(method需要大写的请求方法字符串,URI是相对路径时是相对与当前页面的URL)、是否是异步请求(默认false),身份认证信息(默认空字符串,一般用不着) |
XMLHttpRequest.send(data) |
设置发送的请求体并开始发送请求,可以是字符串、FormData 、ArrayBufferView 和Blob |
XMLHttpRequest.abort() |
终止发出的请求,readyState 变为4,status 变为0 |
XMLHttpRequest.setRequestHeader(key,value) |
设置请求头信息 |
XMLHttpRequest.overrideMimeType(MIME) |
覆盖服务器返回的MIME类型,不过最好还是使用responseType 告诉服务器要使用的什么类型,若服务器无法返回指定类型时才会使用该方法进行覆盖;必须在send() 方法之前调用 |
XMLHttpRequest.getResponseHeader(lab) |
获取指定响应头,若不存在返回null,若有多个相同的头会将多个头的值连成字符串并用逗号空格分隔 |
XMLHttpRequest.getAllResponseHeaders() |
获取所有响应头 |
AJAX示例
步骤:
- 创建XMLHttpRequest实例
- 发出HTTP请求
- 接收服务器传回数据
- 更新网页数据
let xhr = new XMLHttpRequest(); //创建XMLHttpRequest实例
xhr.onreadystatechange=function(){ //设置AJAX状态监听函数
if(xhr.readyState === 4){ //若AJAX处于结束状态
if(xhr.status === 200){ //若响应状态码是200
console.log(xhr.response); //打印请求体数据
}else{
console.log(xhr.statusText); //否则打印请求状态信息
}
}
}
xhr.open("GET","/path",true); //设置请求头
xhr.send(); //发出请求,未设置请求体
文件上传
ArrayBuffer对象
表示一段二进制数据,用来模拟内存里面的数据,通过这个对象,JavaScript可以读写二进制数据
构造方法
- 接收一个参数,表示这段二进制数据占用多少字节
实例属性
属性名 | 描述 |
---|---|
ArrayBuffer.prototype.byteLength |
返回所分配的内存区域的字节长度 |
实例方法
属性名 | 描述 |
---|---|
ArrayBuffer.prototype.slice(start[,end]) |
返回的子ArrayBuffer 对象 |
Blob对象
表示一个二进制文件的数据内容,它与 ArrayBuffer
的区别在于,它用于操作二进制文件,而 ArrayBuffer
用于操作内存
构造方法
参数 | 描述 |
---|---|
第一个参数(必须) | 数组,成员是二进制对象或字符串,表示新生成的Blob 实例对象的内容 |
第二个参数(可选) | 是一个配置对象,只有一个type 属性表示数据的MIME类型,默认是字符串 |
实例属性
属性名 | 描述 |
---|---|
Blob.prototype.size |
返回数据的大小 |
Blob.prototype.type |
返回数据的MIME类型 |
实例方法
属性名 | 描述 |
---|---|
Blob.prototype.slice([start,end,contentType]) |
返回的子Blob 对象,起始位置和结束位置默认是0和末尾,contentType 是数据的MIME 类型,默认为空字符串 |
FormData对象
通过脚本的形式填写表单,通过AJAX发送
构造方法
参数 | 描述 |
---|---|
无参 | 表示空白表单 |
一个表单元素 | 会关联这个表单 |
实例方法
方法名 | 描述 |
---|---|
FormData.get(key) |
根据键名获取键值,若有多个同名键值对只会返回第一个 |
FormData.getAll(key) |
根据键名获取键值对,返回是一个数组,包含所有同名键的键值 |
FormData.set(key,value) |
设置键值对,若键名重复会覆盖,若第二个参数是文件,还可以使用第三个参数用来表示文件名 |
FormData.append(key,value) |
添加一个键值对,若键名重复会生成两个相同键名的键值对,若第二个参数是文件,还可以使用第三个参数用来表示文件名 |
FormData.delete(key) |
根据键名删除一个键值对 |
FormData.has(key) |
判断是否具有该键名的键值对 |
FormData.keys() |
返回一个迭代器对象,用于迭代所有的键名 |
FormData.values() |
返回一个迭代器对象,用于迭代所有的键值 |
FormData.entries() |
返回一个迭代器对象,用于迭代所有的键值对 |
FileList对象
FileList是一个存放File对象的集合,是一个伪数组,最常见的使用场合
- 文件表单控件
<input type="file" multiple>
,multiple
表示可以多选,该控件的files
属性就是用户上传的文件列表,即时没有设置multiple
属性只能选择一个文件的情况下,也是一个包含一个元素的FileList集合 - 拖拽事件中的
DataTransfer.files
属性,返回一个FileList实例
File对象
一个File对象代表一个文件,存放在FileList集合中
构造方法
参数 | 描述 |
---|---|
第一个参数(必须) | 数组,成员是二进制对象或字符串,表示文件内容 |
第二个参数(必须) | 字符串,表示文件名或文件路径 |
配置对象(可选) | type:MIME类型字符串,默认是空字符串;lastModified:时间戳表示最后修改时间,默认是现在 |
实例属性
属性名 | 描述 |
---|---|
File.lastModified |
文件最后修改时间 |
File.name |
文件名 |
File.size |
文件大小 |
File.type |
文件类型 |
FileReader对象
对象用于读取File对象所包含的文件内容
构造方法
没有参数,只是生成实例
实例属性
状态与结果
属性名 | 描述 |
---|---|
FileReader.error |
读取文件时产生的错误对象 |
FileReader.readyState |
返回一个整数,表示实例对象的状态;0=尚未加载任何数据,1=数据正在加载,2=加载完成 |
FileReader.result |
读取完成后的文件内容,有可能是字符串,也可能是一个ArrayBuffer实例 |
监听函数
属性名 | 描述 |
---|---|
FileReader.onprogress |
progress 事件监听函数,读取时持续触发 |
FileReader.onabort |
abort 事件监听函数,用户主动触发(即调用调用abort() 方法),因为错误中止不会触发 |
FileReader.onerror |
error 事件监听函数,因为错误导致中止时触发 |
FileReader.onload |
load 事件监听函数,读取成功时触发 |
FileReader.onloadstart |
loadstart 事件监听函数,开始读取时触发 |
FileReader.onloadend |
loadend 事件监听函数,读取完成时触发,不管成功或失败 |
实例方法
方法名 | 描述 |
---|---|
FileReader.abort(file) |
终止读取操作,readyState 属性将变成2 |
FileReader.readAsArrayBuffer(file) |
以ArrayBuffer 的格式读取文件,读取完成后result 属性将返回一个 ArrayBuffer 实例 |
FileReader.readAsBinaryString(file) |
读取完成后,result 属性将返回原始的二进制字符串 |
FileReader.readAsDataURL(file) |
读取完成后,result 属性将返回一个Base64 编码的字符串,代表文件内容;对于图片文件,这个字符串可以用于<img /> 元素的src 属性上,由于这种便利性,这个字符串不能直接进行Base64解码,必须把前缀data:*/*;base64 从字符串里删除以后,再进行解码 |
FileReader.readAsText(file[,encodeing]) |
读取完成后,result 属性将返回文件内容的文本字符串,第二个参数默认是utf-8 |
表单提交文件上传
<form method="post" enctype="multipart/form-data" action="#">
<!-- 必须设置为post请求方式,而且enctype要设置为multipart/form-data表示混合表单数据 -->
<input type="file" name="file" multiple />
<!-- 文件表单控件,multiple表示可以多选 -->
<input type="submit" name="submit" value="上传" />
<!-- 提交按钮 -->
</form>
AJAX脚本文件上传
<input type="file" id="file" name="file" multiple />
<!-- 文件表单控件,multiple表示可以多选 -->
<input type="button" id="button" value="上传" />
<!-- 上传按钮 -->
<script>
var button = document.getElementById("button"); //获取页面上传按钮
button.addEventListener("click", function (e) { //为上传按钮绑定点击事件
var file = document.getElementById("file"); //获取页面文件表单控件
var files = file.files; //获取要上传的文件列表
if (files.length > 0) { //若选择了文件
var formData = new FormData(); //创建表单对象
for (var i = 0; i < files.length; i++) { //遍历要上传的文件列表
formData.append("files", files[i], files[i].name);
//放入文件表单对象中,不能使用set()方法因为会被覆盖掉
}
// console.log(formData.getAll("files")); //可用此语句打印调试表单对象中是否有文件列表
}
var xhr = new XMLHttpRequest(); //创建XMLHttpRequest实例
xhr.open('POST', '/up', true); //设置请求行
xhr.onload = function () { //绑定上传成功时的回调
if (xhr.status !== 200) { //若响应状态码不是200
console.err("上传失败");
}
};
xhr.send(formData); //发送表单对象
})
</script>
也可以直接发送文件,但是仅限一个文件
<input type="file" id="file" name="file" />
<!-- 文件表单控件,multiple表示可以多选 -->
<input type="button" id="button" value="上传" />
<!-- 上传按钮 -->
<script>
var button = document.getElementById("button"); //获取页面上传按钮
button.addEventListener("click", function (e) { //为上传按钮绑定点击事件
var file = document.getElementById("file").files[0]; //获取页面文件表单控件
// console.log(file); //可用此语句打印调试是否有文件
if (file != null) { //若选中文件
var xhr = new XMLHttpRequest(); //创建XMLHttpRequest实例
xhr.open('POST', '/up', true); //设置请求行
xhr.setRequestHeader('Content-Type', file.type); //设置请求头中发送的文件类型为当前文件类型
xhr.onload = function () { //绑定上传成功时的回调
if (xhr.status !== 200) { //若响应状态码不是200
console.err("上传失败");
}
};
xhr.send(file); //发送文件对象
}
})
</script>
同源限制
目的是为了保证用户信息的安全,网页A的设置的Cookie、LocalStorage和IndexedDB,网页B不能打开,发送的AJAX请求浏览器也拒绝响应,除非两个网页同源,同源是指:协议、域名和端口都相同
避免同源限制方法
Cookie
客户端:设置两个页面的document.domain
属性为同一个域名,浏览器是通过该属性来判断是否同源的
服务端:在设置Cookie时,使得Cookie的属性中domain
值为相同或子域名可访问
iframe多窗口通信
在一个网页中嵌入另一个网页时(也适用于window.open()
方法打开的窗口对象),每个网页都会有一个window对象,若两个窗口不是同源,就无法使用另一个窗口的DOM对象
同样可以使用document.domain
属性设置为同一个域来避免同源限制
通信方案
基于hash通信:URL中的hash部分发生变化页面是不会刷新的,父子窗口都可以拿到彼此的window
对象,然后在再修改location.hash
就可以传递消息,父子窗口通过window,onhashchange
来绑定hashchange
事件的监听函数,监听函数中通过location.hash
拿到传递的消息后,就可以做一系列操作
H5新增的方法:window.postMessage(message,URL)
,通过向指定URL的窗口(若设置为*
,表示所有窗口)发送消息,无论这两个窗口是否同源,接收方若需要使用window.onmessage
绑定message
事件的监听函数,该监听函数的事件对象共有三个属性:source
=发送消息的窗口对象、origin
=发送的目标URL也就是发送方设置的URL、data
发送的消息内容
AJAX的跨域请求
JSONP
使用script标签向服务器请求一个脚本,这不受跨域影响,所以可以为script标签的src参数填写URL时增加一个查询参数来指定返回结果时的回调函数,让服务器返回的脚本信息是调用该回调函数的脚本,限制就是只能是GET请求
示例
function addScriptTag(src) { //动态的插入script标签,请求地址由外部传入
var script = document.createElement("script"); //创建script标签
script.setAttribute("type", "text/javascript"); //添加该属性表示是javascrip脚本
script.src = src; //添加src属性为要请求的地址
document.body.appendChild(script); //将该script插入到页面中
}
function fun(data){ //服务器一旦返回直接调用的该函数
console.log(data.id); //由于要求服务器返回的是JavaScript脚本,参数就是对象,所以可以直接以对象形式调用
}
window.onload = function () { //当页面加载时
addScriptTag("http://www.baidu.com/?callback=fun"); //调用jsonp跨域请求
//服务器收到这个请求后需要返回以下形式脚本数据
//fun({
// id:1,
// name:zhangsan
//})
}
WebSocket
使用WebSocket进行协议进行通信,该协议不实行同源政策,只要服务器支持,就可以通过他进行跨域通信
CORS(跨域资源共享)
需要浏览器和服务器都支持,目前所有浏览器都支持,整个CORS通信过程,浏览器都是自动完成的,浏览器一旦发现AJAX请求跨域,就会自动附加一些头部信息,所以实现CORS通信的关机是服务器
简单请求
简单的说简单请求就是简单的请求方法和简单的请求头的结合,表单请求也是简单请求,表单请求在历史上是一直可以跨域请求的
-
请求方法是:
HEAD
、GET
、POST
-
请求头只有:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
:仅限三个值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
简单请求流程
浏览器直接发出CORS请求,在请求头中添加一个Origin
字段来标明本次请求来自哪个域(协议+域名+端口)
- 若该域不在许可范围内,服务器会返回一个正常的HTTP响应,这个正常的响应中没有包含
Access-Control-Allow-Origin
字段,所以浏览器就会忽略掉接收请求,就会抛出一个错误被XMLHttpRequest
的onerror
回调函数捕获,这种错误是无法使用状态码来判断的,因为是一个正常的HTTP响应 - 若该域在许可范围内,服务器就会在响应头中添加以下三个字段
Access-Control-Allow-Origin
:该字段是必须的,要么是*
表示可接受任意域请求,要么与请求时的Origin
的值相同(要注意域名和IP的映射关系浏览器是不知道的,所以也属于不同域,而且只能设置一个值,若想设置多个就需要在程序中实现)Access-Control-Allow-Credentials
:该字段可选,它的值是一个布尔值,表示是否允许浏览器发送Cookie,默认不发送(不设置就相当于false),只能设置为true;若只是将XMLHttpRequest
的withCredentials
设置为true,但是服务器不允许发送Cookie,则浏览器不会将响应内容返回给请求者,只有两者同时为true才行;设置上传Cookie后Access-Control-Allow-Origin
字段就不能使用*
,必须与请求网页同域,Cookie依然遵循同源政策,也就是上传是AJAX请求域的Cookie而不是当前页面的Cookie,当前页面也无法拿到AJAX请求域的CookieAccess-Control-Expose-Headers
:该字段可选,在CORS请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到6个服务器返回的基本字段,若想使用该方法拿到其他的字段(包括自定义字段)就需要在该字段指定;6个基本字段包括:``Cache-Control、
Content-Language、
Content-Type、
Expires、
Last-Modified和
Pragma`
非简单请求
非简单请求的CORS请求,会在正式通信前增加一次HTTP查询请求,询问服务器当前页面域名是否在服务器允许跨域白名单中,以及可以使用哪些HTTP方法和请求头字段,只有得到肯定的答复,浏览器才会发出正式的AJAX请求,否则就会报错
比如:请求方法是PUT
或DELETE
,Content-Type
字段的类型是application/json
1. 预检请求
- 预检请求使用的是
OPTIONS
方法 - 主要有这几个字段来进行询问
Origin
:标明请求来自哪个域Access-Control-Request-Method
:要使用的请求方法Access-Control-Request-Headers
:要使用哪些自定义请求头字段,多个可使用逗号分隔
2. 预检请求响应
- 主要有对应的字段来进行响应
Access-Control-Allow-Origin
:允许跨域的域Access-Control-Allow-Methods
:允许使用的请求方法,多个用逗号分隔Access-Control-Allow-Headers
:允许使用的自定义请求头字段,多个用逗号分隔Access-Control-Allow-Credentials
:是否需要发送Cookie,与简单请求中的一致Access-Control-Max-Age
:指定多久之内不需要在发送预检请求,以秒为单位
- 允许:上述的字段的值都应该大于等于(预检请求的值都包含在预检响应中)请求允许
- 不允许:返回一个正常的HTTP响应,但是没有任何CORS相关字段或者明确表示请求不符合条件,这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被
XMLHttpRequest
对象的onerror
回调函数捕获
3. 浏览器的正常请求和响应
一旦预检请求通过后,就都跟简单请求一样,请求头会增加一个Origin
头信息字段,响应体会增加一个Access-Control-Allow-Origin
字段
Comments NOTHING