node.js实现跨域AJAX post

最近遇到一个需求,需要做一个网页实现停电查询的功能。


不知道是跨域问题


我建站使用的是快站,但是请求数据的来源来自于国家电网。快站(https://www.kuaizhan.com/)使用的协议是使用 ssl 加密的 https 国家电网(http://www.95598.cn/)使用的是http 二者协议不同没有办法通信,使用 ajax 时会报错。


于是我改为自己写一个网站,然后网站采用http协议。在使用国家电网查询信息的时候,打开控制台,分析报文,发现95598(电网)使用json来传输数据,这为我的需求解决提供了可能性,我也可以使用ajax发送get/post请求报文来获取json数据。

定位问题为跨域


在网页中使用$.ajax 获取数据代码如下

$.ajax({
				type:"get",
				url:"http://www.95598.cn/95598/outageNotice/queryOutageNoticeList?orgNo=51412&outageStartTime=2017-08-16&outageEndTime=2017-08-23&scope=&provinceNo=51101&typeCode=&lineName=&anHui=02",
				async:true,
				success:function(data){
					if(data.query.results) {
						//$("#outage").text(JSON.stringify(data.query.results));
						var J_data = JSON.parse(JSON.stringify(data.query.results));
						//alert(J_data.json.today);
						messageStr(J_data.json);
					} else {
						$("#content").text('no such code: ' + code);
					}
				}
			});
然后报错,错误如下
[Web浏览器] "XMLHttpRequest cannot load http://www.95598.cn/95598/outageNotice/queryOutageNoticeList?orgNo=51412. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8020' is therefore not allowed access." /MyProject/index.html (0)

错误的意思是不可以请求非同源的数据,除非请求源的头部添加了,Access-Control-Allow-Origin:* 这样其他对象可以来请求数据。

后台不在我这里,我没有办法在网站中添加 Access-Control-Allow-Origin,所以这时候要采用其他办法


解决方式:JSONP

$(document).ready(function(){
   var url='http://localhost:8080/WorkGroupManagment/open/getGroupById"
       +"?id=1&callback=?';
   $.ajax({
     url:url,
     dataType:'jsonp',
     processData: false, 
     type:'get',
     success:function(data){
       alert(data.name);
     },
     error:function(XMLHttpRequest, textStatus, errorThrown) {
       alert(XMLHttpRequest.status);
       alert(XMLHttpRequest.readyState);
       alert(textStatus);
     }});
   });

其与json传输不同的地方有两点:

1.在请求中要 dataType:'json'  ==> dataType:'jsonp'

2.在返回的数据方面 json : 

{
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
}

jsonp:

callback({
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
})
jsonp比json多了一个回调函数的函数名,因为二者工作方式不同,AJAX其实是XMLHttpRequest 请求来实现异步加载数据,而jsonp原理相当于加载一段js代码在获取数据的同时返回了js对象,这样就可以直接使用callback。但是这样方式并不适合我使用,所以继续去找下一种解决办法


解决方式:XHR2

HTML5中提供的XMLHTTPREQUEST Level2(及XHR2)已经实现了跨域访问。但ie10以下不支持

只需要在服务端填上响应头:

header("Access-Control-Allow-Origin:*");
/*星号表示所有的域都可以接受,*/
header("Access-Control-Allow-Methods:GET,POST");
 
这种我没有试过,因为我并没有服务端。而且这种方案也不能帮助我解决需求

解决方式:YQL

雅虎的Yahoo Query Language (YQL)通过它可以把 Web 上的各种数据服务作为数据库表来查询,并获得结果。

通过yql服务, 可以把https://openapi.baidu.com/api的内容再次封装, 还可以把接口返回的内容, 再次使用sql语句查询, 然后再通过yql服务返回最终结果

  • 具体使用方法:
  • 打开网址https://developer.yahoo.com/yql/
  • 找到示例, 把默认的示例替换成你自己的: select * from html where url='你要访问的api地址'
  • 然后点击Test, 就能看到Response里面已经有查询结果了
  • 还可以定制查询select id,name from html where url='你要访问的api地址', 定制返回结果
  • 最后复制Endpoint里面的链接大概是这样https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20urlles.org%2Falltableswithkeys
  • 最后, 把得到的链接地址复制到自己的js里面去执行就能正确拿到json

$.getJSON("http://query.yahooapis.com/v1/public/yql?q=select * from json where url=\"http://www.95598.cn/95598/outageNotice/queryOutageNoticeList?orgNo=51412&outageStartTime=2017-08-16&outageEndTime=2017-08-23&scope=&provinceNo=51101&typeCode=&lineName=&anHui=02\"", function (data) {
    console.log(data);
});

到这就结束了, 已经能在控制台输出结果了


这个解决方案不好的一点就是又通过了一次yql, 等于是yql先拿到结果, 然后又到浏览器, 中转了一次, 好的一点是不用自己再去动服务器代码

虽然用这个方法,我获取到了停电信息,但是部门等信息我却没办法获得,因为这种办法只适合把参数写在url中get请求,对于post请求无能为力。

最终解决方案:node.js服务器代理

node.js可以快速构建后台,我可以将整个项目搭建在这个服务器上,在需要请求95598相关信息时直接转发到国家电网的网页,步骤如下:

  1. 安装Nodejs: 下载地址:http://nodejs.org/download/
  2. 设置环境变量,例如我将nodejs装在D:/program文件夹下,则设以下为系统环境变量
    [html]  view plain  copy
     print ?
    1. D:\Program\nodejs  

  3. 安装Express开发框架:
    [html]  view plain  copy
     print ?
    1. //命令行输入命令  
    2.         npm install -g express  
    3.         npm install -g express-generator  
    4.            
  4.  新建项目
    [html]  view plain  copy
     print ?
    1. //命令行输入命令  
    2.         express -t ejs newsproject  

  5.  按照提示进入项目目录,运行npm安装
    [html]  view plain  copy
     print ?
    1. //进入项目目录  
    2.         cd newsproject  
    3. //输入命令安装  
    4.         npm install  

  6.  运行项目
    [html]  view plain  copy
     print ?
    1. //项目目录下运行命令  
    2.          node app.js  

  7.  浏览器访问:http://127.0.0.1:3000/即可见nodejs站点页面,页面输出:Express
  8. 在app.js中添加js路径规则:
var ajax = require('./public/javascripts/express_demo');
app.use('/95598',ajax);
当浏览器有95598的请求就会转发给express_demo.js
编辑express_demo.js,如下所示
//  一级单位获取POST 请求
router.post('/region/findOrgList', function (req, res) {
	console.log("post for org list");
    
	//不能正确解析json 格式的post参数
	var body = '',jsonStr;
	
	req1 = http.request(options, (res1) => {
		console.log(`STATUS: ${res.statusCode}`);
		console.log(`HEADERS: ${JSON.stringify(res1.headers)}`);
		res1.setEncoding('utf8');
		
		res1.on('data', (chunk) => {
			console.log(`BODY: ${chunk}`);
			body += `${chunk}`; //读取参数流转化为字符串
			console.log('\n\n\n body------------'+body);
			}).on('end', () => {
			console.log('No more data in response.');
			console.log(res1.headers);
			console.log('body');
			res.writeHead(200, {'Content-Type': 'application/json'});
			res.end(body);
		}).on('error', (e) => {
			console.error(`problem with request: ${e.message}`);
		})
		
		
	});
	
	// write data to request body
	req1.write(postData);
	req1.end();
	//console.log('\n\n\n before send body------------'+body);
	
})
我这里做的有点复杂,在获取请求的同时向95598发送了同样的请求,用代理会简便很多,但是这样其实也有好处,可以定制不同的请求头,也可以处理数据,实际上也只起到了一个代理的作用。
获取数据如下
[{"value":"北京市电力公司","code":"11102","codeType":"02","pcode":null},{"value":"国网天津市电力公司","code":"12101","codeType":"02","pcode":null},{"value":"河北省电力公司","code":"13102","codeType":"02","pcode":null},{"value":"山西省电力公司","code":"14101","codeType":"02","pcode":null},{"value":"内蒙古东部电力有限公司","code":"15101","codeType":"02","pcode":null},{"value":"辽宁省电力有限公司","code":"21102","codeType":"02","pcode":null},{"value":"吉林省电力有限公司","code":"22101","codeType":"02","pcode":null},{"value":"黑龙江省电力有限公司","code":"23101","codeType":"02","pcode":null},{"value":"上海市电力公司","code":"31102","codeType":"02","pcode":null},{"value":"江苏省电力公司","code":"32101","codeType":"02","pcode":null},{"value":"浙江省电力公司","code":"33101","codeType":"02","pcode":null},{"value":"安徽省电力公司","code":"34101","codeType":"02","pcode":null},{"value":"福建省电力有限公司","code":"35101","codeType":"02","pcode":null},{"value":"江西省电力公司","code":"36101","codeType":"02","pcode":null},{"value":"山东省电力公司","code":"37101","codeType":"02","pcode":null},{"value":"河南省电力公司","code":"41101","codeType":"02","pcode":null},{"value":"湖北省电力公司","code":"42102","codeType":"02","pcode":null},{"value":"湖南省电力公司","code":"43101","codeType":"02","pcode":null},{"value":"重庆市电力公司","code":"50101","codeType":"02","pcode":null},{"value":"四川省电力公司","code":"51101","codeType":"02","pcode":null},{"value":"西藏自治区电力有限公司","code":"54101","codeType":"02","pcode":null},{"value":"陕西省电力公司","code":"61102","codeType":"02","pcode":null},{"value":"甘肃省电力公司","code":"62101","codeType":"02","pcode":null},{"value":"青海省电力公司","code":"63101","codeType":"02","pcode":null},{"value":"宁夏区电力公司","code":"64101","codeType":"02","pcode":null},{"value":"新疆区电力公司","code":"65101","codeType":"02","pcode":null},{"value":"冀北电力公司","code":"66103","codeType":"02","pcode":null}]

参考:
http://www.cnblogs.com/zhouyalei/p/6379477.html
http://www.jb51.net/article/91627.htm
http://blog.csdn.net/dreamzml/article/details/8902591
http://www.tuicool.com/articles/vYBR3y
http://blog.csdn.net/hearain528/article/details/45460287 (实际安装node.js项目使用的)
http://www.95598.cn/95598/outageNotice/initOutageNotice?type=1&partNo=PM06003002(国家电网停电查询)

你可能感兴趣的:(AJAX)