Chrome Ajax 302重定向问题

ECSHOP的后台每次删除一个商品都要报下面的错误

Uncaught transport.js/parseResult() error: can't parse to JSON.

错误的原因是ajax的返回值是空字符串, 而ajax中规定的格式是JSON, 返回之后要对字符串进行parseJSON, 空字符串被认为不是合法的JSON字符串. 看看代码就知道了:

            try {
                if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)) {

                    var eval('(' this ')');

                    if (typeof filter === 'function') {

                        function walk(kv) {
                            if (&& typeof === 'object') {
                                for (var in v) {
                                    if (v.hasOwnProperty(i)) {
                                        v[i] = walk(iv[i]);
                                    }
                                }
                            }
                            return filter(kv);
                        }

                        walk(''j);
                    }
                    return j;
                }
            catch (e) {

            // Fall through if the regexp test fails.

            }
            throw new SyntaxError("parseJSON");
        };

正则表达式会是false, 从而抛出异常. 这里主要的原因是为什么AJAX返回空字符串. 比如一个典型的ajax url:

"http://127.0.0.144/shop/admin/goods.php?is_ajax=1&act=remove&id=32&cat_id=0&intro_type=&is_promote=0&stock_warning=0&brand_id=0&keyword=&suppliers_id=&is_on_sale=&sort_by=goods_id&sort_order=DESC&extension_code=&is_delete=0&real_goods=1&record_count=1&page_size=15&page=1&page_count=1&start=0&1346143445423423"

这个url会执行一个删除商品的动作然后执行一个header location进行重定向, 重定向的url会返回刷新的商品列表的JSON表示, 返回到ajax中, 解析之后写入到HTML页面上. 这原本是一个普通的ajax删除然后刷新, 而且在IE下面工作很正常. 而Chrome里面行不通. 搜索了一下, 这可能是一个问题, 不知道算不算bug, 在Chrome 18里面就提出来了, 例如 http://code.google.com/p/chromium/issues/detail?id=121614 , 我用的是19, 显然问题还是没有修复.

先讲一下原理, header Location会发送一个302的HTTP响应, 按照WEB标准, 浏览器应该透明的执行重定向, 应用程序应该可以对重定向的过程毫不知情, 也不应该收到302这个状态, 例如请求a.php, 但是得到的是b.php的输出, 如果b.php正常执行那么ajax应该直接得到一个200的响应. 那么Chrome是如何处理这个问题的? 在控制台中打开Network面板得到下面的图:

Chrome收到了302, 但是每一个302都是canceled. 显然这是浏览器内部的行为, 与脚本无关. 然后我用了jQuery来测试了一下


a.php

<script>
$(document).ready(function(){
  $('#abutton').click(function(){

        $.ajax({
        type: 'GET',
        url: 'b.php',
        data: {
            usertheme : 'sss'
        },
        dataType: 'text',
        
        success : function(xml) {
            var a = 'jjiji';alert('success' + xml);
        },
        error : function(a,b,c) {
            var b = 'sjfiwf'; alert('error');
        },
        complete : function() {
            alert('complete');
        }
    });
        
  });
});
</script>
<body>
<a href="javascript:;" id="abutton">ajax请求</a>
</body>

b.php

<?php
//echo 'ajfeiofjei;'; exit;
header("Location: c.php"); exit;
?>

c.php

<?php

echo 'output form c.hphp'exit;
?>

结果$.ajax的error函数被调用.

