对于web开发来说,因为浏览器的同源策略,咱们须要常用一些hack的方法去跨域获取资源,可是hack的方法总归是hack。直到W3C出了一个标准-CORS-"跨域资源共享"(Cross-origin resource sharing)。
它容许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
首先来讲 CORS 须要浏览器和服务端同时支持的,对于兼容性来讲主要是ie10+,其它现代浏览器都是支持的。
http://caniuse.com/#feat=cors
使用 CORS 跨域的时候其实和普通的 ajax 过程是同样的,只是浏览器在发现这是一个跨域请求的时候会自动帮咱们处理一些事,好比验证等等,因此说只要服务端提供支持,前端是不须要作额外的事情的。javascript
CORS 的请求分两种,这也是浏览器为了安全作的一些处理,不一样状况下浏览器执行的操做也是不同的,主要分为两种请求,固然这一切咱们是不须要作额外处理的,浏览器会自动处理的。css
只要同时知足如下两大条件,就属于简单请求。html
1) 请求方法是如下三种方法中的一个: HEAD GET POST 2)HTTP的头信息不超出如下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
对于简单的跨域请求,浏览器会自动在请求的头信息加上 Origin
字段,表示本次请求来自哪一个源(协议 + 域名 + 端口),服务端会获取到这个值,而后判断是否赞成此次请求并返回。前端
// 请求 GET /cors HTTP/1.1 Origin: http://api.qiutc.me Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
若是服务端许可本次请求,就会在返回的头信息多出几个字段:html5
// 返回 Access-Control-Allow-Origin: http://api.qiutc.me Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Info Content-Type: text/html; charset=utf-8
这三个带有 Access-Control
开头的字段分别表示:java
Access-Control-Allow-Origin
必须。它的值是请求时Origin字段的值或者 *
,表示接受任意域名的请求。git
Access-Control-Allow-Credentials;
可选。它的值是一个布尔值,表示是否容许发送Cookie。默认状况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie能够包含在请求中,一块儿发给服务器。github
再须要发送cookie的时候还须要注意要在AJAX请求中打开withCredentials属性:var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
须要注意的是,若是要发送Cookie,Access-Control-Allow-Origin就不能设为*
,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其余域名的Cookie并不会上传,且原网页代码中的document.cookie
也没法读取服务器域名下的Cookie。web
Access-Control-Expose-Headers
可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()
方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。若是想拿到其余字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('Info')
能够返回Info字段的值。ajax
固然咱们为了防止接口被乱调用,须要限制源,对于不容许的源,服务端仍是会返回一个正常的HTTP回应,可是不会带上 Access-Control-Allow-Origin
字段,浏览器发现这个跨域请求的返回头信息没有该字段,就会抛出一个错误,会被 XMLHttpRequest
的 onerror
回调捕获到。
这种错误没法经过 HTTP 状态码判断,由于回应的状态码有多是200
出了简单请求之外的CORS请求。
非简单请求是那种对服务器有特殊要求的请求,好比请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通讯以前,增长一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可使用哪些HTTP动词和头信息字段。只有获得确定答复,浏览器才会发出正式的XMLHttpRequest请求,不然就报错。
预检请求的发送请求:
OPTIONS /cors HTTP/1.1 Origin: http://api.qiutc.me Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.qiutc.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪一个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
预检请求的返回:
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://api.qiutc.me Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain
Access-Control-Allow-Methods
必需,它的值是逗号分隔的一个字符串,代表服务器支持的全部跨域请求的方法。注意,返回的是全部支持的方法,而不单是浏览器请求的那个方法。这是为了不屡次"预检"请求。
Access-Control-Allow-Headers
若是浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,代表服务器支持的全部头信息字段,不限于浏览器在"预检"中请求的字段。
Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即容许缓存该条回应1728000秒(即20天),在此期间,不用发出另外一条预检请求。
一旦服务器经过了"预检"请求,之后每次浏览器正常的CORS请求,就都跟简单请求同样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
参考:[《跨域资源共享 CORS 详解》]()http://www.ruanyifeng.com/blog/2016/04/cors.html
)
阮大神的文章,复制粘贴了很多。
jsonp = json + padding
其实对于经常使用性来讲,jsonp应该是使用最常常的一种跨域方式了,他不受浏览器兼容性的限制。可是他也有他的局限性,只能发送 GET 请求,须要服务端和前端规定好,写法丑陋。
它的原理在于浏览器请求 script 资源不受同源策略限制,而且请求到 script 资源后当即执行。
主要作法是这样的:
在浏览器端:
首先全局注册一个callback回调函数,记住这个函数名字(好比:resolveJson),这个函数接受一个参数,参数是指望的到的服务端返回数据,函数的具体内容是处理这个数据。
而后动态生成一个 script 标签,src 为:请求资源的地址+获取函数的字段名+回调函数名称,这里的获取函数的字段名是要和服务端约定好的,是为了让服务端拿到回调函数名称。(如:www.qiute.com?callbackName=resolveJson
)。
function resolveJosn(result) { console.log(result.name); } var jsonpScript= document.createElement("script"); jsonpScript.type = "text/javascript"; jsonpScript.src = "http://www.qiute.com?callbackName=resolveJson"; document.getElementsByTagName("head")[0].appendChild(jsonpScript);
服务端
在接受到浏览器端 script 的请求以后,从url的query的callbackName获取到回调函数的名字,例子中是resolveJson
。
而后动态生成一段javascript片断去给这个函数传入参数执行这个函数。好比:
resolveJson({name: 'qiutc'});
执行
服务端返回这个 script 以后,浏览器端获取到 script 资源,而后会当即执行这个 javascript,也就是上面那个片断。这样就能根据以前写好的回调函数处理这些数据了。
在一些第三方库每每都会封装jsonp的操做,好比 jQuery 的$.getJSON
。
一个页面框架(iframe/frame)之间(父子或同辈),是可以获取到彼此的window对象的,可是这个 window 不能拿到方法和属性(尼玛这有什么用,甩脸)。
// 当前页面域名 http://blog.qiutc.me/a.html <script> function onLoad() { var iframe =document.getElementById('iframe'); var iframeWindow = iframe.contentWindow; // 这里能够获取 iframe 里面 window 对象,可是几乎没用 var doc = iframeWindow.document; // 获取不到 } </script> <iframe src="http://www.qiutc.me/b.html" onload="onLoad()"</iframe>
这个时候,document.domain
就能够派上用场了,咱们只要把 http://blog.qiutc.me/a.html
和 http://www.qiutc.me/b.html
这两个页面的 document.domain
都设成相同的域名就能够了。
前提条件:这两个域名必须属于同一个基础域名!并且所用的协议,端口都要一致。
但要注意的是,document.domain
的设置是有限制的,咱们只能把 document.domain
设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com
中某个文档的 document.domain
能够设成a.b.example.com
、b.example.com
、example.com
中的任意一个,可是不能够设成 c.a.b.example.com
,由于这是当前域的子域,也不能够设成baidu.com
,由于主域已经不相同了。
这样咱们就能够经过js访问到iframe中的各类属性和对象了。
// 主页面:http://blog.qiutc.me/a.html <script> document.domain = 'qiutc.me'; function onLoad() { var iframe =document.getElementById('iframe'); var iframeWindow = iframe.contentWindow; // 这里能够获取 iframe 里面 window 对象而且能获得方法和属性 var doc = iframeWindow.document; // 获取到 } </script> <iframe src="http://www.qiutc.me/b.html" onload="onLoad()"</iframe>
// iframe 里面的页面 <script> document.domain = 'qiutc.me'; </script>
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的全部的页面都是共享一个 window.name
的,每一个页面对 window.name
都有读写的权限,window.name
是持久存在一个窗口载入过的全部页面中的,并不会因新页面的载入而进行重置。
好比有一个www.qiutc.me/a.html
页面,须要经过a.html页面里的js来获取另外一个位于不一样域上的页面www.qiutc.com/data.html
里的数据。
data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要获得的数据值。data.html里的代码:
<script> window.name = '我是被指望获得的数据'; </script>
那么在 a.html
页面中,咱们怎么把 data.html
页面载入进来呢?显然咱们不能直接在 a.html
页面中经过改变 window.location
来载入data.html页面(这简直扯蛋)由于咱们想要即便 a.html
页面不跳转也能获得 data.html
里的数据。
答案就是在 a.html
页面中使用一个隐藏的 iframe
来充当一个中间人角色,由 iframe
去获取 data.html
的数据,而后 a.html
再去获得 iframe
获取到的数据。
充当中间人的 iframe
想要获取到data.html
的经过window.name
设置的数据,只须要把这个iframe
的src设为www.qiutc.com/data.html
就好了。而后a.html
想要获得iframe
所获取到的数据,也就是想要获得iframe
的window.name
的值,还必须把这个iframe
的src
设成跟a.html
页面同一个域才行,否则根据前面讲的同源策略,a.html
是不能访问到iframe
里的window.name
属性的。这就是整个跨域过程。
// a.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script> function getData() { var iframe =document.getElementById('iframe'); iframe.onload = function() { var data = iframe.contentWindow.name; // 获得 } iframe.src = 'b.html'; // 这里b和a同源 } </script> </head> <body> <iframe src="http://www.qiutc.com/data.html" style="display:none" onload="getData()"</iframe> </body> </html>
window.postMessage(message, targetOrigin)
方法是html5新引进的特性,可使用它来向其它的window对象发送消息,不管这个window对象是属于同源或不一样源。兼容性:
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,若是不想限定域,可使用通配符 * 。
须要接收消息的window对象,但是经过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
上面所说的向其余window对象发送消息,其实就是指一个页面有几个框架的那种状况,由于每个框架都有一个window对象。在讨论第种方法的时候,咱们说过,不一样域的框架间是能够获取到对方的window对象的,虽然没什么用,可是有一个方法是可用的-window.postMessage
。下面看一个简单的示例,有两个页面:
// 主页面 blog.qiutc.com <script> function onLoad() { var iframe =document.getElementById('iframe'); var iframeWindow = iframe.contentWindow; iframeWindow.postMessage("I'm message from main page."); } </script> <iframe src="http://www.qiutc.me/b.html" onload="onLoad()"</iframe>
// b 页面 <script> window.onmessage = function(e) { e = e || event; console.log(e.data); } </script>
一种用 CSS 跨域传输文本的方案。
优势:相比 JSONP 更为安全,不须要执行跨站脚本。
缺点:没有 JSONP 适配广,CSST 依赖支持 CSS3 的浏览器。
原理:经过读取 CSS3 content 属性获取传送内容。
具体能够参考:CSST (CSS Text Transformation)
flash 嘛,这个东西注定灭亡,不想说了。。。