跨域post请求

来源:互联网 发布:绿里奇迹 知乎 编辑:程序博客网 时间:2024/05/30 23:52

跨域post请

今天被布置了一个任务,要ajax跨域做一个post请求,叫我去查查要怎么实现。

出于安全方面的考虑,ajax是不给跨域的(这里指的ajax其实是浏览器的对象XMLHttpRequest()),除非要有服务器端代理,意思是同域下的服务器提供服务帮我们做一个中转。可以看到,我们请求的其实仍然是同域的服务器,事实上并没有绕开跨域的问题,而是将它抛给服务器端解决,缺点也很明显,必须服务器提供一个服务,而且数据多走了一个来回,效率就变得比较低了,好处是不会有兼容性的问题。我想要一个纯前端的方案,所以server proxy排除。

这里觉得要提一下,看网上很多关于ajax的文章,其中ajax指的并不是浏览器的那个对象,而是指一种不用刷新页面就更新数据的手段,总会提jasonpm,iframe之类的,其实严格来说这些和ajax一点关系都没有,这是一个概念上的不同吧。

那我们抛开ajax来看,其实需求就是要达到不刷新页面的效果,不用ajax,跨域的解决方案其实挺多,jasonp算一个,但只能用get方法,post就无能为力了。get的数据是存放在url中的,如果数据量太大就不能支持了,而且从安全方面考虑也差一点,排除。关于jasonp之前写过一篇文章,有兴趣的戳传送门

flash也是可以post到任何域,原理是flash自己有一个跨域标准,在服务器要有一个文件定义允许跨域访问的域名。flash如果读到这个文件,并且允许当前域名访问,就可以进行跨域了。但是flash的方式需要安装插件,而且我不喜欢flash,所以也排除。

最后看来有两种解决方法,一种是CORS,一种是iframe+form,前者高大上,后者相对比较麻烦,但兼容性较好,下面分别来说一下。

CORS

CROS是Cross-Origin Resource Sharing的缩写,一看就知道是用来解决跨域问题的,其实这是通过在响应中加入允许跨域访问的头信息来实现的,标准比较新,所以并不是所有的浏览器都实现了。下面的图是从caniuse.com截的图。

caniuse_CORS

这样看来兼容性也不算太差,自己做小项目可以玩玩,移动端的除了Opera,主流的也都可以。

详细的标准可以上w3.org看看,英文苦手的话还是搜一下Cross-Origin Resource Sharing协议,看看博客一般也够了。我自己也不喜欢看英文,但慢慢觉得想要研究什么东西的话,还是得耐下性子看文档。

响应头有以下6个

1.Access-Control-Allow-Origin

取值可以是”origin-list-or-null | *”,但文档中也说,实现的时候和标准有区别,并不支持list的形式,后面测试的时候来试试。

2.Access-Control-Allow-Credentials

credentials是凭证的意思,是预检请求才会用到的一个属性,放在后面一起讲

3.Access-Control-Expose-Headers

这个头规定哪些头可以暴露给CROS API规范的API

4.Access-Control-Max-Age

规定那些预检请求验证通过的信息可以缓存多久

5.Access-Control-Allow-Methods

响应预检请求,指定在通过预检之后真正的跨域请求中,哪些method是可用的。

6.Access-Control-Allow-Headers

响应预检请求,指定在通过预检之后真正的跨域请求中,哪些头是可用的。

请求头有3个

1.Origin

该字段表明跨域请求的来源

2.Access-Control-Request-Method

预检请求的字段,指明在真正的跨域请求中,要用哪种方法

3.Access-Control-Request-Headers

预检请求的字段,指明在真正的跨域请求中,要用哪个字段

这里看到预检请求,真正的跨域请求等名词,都是什么意思呢。其实CORS跨域请求有两种,一种是简单的,一种是预检的。

简单的CORS跨域请求就是简单的跨域GET或者POST,然后外域服务器端给你返回一个带有Access-Control-Allow-Origin: “当前域” 头信息的响应,就表明他允许你的跨域请求了。

预检的CORS跨域请求就是说,要先发一个OPTION请求,告诉外域的服务器你要带什么头信息,用什么方法等等,问他肯不肯,这个请求就是所谓的预检请求。等外域的服务器允许了你的预检请求,后面才会发起真正的跨域请求。

我们分别来对着两种CORS跨域请求做下测试

一,简单的CORS跨域请求

两台机子,各开一个web服务器,我这里用的都是node。A机地址是192.168.1.104,B机器的地址是192.168.1.100.

