业务方提出的BUG,某位用户总是无法正常使用系统,初步诊断为接口JSON解析失败。
进一步检查发现,该接口返回数据:
正常情况下,“Response Headers”中“Content-Type”为“application/json; charset=utf-8
”,Chrome开发工具中“Response Body Preview”为JSON对象。
异常情况下,“Response Headers”中“Content-Type”为“application/json; charset=utf-8
”,Chrome开发工具中“Response Body Preview”为JSON字符串。
而“Response Body”初看起来,一切正常。仔细一看才发现,原来其中混入了一个控制字符“ESC”(Unicode编码为\u001b),现代浏览器(IE9-11、Chrome、Firefox)不可正常识别数据格式(IE8反而可以识别),造成JSON解析失败,进而触发Ajax的error回调。如图所示:
查询相关资料,发现之前对于JSON的理解有些狭隘,原来JSON并不完全是JavaScript的子集。
mozilla说明:
现代浏览器测试:
1 2 3 4 5 6 7 |
var code_1 = '"\u2028\u2029"'; JSON.parse(code_1); // 正常 eval(code_1); // 异常 var code_2 = '"\u001b"'; JSON.parse(code_2); // 异常 eval(code_2); // 正常 |
由此可知,需要对用户输入进行过滤。对于部分字符进行预处理,避免浏览器无法解析接口返回数据。
写了一个处理函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//过滤字符串中特殊字符,避免破坏JSON结构。 var stringJsonFilter = function(source, hideCode){ /* * 参考资料: * http://blog.codemonkey.cn/archives/437 */ var toString = Object.prototype.toString; var isArray = Array.isArray || function(a) { return toString.call(a) === '[object Array]'; }; var escMap = { '"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t' }; var escFunc = function(m) { var value = escMap[m]; if(value){ //后台可正常处理这些字符,故而不再处理。 //return value; return m; } else if(hideCode){ return " "; //用空格占位 } else{ return '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substring(1); } }; var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g; //只处理字符串类型 if (typeof(source) != "string") { return source; } else { var target = source.replace(escRE, escFunc); return target; } }; |
参考资料:
介绍 JSON
http://www.json.org/json-zh.html
JSON
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON
JSON: The JavaScript subset that isn’t
http://www.timelessrepo.com/json-isnt-a-javascript-subset