Chrome的控制台有个"Copy as HAR"的功能, 是请求响应的详细信息, 从中可以发现一点蛛丝马迹:

        "request": {
          "method""GET",
          "url""http://127.0.0.144/shop/admin/goods.php?is_ajax=1&act=remove&id=32&cat_id=0&intro_type=&is_promote=0&stock_warning=0&brand_id=0&keyword=&suppliers_id=&is_on_sale=&sort_by=goods_id&sort_order=DESC&extension_code=&is_delete=0&real_goods=1&record_count=1&page_size=15&page=1&page_count=1&start=0&1346143445423423",
          "httpVersion""HTTP/1.1",
          "headers": [
            {
              "name""Accept-Encoding",
              "value""gzip,deflate,sdch"
            },
            {
              "name""Accept-Language",
              "value""zh-CN,zh;q=0.8"
            },
            
   
          ],
          "cookies": [
            {
              "name""ECS_LastCheckOrder",
              "value""Tue%2C%2028%20Aug%202012%2008%3A43%3A40%20GMT",
              "expires"null,
              "httpOnly"false,
              "secure"false
            },
         
          ],
          "headersSize"2775,
          "bodySize"0
        },
 
 
 
 
 
        "response": {
          "status"302,
          "statusText""Found",
          "httpVersion""HTTP/1.1",
          "headers": [
         
            {
              "name""Server",
              "value""Apache/2.0.63 (Win32) PHP/5.2.10"
            },
            {
              "name""Location",
              "value""goods.php?act=query&is_ajax=1&&id=32&cat_id=0&intro_type=&is_promote=0&stock_warning=0&brand_id=0&keyword=&suppliers_id=&is_on_sale=&sort_by=goods_id&sort_order=DESC&extension_code=&is_delete=0&real_goods=1&record_count=1&page_size=15&page=1&page_count=1&start=0&1346143445423423"
            },
         
          ],
          "cookies": [],
          "content": {
            "size"0,
            "mimeType""text/html",
            "compression"0
          },
          "redirectURL""goods.php?act=query&is_ajax=1&&id=32&cat_id=0&intro_type=&is_promote=0&stock_warning=0&brand_id=0&keyword=&suppliers_id=&is_on_sale=&sort_by=goods_id&sort_order=DESC&extension_code=&is_delete=0&real_goods=1&record_count=1&page_size=15&page=1&page_count=1&start=0&1346143445423423",
          "headersSize"712,
          "bodySize"0
        },
        "cache": {},
        
        "pageref""page_12"
      },
      
      {



        "request": {
          "method""GET",
          "url""http://127.0.0.144/shop/admin/goods.php?act=query&is_ajax=1&&id=32&cat_id=0&intro_type=&is_promote=0&stock_warning=0&brand_id=0&keyword=&suppliers_id=&is_on_sale=&sort_by=goods_id&sort_order=DESC&extension_code=&is_delete=0&real_goods=1&record_count=1&page_size=15&page=1&page_count=1&start=0&1346143445423423",
          "httpVersion""HTTP/1.1",
          "headers": [
            {
              "name""User-Agent",
              "value""Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.1 Safari/536.5"
            },
            {
              "name""Referer",
              "value""http://127.0.0.144/shop/admin/goods.php?act=list"
            }
          ],
  
          ],
          "cookies": [],
          "headersSize"495,
          "bodySize"0
        },




        "response": {
          "status"0,
          "statusText""",
          "httpVersion""HTTP/1.1",
          "headers": [],
          "cookies": [],
          "content": {
            "size"0,
            "compression"0
          },
          "redirectURL""",
          "headersSize"13,
          "bodySize"0
        },

里面记录了请求响应的流程, 这里面第一个url请求, 响应是一个302, 之后根据302响应头中的Location字段构造了一个重定向的请求, 得到一个状态值为0的响应, 这个响应就是在ajax返回的时候所看到的那个, 符合透明重定向的原则. 看起来似乎有一个GET请求是指向重定向的url的, 而且还有一个response, 这个response的状态值是0. 那么这个GET请求到底有没有发出去, 答案是没有. Chrome只是构造了这个请求, 然后因为某种原因canceled, 之后又构造了一个response, 一个没有任何内容的response, 返回给ajax.

仍然没有搞清楚的是Chrome根据什么条件决定cancel这个重定向请求 ?

有一点要提到的是, 有些帖子里面说判断status的值, 如果是302就获取其header中的Location, 然后赋值给document.location. 这是行不通的, 因为302状态码是不会在脚本中见到的, 脚本要么得到一个错误或者200或者状态0 . StackOverflow上面有好多这样的例子, 容易误解.