最近公司的项目中,有一个模块需要调用集团提供的数据文件(本质上是一 js 文件,存储的是 json 对象)。公司项目的域名和集团的域名不一样,ajax 为了安全考虑,不允许异域调用(某些 js 框架实现了异域调用,如 dojo,这里我不打算使用 dojo 的 ajax),因此我们可以通过在页面动态创建 script 节点:
/** * 该段代码使用了 prototype.js,这里略过 prototype.js 的引入 * 使用 document.createElement 的方式动态创建 script 脚本 * * @param scriptId <script> 节点的 id * @param url 请求的 js 的 url * @param callback 加载完 js 后的回调函数 */ function loadScript(scriptId, url, callback) { // 根据 url 中是否出现过 "?" 来决定添加时间戳参数时使用 "?" 还是 "&" var paramPrefix = url.indexOf("?") == -1 ? "?" : "&"; url = url + paramPrefix + "rnd=" + new Date(); var script = $(scriptId); // 没有 id 为 scriptId 的 script 节点,创建并附加到 document.body 上 if (!script) { script = document.createElement("script"); script.id = scriptId; document.body.appendChild(script); } script.src = url; // script 节点创建后,加载 url 所指定的资源后,可以进行后续处理 // 不同浏览器判断加载是否完成的回调不一样 // for firefox, google etc. script.onload = function() { if (callback) { callback(); } /** * 对于创建过的 scriptId,传递的 url 即使在改变,firefox 中并不会再去加载,ie 是当每次 script * 的 url 改变时重新加载资源。为了解决在 firefox 中改变 url 不重新加载的问题,这里把上次加载资源 * 成功后的回调函数执行完毕后,将 script 节点移除,下次就会重新创建 script 节点。 */ document.body.removeChild(script); } // for ie script.onreadystatechange = function() { if (this.readyState == "loaded" || this.readyState == "complete") { if (callback) { callback(); } } } };
上述代码在 callback 函数中可以处理异域返回的 js,但这里又出现了一个问题。公司项目统一采用的是 UTF-8 编码,而集团采用的却是 GBK 编码,因此通过上述代码,我们在异域上拿到的 response 的编码和页面编码不一致,这就导致了乱码的出现。而 javascript 本身不能处理各种字符编码间的转换。可我们知道,java 是可以处理字符编码间的转换的,基于这点,我们大概知道了思路。
PS: 之后,我和集团的同事沟通了下,其实解决这个很简单,为 script 指定编码即可。即在上述代码的第 20 行 添加:script.charset = "GBK";。但下面阐述的方法也是一种解决方案,能用那一句话解决问题的,其实也就没有必要用下面的方法了。
我们在页面发送 ajax 请求,发送的参数就是异域资源的 url。公司项目的服务器在接到这个 ajax 请求后,再通过 http 调用(ajax 传来的异域资源的 url),拿到异域返回的页面字符串(这里异域返回的是 js 片段,java 程序仍然认为是字符串),这里,java 程序可以自由地进行字符编码的转换。最后,将处理好编码的字符串发送至客户端(这里由于知道是 js,而且是 json 对象,我们可以在 java 代码中使用 renderJSON 直接向客户端输出 json)即可:
/** * 该段代码使用了 prototype.js,这里略过 prototype.js 的引入 * 通过 ajax 请求拿到异域的数据 * * @param (String) dataUrl 异域资源的地址 * @param (Function) callback 成功拿到异域数据后的回调函数 */ function loadData(dataUrl, callback) { var url = contextPath + "/quotation/qc!loadData.action"; new Ajax.Request(url, { encoding:"UTF-8", method:"post", // dataUrl 即是向异域请求的数据地址 parameters: {dataUrl: dataUrl}, onComplete: function(response) { if (callback) { callback(response); } } }); }; // 使用 loadData 的示例 loadData("http://res.test.com/some.js", function(response) { eval(response.responseText); // 假设异域返回的数据为:var data = { server: "apache", version: "1.0" }; // 通过 eval 后,我们可以使用 data alert(data.server); });