A机器上服务的页面代码如下

<script type="text/javascript">
var cors_test = new XMLHttpRequest();
cors_test.open("GET", "http://192.168.1.100:8088");
cors_test.send();
<script>

可以预料,结果如下

ajax_cannot_load

ajax跨域请求被拒绝了,我们还看到,这里提示了我们”No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”那我们试试加上这个响应头吧。Access-Control-Allow-Origin: *

allow_all

哟西,ajax跨域请求成功了,修改Access-Control-Allow-Origin字段的值为”http://192.168.1.104:8088″

allow_ip

同样成功了,我们来尝试修改字段值为一个list,看看是否如文档所说是不可行的

allow_multiple_values

这里告诉我们,多个值是不被允许的,请求不成功。

二,预检的CORS跨域请求

我们加上一个请求头’Content-Type’,指定内容类型为’text/html’,意料中的报错。

content_type

需要服务器用Access-Control-Allow-Headers来说明允许携带Content-Type的头信息。我们在B机的服务器上添加允许携带该头信息的字段后,再试一次。

option

可以看到,预检请求的方法是OPTION,请求得到允许的响应之后才发了后面的GET请求,这才是真正的CORS跨域请求。所以,预检的方式就是先申请需要的选项,等待服务器应允之后再进行实际的数据交互,当业务有使用特定的方法,请求头等需求是,就需要使用这种方式。

同源原则本来就是出于安全的考虑而制定的,我们在解决跨域访问需求的统统是,也应该对安全性加以考虑,w3的文档中对安全问题做了描述,不在本文讨论范围内,但实际项目实施的时候是很有必要考虑的

iframe+form

用iframe来解决问题,看上去好像很老土,但这是对浏览器的兼容性问题的妥协。我们都知道,除了带src标签可以跨域(jasonp的原理),form的提交也是可以跨域的,而iframe则是用来解决不用刷新的需求。

仍是两台机子,A机子上页面的代码如下

<!DOCTYPE html><html>    <head>    </head>    <body>        <script type="text/javascript">        (function () {            /*add iframe to document*/            var iframe = document.createElement("iframe");            document.body.appendChild(iframe);            var iframe_name = "cross_domain_iframe";            iframe.style.display = "none";            iframe.contentWindow.name = iframe_name;            /*add frome to document*/            var form = document.createElement("form");            form.target = iframe_name;            form.action = "http://cp01-rdqa-dev149.cp01.baidu.com:8888/";            form.method = "POST";            /*add messages into input tag*/            var input = document.createElement("input");            input.type = "hidden";            input.name = "test_message";            input.value = "yoooooo";            form.appendChild(input);            document.body.appendChild(form);            form.submit();            iframe.onload = function () {                console.log(iframe.contentWindow.document.body.innerHTML);            }        })();        </script>    </body></html>

B机器是一个测试机,我们来看看结果(公司机器,打个码)
iframe_test1

可以看到post的请求成功了,服务器的响应也拿到了,但是响应内容log出来却出错了
iframe_test2

由于同源策略,iframe之间不能相互访问,我们并不能直接得到这个POST的响应的内容。

做到这里,只能大喊一声QQ图片20130628140742妈蛋,搞这么多还是拿不到数据,有个屁用啊。

其实还是有办法的,至少请求已经成功,而且数据也到了本地,我们可以通过浏览器窗口之间的postMessage来传递,仍然需要服务器端的配合,我们请求B站返回数据的时候,B站不能直接返回一个数据就完事儿了,而是要用postMessage把数据给丢出来

iframe_test3

A站页面的js修改如下

iframe.onload = function () {    window.frames[0].postMessage("testtest","http://xxxxxxxxxx:8888/");}window.addEventListener("message", function(event){    console.log(event.data);}, false);

然后终于,在控制台上看到了输出QQ图片20130628134732

iframe_test4

我们来看看postMessage的支持情况

caniusepostMessage

情况比CORS好一些,但要兼容IE6和7之类的,估计就只能用flash了。

参考资料
http://www.w3.org/TR/cors/

http://www.cnblogs.com/shanyou/archive/2012/09/16/2687907.html

http://www.iteye.com/topic/600682
http://blog.sina.com.cn/s/blog_6940cab30101a1yj.html
http://stackoverflow.com/questions/298745/how-do-i-send-a-cross-domain-post-request-via-javascript#answer-6169703

0 0
原创粉丝点击