NodeJs未诞生前,如果一个前端程序员想要通过纯粹的js来实现跨域获取xml数据是想都不敢想的,因为浏览器的同源安全策略拒绝端对端的数据访问, 通常我们需要借助后端程序通过代理中转来实现。
随着NodeJs的问世,前端程序员可以做以往很多由php,java等才能实现的后端功能,最直接简单的就是我们今天所要探讨的jsonp。
要实现今天的主题,我们先来掌握一下nodejs的基本运行机制。nodejs 分为程序类和服务类, 程序类是指不需要创建服务器而实现的功能,比如抓取一个页面的内容保存到你自己的开发机上,供其它场景使用。此时,就可以通过http模块的Get方法去请求网页内容,即要实现的读取数据源;然后通过引入文件模块fs进行写操作,写抓取到的内容到自己想要的地方。写nodejs程序的时候,完全符合按需加载的规范。要实现上述两个功能,只要加载引入http模块和fs模块即可。如下代码:
var http = require('http');
var fs = require('fs');
http.get('http://*****/xml/e_gamedata.xml',function(html){
var cont='';
html.on('data',function(content){
cont += content.toString('utf-8');
})
html.on('end',function(){
fs.writeFile('message.xml', cont, function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
})
});
在此程序中, http.get的回调中,有两个重要的方法需要掌握,一个是接收数据的data,表示有数据接收后要执行的操作,用法就是html.on('data',function(){ do something }); 另一个方法是end,是指此get 数据读取完毕后要执行的操作。ok,运行 node 测试一下吧!如果成功,控制台会告诉你"it's saved"。 是不是很简单呢!
好了我们已经实现了简单的读写操作,那么我们如何实现跨域jsonp方案呢?其实原理跟php等后端程序的实现方案一样,通过nodejs来搭建http服务器作为中转。不过这个是前端的一大荣誉,终于可以用自己的语言来写服务类应用了。首先用nodejs来创建http服务器吧!相当简单的一句话 http.createServer(function(req, res){ so something }); 我们通过这句就创建了一个http的服务器,接下来的步骤就是如何跟前端进行通信了。
前面已经说了和php实现jsonp的方案一样,nodejs服务端通过和前端双方约定信任,即callback参数,来进行通信。 前端通过 getJSON('url+callback=?',function(data){ console.log(data) })向nodejs服务端发送请求,同时发送通信信任参数callback。服务端接到请求后,以接收到的callback参数回传,同时返回数据到前端。如果双方验证信任参数callback一致,就完成了的jsonp通信方案。
服务端解析请求需要用到url模块,生成json格式数据需要用到JSON.stringify()方法,具体代码:
var http = require('http');
var urllib = require('url');
var port = 10011;
var jsondata = [{name:'ang xiao',age:'30',school:'xue feng',score:'98'},{name:'da long',age:'20',school:'aaa',score:'88'}];
http.createServer(function(req, res){
var params = urllib.parse(req.url, true);
var str = params.query.callback + '(' + JSON.stringify(astr) + ')';//jsonp
res.end(str);
}).listen(port, function(){
console.log('server is listening on port ' + port);
})
ok,node一下,前端通过getJSON访问此服务地址看看接收到的数据吧!getJSON('http://**.**.**.**:10011?callback=?',function(data){ console.log(data) });
到此,我们已经实现了get抓取xml数据和jsonp方案。把这两者结合起来就是实现我们的主题功能。“NodeJs 实现 jsonp 方式获取xml数据”;
最后一步的难点在于我们如何在后端实现xml转换为json,读取xml数据有很多第三方的模块,如xmlreader,xml-digester;我自己用了xmlreader来实现。 安装这个第三方模块 npm install xmlreader, 然后引入此模块。用法为:xmlreader.read(xmlstr,function(errors, response){if(null !== errors ){console.log('errors');return;} dosometing });
xmlreader read后,可以在控制台打印一下回调 查看回调的一些属性和方法。其实xmlreader是把读取的数据转换为了一个objecet, 你就可以a.b.c的方法去取到你想要的数据。其中一个重要的方法是each,相当于一个数组。
{ text: [Function],
DOCUMENT:
{ attributes: [Function],
parent: [Function],
count: [Function],
at: [Function],
each: [Function],
text: [Function],
funny:
{ attributes: [Function],
parent: [Function],
count: [Function],
at: [Function],
each: [Function],
text: [Function],
game: [Object] },
competitive:
{ attributes: [Function],
parent: [Function],
count: [Function],
at: [Function],
each: [Function],
text: [Function],
game: [Object] } } }
你要做的就是操作拼装你需要的数据,然后进行json转换,再然后返回到前端。 ok,看到此,我默认你已了解了实现方案的原理。上代码:
var http = require('http');
var xmlreader = require('xmlreader');
var urllib = require('url');
var port = 10011;
var xmlstr = '';
var jsondata = {};
http.get('http://*******/xml/e_gamedata.xml', function (html) {
html.on('data',function (content) {
xmlstr += content;
})
})
http.createServer(function(req,res){
xmlreader.read(xmlstr, function (errors, response) {
if (null !== errors) {
console.log('errors');
return;
}
var oSourceFunny=[],oSourceCompeti=[];
response.DOCUMENT.funny.game.each(function (i, item) {
oSourceFunny.push({
name : item.name.text(),
intro : item.intro.text(),
score : item.score.text(),
type : item.type.text(),
property : item.property.text(),
feature : item.feature.text(),
img : item.img.text(),
link : item.link.text(),
from : item.from.text()
});
});
response.DOCUMENT.competitive.game.each(function (i, item) {
oSourceCompeti.push({
name : item.name.text(),
type : item.type.text(),
img : item.img.text(),
link : item.link.text(),
from : item.from.text()
});
});
jsondata = {oSourceFunny:JSON.stringify(oSourceFunny),oSourceCompeti:JSON.stringify(oSourceCompeti)};
var params = urllib.parse(req.url, true);
if (params.query && params.query.callback) {
var calldata = params.query.callback + '(' + JSON.stringify(jsondata) + ')'; //jsonp
res.end(calldata);
} else {
res.end(jsondata);//普通的json
}
})
}).listen(port,function(){
console.log('this server run at'+port);
})
至此,一整套完整的前端获取解析xml数据的jsonp方案实现了。
要了解nodejs http和url、fs等模块使用方法,请查阅nodejs官网接口文档。http://nodejs.org/api/
想要了解 xml-digester使用方法,请参阅 http://jackie198512.blog.163.com/blog/static/9607024201391823011748/ 。
据称 xml-digester 可以直接将文档转换为json,个人没有用过,如果真如所说,倒是节省了很多后台的读取操作,那真正是终极完美方案。