Ajax跨域
作者:
秒速五厘米
在web开发中,前端向后端发送请求,基本上都是用ajax的方式。如果我们前端页面的url和我们要提交的后端url存在跨域问题时,我们该如何解决呢? 下面将分别讨论几种解决方案。
CORS是一套解决前后端跨域通信的解决方案,简单说是一种前后端用于允许跨域通信的一种约定机制。下图1 简单明了简述了CORS的概念。
只要浏览器和后端做好相关的对接和支持工作,CORS就能跑通。
目前除IE10以下的IE浏览器,其余主流浏览器均支持CORS。
服务器端,只需要设置特定的头就可以允许跨域通信:
//允许milo.qq.com的请求跨域 header("Access-Control-Allow-Origin:milo.qq.com"); //设置通配符,允许所有请求跨域 header("Access-Control-Allow-Origin:*");
有一点需要注意:
不建议后端配置Access-Control-Allow-Origin头为通配符*,因为为了安全起见,这种设置不可控。
建议后端以白名单的形式加header头,对于白名单内的请求,设置对应的跨域头,否则拒绝跨域。 可以参考:
//配置允许跨域请求的白名单origin $allowed_origin= array( 'http://a.qq.com', 'http://b.qq.com', 'http://www.qq.com' ); //获取本次请求的origin $origin=isset($_SERVER['HTTP_ORIGIN'])?$_SERVER['HTTP_ORIGIN']:''; //判断是否白名单 if(in_array($origin,$allowed_origin)){ //设置允许跨域头 header("Access-Control-Allow-Origin:'.$origin); }
上面说到了CORS可以跨域,但是我们发现简单得设置了Access-Control-Allow-Origin头并不能把cookie带过去,cookie作为前
后端通信中常用的数据载体经常用于校验凭证等数据传输,非常重要。 比如在a.qq.com的网站上,请求了一个c.qq.com/xxx.php
的接口,但是此接口需要从c.qq.com的域名下拿cookie中的登录态作为身份校验,这时发现cookie取不到。 下面简单介绍一下通过
CORS实现跨域发送cookie。设置withCredentials相关头跨域发送cookie只需要前端带上withCredentials相关头,并且后端加上
Access-Control-Allow-Credentials:true即可。 具体操作如下:[前端代码]
//当前位于a.qq.com中,向c.qq.com/xx.php接口发送请求 $.ajax({ type:'GET', url:'http://c.qq.com/xx.php', xhrFields:{ withCredentials:true }, success:function(res){ console.log(res); }, fail:function(){ } })
[后端代码]
//当前为c.qq.com/xx.php //设置为制定的origin,不能设置为* header('Access-Control-Allow-Origin:http://a.qq.com'); //允许携带cookie header('Access-Control-Allow-Credentials:true');
[需要注意:]跨域发送cookie,后端设置Access-Control-Allow-Origin头不能设置为通配符,否则浏览器将会拒绝跨域并报错。
jsonp本质上是script请求,是前端页面中用于外链script的一种请求方式。由于script标签有天然的跨域特性(拥有此特性的还用img标签等),
而且其返回的内容为文本,且可以直接执行的特点。故通过将请求返回的内容封装成js脚本的形式,在前端直接执行的方式可以得到后端返回内容。
使用jsonp跨域请求后端可以这么做:[前端代码]
//以jquery调用为例 $.ajax({ url:'http://c.qq.com/xx.php', dataType:'jsonp',//表示返回格式为jsonp type:'GET', success:function(res){ console.log(res); }, fail:function(){ } })
前端调用默认会发出一个类似:http://c.qq.com/xx.php?callback=xxxxxx 的请求到后端,后端拿到callback参数值,后会将其作为回调方法,
直接返回一段用callback调用responseData的方法即可。
[后端代码]
$callbackName=$_GET['callback'] || 'callback'; $retData=array( "a" =>1, "b" =>2 ); echo $callbackName.'('.json_encode($retData).')';
[优点与缺点]使用jsonp方式跨域的优点很明显,就是兼容性强,所有浏览器均支持。而且后端改造的成本也低。 缺点就是jsonp本质上是script请求,
只能支持GET请求,对于大数据量和传输文件等都不支持,而且也无法拿到相关的返回头,状态码等数据。