能否将JSON响应包装为JSONP格式?JSONP解析及回调报错排查
我最近在处理JSONP跨域请求时遇到了两个棘手的问题,具体情况如下:
后端返回的标准JSON响应是:{"specialOffer":"rollback","format":"json"},我需要把它包装成类似jQuery321049913783140015044_1515731209916({"query":"test"})的JSONP格式。但实际调试时遇到了两个错误:
- 用jQuery的
$.getJSON发起请求,设置dataType: 'jsonp'时,浏览器控制台报Uncaught SyntaxError: Unexpected token :解析错误,虽然浏览器和Chrome Rest Client能正常看到响应内容,但代码就是拿不到数据。 - 后来手动设置
jsonpCallback: 'myCallback',回调函数确实触发了,但又报出jQuery.Deferred exception: Cannot assign to read only property 'undefined' of object '#' TypeError: Cannot assign to read only property 'undefined' of object '#'的错误。
核心根源:JSONP的本质要求
首先得明确:JSONP不是JSON,它是合法的JavaScript函数调用代码——通过动态创建<script>标签加载跨域资源,资源内容必须是某个函数的调用,把JSON数据作为参数传递进去。如果后端返回纯JSON,JS引擎会把它当成非法代码执行,自然会触发各种语法错误。
解决第一个错误:Unexpected token :
这个错误的原因很直接:你请求的后端返回的是纯JSON,而非JSONP格式的函数调用。当jQuery按JSONP逻辑处理时,会把纯JSON当作JS代码执行,而纯JSON的{}开头在JS里会被解析成代码块,里面的键值对语法自然不合法,就会抛出Unexpected token :。
解决分两种场景:
- 能控制后端代码:修改后端逻辑,检测请求中的
callback参数(jQuery默认会自动生成并传递这个参数,比如callback=jQueryxxx_xxx),然后把JSON数据包裹在这个回调函数里返回。比如收到callback=myCallback,就返回myCallback({"specialOffer":"rollback","format":"json"})。 - 不能控制后端:JSONP方案直接不可行,得换其他跨域方案,比如让后端配置CORS(允许你的域名跨域访问),或者通过自己的后端服务做请求代理转发。
解决第二个错误:Cannot assign to read only property 'undefined'
当你手动设置jsonpCallback: 'myCallback'后,jQuery会期望后端返回myCallback(...)的调用代码,但如果后端还是返回纯JSON,JS引擎会把这段纯JSON当成代码块执行,进而触发这种莫名其妙的赋值错误——本质还是格式不匹配导致的非法代码执行。
正确的做法是先确保后端返回正确的JSONP格式,再配合jsonpCallback(更推荐让jQuery自动生成回调名,避免全局函数污染)。如果一定要手动指定回调名,要确保全局作用域下存在这个函数:
// 全局作用域定义回调函数 function myCallback(data) { console.log('成功拿到数据:', data); } // 发起请求 $.getJSON('你的接口地址', { dataType: 'jsonp', jsonpCallback: 'myCallback' });
完整可行的示例代码
如果后端已经支持识别callback参数并返回正确的JSONP格式,最简洁的写法是:
// 用?让jQuery自动生成回调函数名 $.getJSON('你的跨域接口地址?callback=?', function(data) { console.log('specialOffer:', data.specialOffer); });
如果后端要求用其他参数名(比如不是callback而是jsonp),可以通过$.ajax的配置项指定:
$.ajax({ url: '你的跨域接口地址', dataType: 'jsonp', jsonp: 'jsonp', // 告诉jQuery用这个参数名传递回调函数名 success: function(data) { console.log('返回数据:', data); } });
内容的提问来源于stack exchange,提问作者christian gallegos

