ajax跨域的几种解决方法和坑

今天在项目中要用到ajax提交数据到其它公司的接口,但ajax不允许跨域,折腾了一下午,把一些解决方法和踩到的坑记录一下。

 

一、浏览器

在chrome的快捷方式最后加上 --disable-web-security,注意前面要有个空格,且要加在引号后面。

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security

这样打开浏览器就会提示,但不影响使用。

您使用的是不受支持的命令行标记: --disable-web-security。稳定性和安全性会有所下降

 只适用于chrome(其它浏览器没试过,不知有没有这样的参数),每台电脑的浏览器都要这样设置,不方便。

 

二、提交到后台

用ajax提交到后台(如C#的ashx),后台用WebClient之类的访问Url,返回数据给ajax的回调

多绕了一下,如果后台有封装好访问接口的公共方法的话,倒也不算麻烦。

 

三、jsonp

jsonp也是异步请求,也有回调,所以jquery把jsonp封装到了ajax中。但其实jsonp和ajax是两码事,ajax是XMLHttpRequest,而jsonp是html的标签。在chrome开发者工具中可以看到,ajax是xhr请求,而jsonp是算到script的js请求

jsonp是利用标签跨域的特点,生成一个新标签插入头部,来提交和返回数据。就像

<script src="http://www.abc.com?param=1&callback=CallbackFuc"></script>

标准使用方法,由于jsonp本质不是ajax而是<script>标签,所以只支持get。如果在参数里加上type:"POST",jquery会自动判断,同源的情况就用普通ajax进行post,不同源的情况自动转成get

$.ajax({
    url : "http://www.abc.com?param=1",
    dataType : "jsonp",    //表示使用jsonp
    jsonp : "callback",    //传给服务器的值,为jsonpCallback服务的,两个值以&callback=jsonpCallback进行传递。不填的话默认就是callback,不想以此参数传递的话,可以填jsonp:false,这样就会生成jquery_xxxx之类的随机参数
    jsonpCallback : "jsonpCallback",    //和jsonp配合使用,服务器返回的结果是js字符串并直接执行:jsonpCallback(返回数据),jsonpCallback就是js的方法,数据就是该方法的参数
    success : function(ret){
        console.log("ok");
    },
    error : function(ret){
        console.log("err");
    }
});

因为最后返回的是js代码,如jsonpCallback({"key":"value"}),所以不要success和error,直接在外面定义一个方法function jsonpCallback(){}也可以。(success是jquery帮我们封装好的,便于使用)

 

1、服务器端坑

一开始以为直接datatyepe:"jsonp"就可以了,没想到一直报错,无法解析。

本来返回的数据是json,如{"key":"value"},由于jsonp是返回直接执行的代码,所以要变成拼成jsonpCallback({"key":"value"})这样的字符串。

好在合作公司服务端接口的同事就在旁边,改一下就可以了,碰到其它不熟的公司,人家一般不会改。

 

2、循环异步jquery坑

项目是循环异步调用,一下子发若干个请求,发现回来时,有些成功有些失败,数目不定。。。

查了半天,总算发现一篇文章了,原来是jquery的bug。。。为避免广告,就不放原网址了,感谢原作者(原网站也没写作者,不知是不是转来的)

jquery已经将所有的调用封装成对象,调用起来很方便 js跨域写起来比较麻烦,尤其是传递参数的时候,需要手动拼接data ?&p1=1&p2=2 但是jquery的跨域有个bug,当同时发出大量请求瞬间返回结果时,会导致接收请求的时候丢失部分回调请求(实际上已经发送成功了) 例: 
这种情况很常见,就是循环发送ajax请求么。 非跨域请求这么写没有问题,success会被调用100次,但是当dataType : "jsonp"时,success可能只会执行70次80次,剩下的请求没有成功也没有失败,用complete也抓不到。 浏览器会报错找不到callback,或无任何提示。有两个地方会引起这种问题
        // Clean-up function
        jqXHR.always(function() {
            // Set callback back to previous value
            window[ jsonpCallback ] = previous;
            // Call if it was a function and we have a response
            if ( responseContainer && jQuery.isFunction( previous ) ) {
                window[ jsonpCallback ]( responseContainer[ 0 ] );
            }
        });
1.callback被清除,所以有时候会报出找不到callback这个方法 2.xmlHttp.readyState==4  同时发送请求的时候,浏览器的状态只有这么一个readyState当浏览器知道已经获取返回结果了,就不接收请求了 这个肯定是jquery的bug,使用js跨域没有问题

解决方法1:加上async:false,把异步改成同步就可以了,但是会卡浏览器,循环大、返回慢的时候很不好用。

经实践,加上异步也没用。。。

 

解决方法2:改成原生js请求jsonp

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://www.abc.com?param=1&callback=jsonpCallback";  //src可以动态生成
var head = document.getElementsByTagName("head")[0];
head.insertBefore(script, head.firstChild);

定义好function jsonpCallback(){},回调时就会执行(因为返回的是jsonpCallback(数据)这种形式,直接执行js代码)

 

3、jquery自动转换的坑

被这个坑害惨了,可以说一晚上都被它误导。

前面说过,jquery把jsonp封装到ajax中,其实两者完全不同。我调试时,本身又作服务端又作客户端,实际上它是把jsonp当作ajax进行发送(chrome开发者工具中可以看到,是走xhr)

根据同源策略,同一域不同端口也算跨域,好在机上IIS布了两个端口用于两个项目的调试,把服务端布到另一个项目上,这样调试时jquery就会自动识别使用jsonp(开发者工具看到走的是js)

 

四、改进jquery的jsonp

虽然原生的是可以解决问题,但平时jquery用习惯了,尝试着改进一下。由于主要是因为瞬间大量并发请求造成jquery的Bug,那么就在这里面做文章

1、延时执行

前端

var interVal=0;
for(var i=0;i<1000;i++){
    interVal += 1000;  //这个值根据数据返回速度进行设置,快的话200也可以
    setTimeout(function(){
        interVal += 1000;
        $.ajax({
            //省略
        });
    },interVal)
}

后端

Thread.Sleep(500);
Response.Write("jsonpCallback({\"key\":\"value\"})");

用Thread.Sleep来模拟网络阻塞

如果网络状况好,返回数据快,interVal可以设成每次递增200。这个值要大于数据请求所用的时间,不然还是会并发导致jquery的Bug

这个方法可以凑合着用,特别是对一些后台的自动任务,间隔设大一些无所谓,让它慢慢跑。偶尔出一些错也不要紧,下次再循环时还会去请求。

 

2、响应式

使用$.Deferred()配合$.when()、$.then()、$.done()、$.fail(),一个完成后再调用下一个。

比较高级的用法,之前有在项目中试用过,好像还是会一口气把循环里的ajax请求全发出去,而不是一个个排队发。这里就不尝试了

 

3、递归

循环ajax时非常好用的手段,也是踩了无数坑研究出来的,成就感非常强。

先执行第一个ajax,在success和error里,都要执行递归,然后把计数器++,根据计数器==循环总数进行判断是否完成循环。

递归的好处就是界面上可以友好的显示正在执行第几个,每个的结果是什么,而且是按顺序执行的(不像发一堆ajax出去,回来时零零散散的),还可以随时中断。

缺点就是多重ajax时,每一个都要判断error并递归,如果漏了,在那个环节出错的话,就不会继续下去。

这里也懒的再尝试了

 

posted @ 2015-12-16 18:07  文刂亻右景彡  阅读(652)  评论(0)    收藏  举报