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 j = eval('(' + this + ')');
if (typeof filter === 'function') {
function walk(k, v) {
if (v && typeof v === 'object') {
for (var i in v) {
if (v.hasOwnProperty(i)) {
v[i] = walk(i, v[i]);
}
}
}
return filter(k, v);
}
j = 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上面有好多这样的例子, 容易误解.