本文就是对ajax
方面的知识作一个总结,没有什么深刻的地方。虽然总结的文章有不少,可是看本身写的和看别人的文章感受终究仍是相去甚远的。因此若是读者以为内容重复请直接右上角。javascript
用法:html
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >= 200 & xhr.status < 300 || xhr.status === 304) {
}
}
};
xhr.open('GET', 'http://localhost:8080', true);
// 若是是POST请求,则send的参数是具体的数据
xhr.send(null);
复制代码
readystatechange
:每当xhr.readyState
改变时触发。timeout
:当请求发出超过xhr.timeout
设置的时间后,依然没有收到响应,则会触发。若是在超时终止请求以后再调用访问status
等属性就会致使错误,因此最好在onreadystatechange
事件中使用try-catch
。loadstart
:收到响应1byte
后触发。progress
:其event.tartget === xhr
,event.lengthComputable
表示进度信息是否可用,event.total:Content-Lengt
的预期字节数。event.loaded:
已接收的字节数。(须要服务器返回Content-Length
头部,不然lengthComputable
一直为false
)。error
:请求出错触发。abort
:xhr.abort();
终止链接时触发。load
:接受到完整数据时触发,至关于readyState === 4时loadend
:通讯完成,不论是error、abort
或者load
,都会致使此事件的触发(没有浏览器实现)。ps: 为确保兼容性、正常执行,
onreadystatechange、progress
最好在open
以前绑定。前端
responseText
:做为响应主体被返回的文本responseXML
:若是返回类型是"text/xml" || "application/xml"
则这个属性保存这XML DOM
文档。不然为null
。status
:http
状态码statusText
:状态码的说明readyState
:取值以下:
open()
;open()
;send()
;readyState
变化的时候都会触发onreadystatechange
事件,并且这个事件最好在open
以前就绑定(为了兼容性)。timeout
:超时时间(ms)。abort
:用于取消异步请求。setRequestHeader(key, value)
:open()
后send()
前调用。getResponseHeader/getAllResponseHeaders
:看名字,不解释了。overrideMinmeType
:重写xhr
响应的MIME
(最好在send
以前调用,这样能够确保绝对有效)。并非全部的事件都是异步的, xhr.onreadystatechange 和xhr.onloadstart就是同步事件。java
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => console.log('ready state change');
xhr.onloadstart = () => console.log('load start');
xhr.open(method, url);
xhr.send();
console.log('sync');
// 因此结果为 ready state change => load start => sync
复制代码
用法:web
/** * 此处的request、response见下文 */
fetch(request)
.then(response => {
response.json()
.then(data => console.log(data));
})
.catch(err => console.log(err));
复制代码
能够经过new Request();
建立request
对象(固然也能够直接写)ajax
let request = new Request('http://localhost:8080', {
// headers见下文
headers,
method: 'GET',
mode: 'cors'
});
复制代码
request
上的方法:json
method
: 支持GET
,POST
,PUT
,DELETE
,HEAD
url
:请求的 URL
headers
: 对应的Headers
对象referrer
: 请求的 referrer
信息mode
: 能够设置cors
,no-cors
,same-origin
credentials
: 设置 cookies
是否随请求一块儿发送。能够设置:omit
,same-origin
redirect
:follow
,error
,manual
integrity
: subresource
完整性值(integrity value
)cache
: 设置 cache
模式 (default
,reload
,no-cache
)能够经过new Header();
来建立请求头:跨域
let headers = new Headers({'Content-Type': 'text/plain'});
headers.append('accept', 'text/*');
复制代码
定义在Headers之上的一些方法以下:promise
fetch().then(response);
中的response
就是一个Response
对象 能够经过new Request();
建立request
对象浏览器
clone()
: 建立一个新的 Response
克隆对象.error()
: 返回一个新的,与网络错误相关的 Response
对象.redirect()
: 重定向,使用新的 URL
建立新的 response
对象..arrayBuffer()
: Returns a promise that resolves with an ArrayBuffer.blob()
: 返回一个 promise
, resolves
是一个 Blob
.formData()
: 返回一个 promise
, resolves
是一个 FormData
对象.json()
: 返回一个 promise
, resolves
是一个 JSON
对象.text()
: 返回一个 promise
, resolves
是一个 USVString (text)
.同源就是拥有相同的协议(protocol) && 主机(hostname) && 端口(port)
,那么这两个页面称为同源。一切非同源的请求均为跨域。并跨域没法随意请求,只是说为了网站的安全性,浏览器才采起同源策略。
若是是协议和端口形成的跨域问题,前端是无能为力的。 跨域问题中的域,浏览器只是用url
首部来区分的,并不会对DNS
以后获得的IP
进行判断。
ps:url首部 = protocol + host;
严格的说,浏览器并非拒绝全部的跨域请求,实际上拒绝的是跨域的读操做。浏览器的同源限制策略是这样执行的:
同源策略呢,限制了如下行为:
Cookie、LocalStorage、IndexDB
js
的交互操做ajax
请求发不出去(其实能够发出去,只不过浏览器将响应给拦截了)跨域方式:JSONP、CORS、postMessage等
。
JSONP,JSON with Padding 参数式JSON
。 JSONP
的原理其实就是利用了<script>
标签的src
引入外部脚本时不受同源策略的限制,经过手动添加DOM
并赋予src
请求的url
,在请求的url
中填写接收数据的回调,再加上服务器对callback
的支持便可。
Cross-Origin Resource Sharing, CORS 跨域资源共享
。CORS
是一种web
浏览器的技术规范,它为web
服务器定义了一种容许从不一样域访问其资源的方式。而这种跨域访问是被同源策略所禁止的。CORS
系统定义了一种浏览器和服务器交互的方式来肯定是否容许跨域请求, 有更大的灵活性,比起简单地容许这些操做来讲更加安全。 CORS
须要浏览器和服务器共同配合、支持。整个CORS
通讯过程都是由浏览器来完成的,除了一些限制之外,代码和普通的ajax
没有什么不一样,实现CORS
的关键是服务器,只要服务器支持、实现了CORS
接口就能实现CORS
。
知足如下两大条件的请求就是simple request
:
对于简单请求(如下都是跨域状况)浏览器直接发出CORS, 增长一个Origin头部。
这个Origin
字段做用是告诉服务器,本次跨域请求是源自哪一个主机、端口、协议,服务器以此来判断是否容许这次跨域。
若是Origin
不在服务器的许可范围内,那服务器就返回正常的HTTP
响应。浏览器发现响应的Access-Control-Allow-Origin
和发起请求的源不相等,或者根本没有这个字段,则浏览器拒绝这次请求。会被xhr
的onerror
事件捕获,这种错误没法经过状态码识别。
不然服务器返回的响应会多出(所谓多出,其实就是浏览器设置了这些头部)等头部信息。
能够看到多出了'Access-Control-Allow-Credentials'、'Access-Control-Allow-Headers'
等头部,这些头部具体意义见下文。
服务器必须设置的值,不然不能实现CORS,它的值要么是精确的请求的Origin,要么是通配符*(在须要Cookie的时候不支持*)。
可选。意为是否容许发送cookie
,默认为不容许,不过这个字段只能设置为true
。若是浏览器不容许发送cookie
,删除该字段便可。 注意:浏览器在请求的时候也必须设置:xhr.withCredentials = true
;不过有的浏览器省略还会自动带上cookie
,能够手动关闭。
可选。在CORS
中,用于设置浏览器能够发送的头部。
res.setHeader('Access-Control-Allow-Headers', 'Your-Fucking-Header');
复制代码
可选。CORS
返回请求的时候,xhr.getAllResponseHeaders();
只能拿到6个基本头部字段:'Cache-Control'、'Content-Language'、'Content-Type'、'Expires'、'Last-Modified'、'Pragma'
。经过res.setHeader('Access-Control-Expose-Headers', 'some headers');
能够得到容许的header
。
非简单请求指的是那种对服务器有特殊要求的请求,如:request method
是put、delete
,或者Content-Type
为application/json
等。 非简单请求的CORS
请求会在正式通讯以前进行一次HTTP查询请求,称之为 预检请求(preflight
)。浏览器先询问服务器,当发送方的域名在服务器容许之列,而且发送方使用的头部、请求方法都是服务器容许的时候才会发送正式的Ajax
请求,不然报错。
非简单请求除了Origin
之外,还会发送两个特殊的头部:'Access-Control-Request-Method','Access-Control-Request-Headers'
。
浏览器这次CORS会用到的HTTP方法。
指出浏览器会发送的额外的头部
'Access-Control-Allow-Origin'
和
'Access-Control-Allow-Headers'
来判断服务器是否容许
CORS
,除此以外还有如下头部:
必需。值由','分割的String
,意为支持的CORS
请求方法,返回的是全部支持的方法,不是浏览器设置的那个方法,避免屡次preflight
。
可选。单位: s(秒)。意为本次preflight
的有效时间。在有效时间内不用再次发送预检请求,即容许缓存该回应。
Headers | Server | Browser |
---|---|---|
Access-Control-Allow-Orgin | √ | × |
Access-Control-Allow-Headers | √ | × |
Access-Control-Allow-Methods | √ | × |
Access-Control-Max-Age | √ | × |
Access-Control-Allow-Credentials | √ | √ |
Access-Control-Expose-Headers | √ | × |
Access-Control-Request-Method | × | √ |
Access-Control-Request-Headers | × | √ |
-- | 目的 | 支持方法 | 优点 | 不足 |
---|---|---|---|---|
CORS | 跨域 | 全部HTTP请求方法 | 请求方法不只仅局限于GET,支持全部HTTP请求方法。安全性高。 | 老版本浏览器不支持CORS,有必定兼容性问题,好比IE10及更早版本、Safari4及更早版本、FireFox3.5及更早版本都不支持。 |
JSONP | 跨域 | GET | 能够向老式、不支持CORS的网站请求数据。并且设置简单,无需设置过多的响应、请求头部。 | ①只能支持GET方法 ②对于存在恶意行为的服务器存在必定的安全隐患。 ③须要一个接收数据的全局函数,污染了全局做用域。 ④判断请求是否失败不容易(H5给script新增error事件,可是等浏览器实现还需以时日)。 |
若是两个网页的主域名相同,这个时候能够令document.domain
都为其主域名(document.domain
只能将其设置为自身和更高一级的父域名)。 因为同源限制的第二条,不一样域的iframe
之间不能进行js
交互。因此经过iframe.contentWindow
获取到的window
对象,它的方法和属性几乎都是不可用的,而且不容许获取此window.document
。
这个时候:
document.domain = /* 两个页面共同的父级域名 */;
复制代码
而后就能够获得iframe.contentWindow
的属性了。也能够经过iframe
里面的方法请求数据,以此也能够达到跨域的目的。
它的原理是父窗口能够对iframe
的URL
进行读写,而和祖先窗口(不只仅是父窗口)同源iframe
也能够读写父窗口的URL
,而hash
部分不会发送到服务器(不会产生http
请求),因此能够经过修改hash
来实现双向的通讯。 具体操做是:
super
窗口中有一个跨域的iframe0
,
iframe0
中又有一个和super
同源的iframe1
。
如图所示,颜色表示是否同源。
iframe0
想要发送数据的时候,能够直接修改iframe1
的hash
(跨域也能够)iframe1
监听onhashchange
事件,拿到hash
部分后,再修改super
的hash
(由于iframe1
和super
同源,因此能够)super
也监听onhashchange
事件,就能够拿到数据了。 代码以下:super:
<iframe id = "iframe" src="http://localhost:8080/iframe0.html"></iframe>
<script type="text/javascript"> let counter = 0; let url = "http://localhost:8080/iframe0.html#"; const iframe = document.getElementById('iframe'); window.onhashchange = function(event) { console.log('_我获得数据:', event.newURL.split('#')[1]); } </script>
iframe0:
<iframe src="http://localhost/iframe1.html" frameborder="0"></iframe>
<script> let counter = 0; let url = 'http://localhost/iframe1.html#'; const iframe = document.querySelector('iframe'); setInterval(() => { console.log('我发送数据:', + counter); iframe.src = url + counter ++; }, 2000); </script>
iframe1:
<script> window.onhashchange = function() { let data = event.newURL.split('#')[1]; // 修改super的hash window.parent.parent.location.hash = data; } </script>
复制代码
结果:
要使用postMessage
这个API
必需要有其余窗口的引用otherWindow
发送方:
otherWindow.postMessage(data, targetOrigin, [transfer]);
复制代码
参数说明:
data
:发送的数据targetOrigin
:指定哪些窗口接收消息,*表示任何窗口, '/'表示当前域下的窗口。transfer
:可选,和message
同时传递的对象,这些对象的全部权被转移给消息的接收方,而发送方再也不拥有全部权。接收方:
window.addEventListener('message', e => {
console.log(e);
}, false);
复制代码
在e
中有4个属性比较重要:
data
:发送来的消息对象type
:发送消息的类型source
:发送消息的window
origin
:发送消息的origin
直接经过给e.source
添加引用类型的属性,能够直接给发送端的window
添加数据。其实比较经常使用的跨域方法就是CORS、JSONP,其余的有个大概了解知道就行了。其余的关于XSS、CSRF等内容回头待续。