XMLHttpRequest
基本格式,readyState,HTTP头部,超时,判断响应的MIME类型,进度事件,跨源(跨域),预检请求,凭据请求
基本格式:
let xhr = new XMLHttpRequest()
【1】
xhr.open(method, url, async)//method: string, url: string, async?: boolen是否异步
//如果method是get,参数要加在url后,post请求则可以写在send()里
xhr.send(null)//这里必须发送数据,如果没有,就为null(必须)
如果是同步的:
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
console.log(xhr.responseText.slice(0, 20));
} else {
console.log('failed', xhr.status);
}
如果是异步的就需要用onreadystatechange监视readyState的变化,从而在请求完成之后获取结果
readyState: number
0: 未初始化,未open
1: 已打开,open()
2: 已发送,send()
3: 接受中,以有部分响应,可以使用部分结果
4: 完成,已收到所有响应
应该在【1】出添加如下代码
xhr.onreadystatechange = function () {
console.log('readyState: ', xhr.readyState);
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
console.log(xhr.responseText.slice(0, 20));
} else {
console.log('failed', xhr.status);
}
}
HTTP头部
浏览器会自动发送一些请求头,如Accept,Host等,若需要发送额外的请求头,需要用到xhr.setRequestHeader(headerName: string, keaderValue: string)
,在open()和send()之间使用。读取请求头的两种方法:
xhr.getResponseHeader(headerName: string)
,xhr.getAllResponseHeaders()
超时timeout与ontimeout
xhr.onreadystateChange = function() {
/*
之所以要try/catch是因为即时超时也会走到xhr.readyState=4这一步,
只是在超时后访问xhr.status会发送错误
*/
if (xhr.readySate != 4) return
try {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
console.log(xhr.responseText.slice(0, 20));
} else {
console.log('failed', xhr.status);
}
} catch(e) {...}
}
xhr.open(..., true)
xhr.timeout = 1000 //1000ms未响应是为超时
xhr.ontimeout = function() {...} //监听超时的函数
xhr.send()
判断响应的MIME类型
响应返回的MIME类型决定了XHR的处理方式。如果浏览器返回的是XML,而响应头的MIME类型默认或者被设置成text/plain结果就会导致responseXML获取为null,即被错误的方式处理了。
xhr.open(...)
xhr.overrideMinmeType('text/xml')
xhr.send(...)
进度事件
名字 | 什么时候触发 |
---|---|
loadstart | 接收到响应返回的第一个字节的时候 |
progress | 在接收响应期间反复触发,进度条 |
error | 请求错误时 |
abort | 调用xhr.abort()终止请求时 |
load | 成功完成响应接收时 |
loadend | 完成响应时,成不成功无所谓,abort,error之后也会触发;在load之后 |
progress的两个常用属性:position,totalSzie;都是number。已经接收的大小,总的大小
//异步请求上述功能才有意义
xhr.onprogress = fucntion (event) {
console.log('已经接收:', event.position / event.totalSize * 100, '%')
}
跨源(跨域)
处于安全,XHR只能访问与发起请求的页面在同一域内的资源。同一域:协议,域名/IP,端口都相同。但是也允许合法跨域。
对于常规的请求如get, post等,都会自带一个text/plain类型的请求头叫Origin,值为发起请求的页面的源(域)。
Origin: http://wwww.baidu.com
服务器端如果允许这个源访问的话,就要发送请求头:
'Access-Control-Allow-Origin': 'http://www.baidu.com'
如果全部都允许的话可以'Access-Control-Allow-Origin': '*'
以expressJs为例:
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*'); //就是这句
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200);
} else next();
});
为了安全:前面提到的setRequestHeader()
可以设置请求头,跨域请求的话就不允许使用这个方法了;也不能发送和接收cookie,getAllResponseHeaders()
返回值也为空。
所以不论同域还是跨域时都建议:访问本地资源用相对路径,远程资源用绝对路径,一来区分使用场景,二来避免出现访问本地资源时头部/cookie使用受限问题
预检请求
这是个服务器验证机制,是自动的,知道就行
第一次向某地址发送请求之前,浏览器会以options方法向该地址发送一个请求,请求的参数包含:
- Origin: string 如:'http://www.baidu.com'
- Access-Control-Request-Headers: string 如:'Content-Type, Content-Length'
- Access-Control-Request-Methods: string 如:'DELETE, PUT, UPDATE'
通过这个请求来检测这个源允许哪些源来请求,允许哪些请求头,请求方法。
而服务器端的跨域处理会向浏览器发送允许的类型,与浏览器沟通,后端代码就如上面那个一样,浏览器收到的也是
凭据请求
暂时不懂有什么用
前端:
默认情况下,跨域请求不提供凭据(即cookie,HTTP认证和客户端SSL证书)。但是可以通过xhr.withCredentials = true
来告诉服务端"我"会发送凭据
后端:
如果允许带凭据请求,就要满足以下几点:
- Access-Control-Allow-Origin不能为*
- Access-Control-Allow-Credentials为true
上面前后端两个有任何一个不满足都会触发xhr.onerror
完整&简略的写法
前端:
xhr.open(...)
xhr.withCredentials = true //【1】会发送凭据
xhr.send(...)
后端:(expressJs为例)
/* app.js */ 跨域处理
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://xxx.com') //【2】此处不能为 *
})
/* index.js */路由
这个接口允许凭据请求
index.get('/allowCredential', (req, res, next) => {
res.header("Access-Control-Allow-Credentials", true) //【3】允许发送凭据请求
res.send(...)
})