Open chenqunfeng opened 7 years ago
某天直接对一个接口发起跨域post请求时,虽然浏览器正常报跨域请求错误,但是却发现请求的接口居然有正常的返回值,并且是200,但是不会进去success回调而是进入error回调。这与我的认知出现的偏差,故有了这篇文章,多为记录的目的,以防遗忘。
XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和Level 2。
XMLHttpResquest Level 1:
主要属性:
主要缺点:
XMLHttpResquest Level 2:
新功能:
setRequestHeader(header, value)
注意点:
header参数大小写不敏感
该方法的调用必须在open()方法之后,send()方法之前,否则报错
该方法设置同一个header多次不会采用覆盖的方式,而是采用追加的方式
getAllResponseHeaders() getResponseHeader(header)
第一个方法获取全部headers,第二个方法根据传入header获取对应的header
两个方法都只能获取特定的header字段
xhr.readyState
xhr.timeout
xhr.ontimeout
open(method, false) // 异步 open(method, true) // 同步
注意点(当设置为同步请求时,需保证以下几点):
send(data) // data参数的数据类型会影响请求头部content-type的值
try{ xhr.send(data); } catch(e) { // ... }
xhr.overrideMimeType(type) // Level 1 中设置response数据类型 xhr.responseType = type // Level 2 中设置response数据类型
注意,Level 1 不支持设置二进制数据类型,但是可以通过特殊的方式获得
function _arrayBufferToBase64( buffer ) { // 如果buffer本身就是无符号整形,则不用经过处理也可以 // var bytes = buffer; var bytes = new Uint8Array( buffer ); // btoa可以将ascii字符串或二进制数据转换成一个base64编码的字符串,但是不能直接作用在Unicode字符串上,不过这里的图片数据buffer中最大不超过255,故没有问题 return window.btoa( String.fromCharCode.apply(this, bytes) ); } var xhr = new XMLHttpRequest(); //向 server 端获取一张图片 xhr.open('GET', '/path/to/image.png', true); // 这行是关键! //将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集 xhr.overrideMimeType('text/plain; charset=x-user-defined'); xhr.onreadystatechange = function(e) { if (this.readyState == 4 && this.status == 200) { //通过 responseText 来获取图片文件对应的二进制字符串 var binStr = this.responseText; var arrayBuffer = [] //然后自己再想方法将逐个字节还原为二进制数据 for (var i = 0, len = binStr.length; i < len; ++i) { // charCodeAt,返回指定位置的字符的Unnicode编码,范围为0-65535 var c = binStr.charCodeAt(i); // 0xff 的二进制表示为 0000000011111111,&操作的情况在都为1的时候才为1,所将byte限制在0-255 var byte = c & 0xff; arrayBuffer.push(byte) } // 假设获取的图片格式为jpg,则可以直接将获取的二进制数据转成base64 base64 = "data:image/jpg;base64," + _arrayBufferToBase64(arrayBuffer) } }; xhr.send();
设置完response数据类型,xhr还提供3个属性来获取请求返回的数据
xhr.response
xhr.responseText
xhr.responseXML
// XMLHttpRequest部分实现代码 interface XMLHttpRequestEventTarget : EventTarget { // event handlers attribute EventHandler onloadstart; attribute EventHandler onprogress; attribute EventHandler onabort; attribute EventHandler onerror; attribute EventHandler onload; attribute EventHandler ontimeout; attribute EventHandler onloadend; }; interface XMLHttpRequestUpload : XMLHttpRequestEventTarget { }; interface XMLHttpRequest : XMLHttpRequestEventTarget { // event handler attribute EventHandler onreadystatechange; readonly attribute XMLHttpRequestUpload upload; };
从代码中可以看到:
所以,xhr共有8个事件,而xhr.upload中有7个事件。
各个xhr事件触发条件说明:
事件触发顺序
当请求一切正常时:
发生absort/timeout/error异常时:
1、一旦发生异常,会立即中止当前请求 2、将readystate置为4,并触发onreadystatechange事件 3、如果还处于上传阶段,则依次触发xhr.upload.onprogress,xhr.upload.[onabort或ontimeout或onerror],xhr.upload.onloadend 4、然后触发下载阶段事件,xhr.onprogress,xhr.[onabort或ontimeout或onerror],xhr.onloadend
理清完这些事件之后,我们可以发现,当请求正常还是不正常,都会触发onreadystatechange事件,而且onreadystatechange的兼容Level 1 和 Level 2,因此将成功回调和失败回调都放在onreadystatechange中是个不错的选择。
xhr.onreadystatechange = function() { if (4 === xhr.readyState) { var statueCode = xhr.status; if (statusCode >= 200 && statusCode <= 300 || statusCode == 304) { // 成功回调 } else { // 失败回调 } } }
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 CORS需要浏览器和服务器同时支持,目前,几乎所有浏览器都支持该功能,IE浏览器不能低于IE10。 CORS使用目的与JSONP相同,但又不同,JSONP只支持GET请求(如script标签和image标签的使用),而CORS支持所有类型的HTTP请求(GET,HEAD,POST,PUT,DELETE),而且JSONP使用GET请求构造的链接长度有限,而CORS的POST请求可以传递更多数据。
简单请求
需要同时满足以下两个条件,凡是不满足的则属于非简单请求
基本流程:
这个过程有一点需要注意的,因为跨域请求中cookie并不会自动加载request header中,因为浏览器在发送跨域请求时,不能发送认证信息。如果想要打开这个限制,则客户端需要设置xhr.withCredentials =true,服务端也需要在response header中设置字段Access-Control-Allow-Credentials:true,但是如果跨域request中携带了认证信息,则服务端不能将Access-Control-Allow-Origin设置为*,而必须指定域名。
非简单请求
非简单请求一般是对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。当然,还有一种情况会出现“预检”请求,当绑定了xhr.upload事件并且跨域的时候,也一样会发起预检请求。 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求(preflight)。 浏览器(以OPTIONS方法发起请求)会先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP方法和请求头字段,只有在得到肯定答复之后,浏览器才会发出正式XMLHttpRequest请求,否则报错。
参考链接1:W3C的xhr 标准 参考链接2:segmentfault,ruoyiqing作者 参考链接3:阮老师的文章
前言
某天直接对一个接口发起跨域post请求时,虽然浏览器正常报跨域请求错误,但是却发现请求的接口居然有正常的返回值,并且是200,但是不会进去success回调而是进入error回调。这与我的认知出现的偏差,故有了这篇文章,多为记录的目的,以防遗忘。
XMLHttpResquest的历史
XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和Level 2。
XMLHttpResquest Level 1:
主要属性:
主要缺点:
XMLHttpResquest Level 2:
新功能:
XMLHttpRequest详解
Request Headers的设置和获取
setRequestHeader(header, value)
注意点:
header参数大小写不敏感
该方法的调用必须在open()方法之后,send()方法之前,否则报错
该方法设置同一个header多次不会采用覆盖的方式,而是采用追加的方式
注意点:
第一个方法获取全部headers,第二个方法根据传入header获取对应的header
两个方法都只能获取特定的header字段
XMLHttpRequest状态追踪
xhr.readyState
XMLHttpRequest超时时间
注意点:
XMLHttpRequest的异步和同步请求
注意点(当设置为同步请求时,需保证以下几点):
XMLHttpRequest发送指定类型数据
send(data) // data参数的数据类型会影响请求头部content-type的值
注意点:
XMLHttpRequest获取指定类型数据
注意,Level 1 不支持设置二进制数据类型,但是可以通过特殊的方式获得
设置完response数据类型,xhr还提供3个属性来获取请求返回的数据
xhr.response
xhr.responseText
xhr.responseXML
XMLHttpRequest相关事件
从代码中可以看到:
所以,xhr共有8个事件,而xhr.upload中有7个事件。
各个xhr事件触发条件说明:
事件触发顺序
当请求一切正常时:
发生absort/timeout/error异常时:
1、一旦发生异常,会立即中止当前请求 2、将readystate置为4,并触发onreadystatechange事件 3、如果还处于上传阶段,则依次触发xhr.upload.onprogress,xhr.upload.[onabort或ontimeout或onerror],xhr.upload.onloadend 4、然后触发下载阶段事件,xhr.onprogress,xhr.[onabort或ontimeout或onerror],xhr.onloadend
理清完这些事件之后,我们可以发现,当请求正常还是不正常,都会触发onreadystatechange事件,而且onreadystatechange的兼容Level 1 和 Level 2,因此将成功回调和失败回调都放在onreadystatechange中是个不错的选择。
XMLHttpRequest跨域请求
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 CORS需要浏览器和服务器同时支持,目前,几乎所有浏览器都支持该功能,IE浏览器不能低于IE10。 CORS使用目的与JSONP相同,但又不同,JSONP只支持GET请求(如script标签和image标签的使用),而CORS支持所有类型的HTTP请求(GET,HEAD,POST,PUT,DELETE),而且JSONP使用GET请求构造的链接长度有限,而CORS的POST请求可以传递更多数据。
简单请求
需要同时满足以下两个条件,凡是不满足的则属于非简单请求
基本流程:
这个过程有一点需要注意的,因为跨域请求中cookie并不会自动加载request header中,因为浏览器在发送跨域请求时,不能发送认证信息。如果想要打开这个限制,则客户端需要设置xhr.withCredentials =true,服务端也需要在response header中设置字段Access-Control-Allow-Credentials:true,但是如果跨域request中携带了认证信息,则服务端不能将Access-Control-Allow-Origin设置为*,而必须指定域名。
非简单请求
非简单请求一般是对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。当然,还有一种情况会出现“预检”请求,当绑定了xhr.upload事件并且跨域的时候,也一样会发起预检请求。 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求(preflight)。 浏览器(以OPTIONS方法发起请求)会先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP方法和请求头字段,只有在得到肯定答复之后,浏览器才会发出正式XMLHttpRequest请求,否则报错。
参考链接
参考链接1:W3C的xhr 标准 参考链接2:segmentfault,ruoyiqing作者 参考链接3:阮老师的文章