码迷,mamicode.com
首页 > 其他好文 > 详细

常用跨域资源请求分析

时间:2015-03-08 23:06:35      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:web   跨域   html5   nodejs   

     WEB开发过程中最常使用 Ajax技术来完成客户端与服务器的通信。而实现Ajax通信的XmlHttpRequest对象会带来跨域安全策略问题。简单来说,默认情况下,XHR对象只能访问与包含它的页面位于同一个域下的资源。

    那么问题来了,何为跨域呢?通常,Ajax指向的地址中,二级域名/端口号/协议/必须与包含它的页面相同。举个栗子:

     www.tangide.com 访问 www.i5r.com是跨域。
    a.tangide.com 访问 b.tangide.com是跨域。
    www.tangide.com:8080 访问 www.tangide.com:8090是跨域。
    http://www.tangide.com 访问https://www.tangide.com是跨域。

    跨域虽然会带来一些安全性方面的问题,但有时候跨域请求资源也是必要的。下面介绍两个常用的跨域方法。

    1)CORS:简称为跨域资源共享

    大概的工作原理可以理解为:A上的页面想要获取B上的资源,浏览器会先发送一个HEAD请求获取B服务器的http header 判断Access-Control-Allow-Origin是否有A(根据之前的栗子推测)
如果有则浏览器允许跨域,否则拒绝~

    简单写个测试代码,先用Nodejs搭建一个简单的服务器:

	var http = require('http');

	var server = http.createServer(function(req, res) {
     		res.writeHead(200, {'Content-Type':'text/plain',
                         'Access-Control-Allow-Origin':'http://www.i5r.com',
                         'Access-Control-Allow-Headers':'If-Modified-Since'})    ;
		console.log((req.url));
		console.log((req.headers));
		res.end(JSON.stringify({code: 0, message:'ok'}));
	});
 
	server.listen(8090);
     服务器监听8090端口。Access-Control-Allow-Origin选项列出了服务器允许跨域资源请求的origin。Access-Control-Allow-Headers选项用于配置允许客户端发送的头部信息。If-Modified-Since选项比较常用,主要用于浏览器的缓存。当浏览器发送http请求获取资源的时候If-Modified-Since选项会带上资源文件最后修改的时间(GMT格式),表示如果从这个时间点后该请求的资源有变化就更新资源。否则服务器返回304状态码,表示“Not Modified”。所以如果有一些资源强制浏览器不缓存,可以把If-Modified-Since设置为0,这样每次都从服务器获取一次资源。

    接下来编写客户端测试代码:

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv='Content-Type' Content='type=text/html;chatset=utf-8'>
	<script type='text/javascript' src='ajax.js'></script>
	<script type="text/javascript">
		window.onload = function() {
			var button = document.createElement('input');	
			button.setAttribute('type', 'button');
			button.setAttribute('value', 'send request');
			document.getElementsByTagName('body')[0].appendChild(button);
			button.onclick = function() {
				window.handle = Ajax.getInstance();
				handle.testAction('/read.php', function(result) {
					console.log(JSON.stringify(result));
				});
			};
		};		
	</script>
</head>
<body>
</body>
</html>
    发送Ajax请求的相关代码:

funcntion testAction(action, onDone) {
		var info = {};
		info.method = 'post';
		info.url = 'http://www.i5r.com:8090' + action;
		httpSendRequest(info);
	}
     function httpSendRequest(info) {
	var xhr = new window.XMLHttpRequest();

	if(!info || !info.url) {
		return false;
	}

	var url = info.url;
	var data= info.data;
	var method = info.method ? info.method : "GET";

	xhr.open(method, url, true);
	xhr.send(info.data ? info.data : null);
	xhr.onreadystatechange = function() {
		if(info.onProgress) {
			info.onProgress(xhr);	
		}	
		if(xhr.readyState === 4) {
			if(info.onDone) {
				info.onDone(true, xhr, xhr.responseText);
				console.log("response:" + xhr.responseText);
			}
		}
		console.log("onreadystatechange:" + xhr.readyState);
		return;
	};
	
    服务器虽然监听的是8090端口,但是已经把www.i5r.com添加到了跨域允许列表所以,正常情况下输出如下:
response:{"code":0,"message":"ok"}
onreadystatechange:4 
    我们可以把‘Access-Control-Allow-Origin‘:‘http://www.i5r.com‘这句稍微做一点改动,比如默认支持8080端口的跨域请求:‘Access-Control-Allow-Origin‘:‘http://www.i5r.com‘。刷新页面:点击发送请求,会得到如下错误信息:

    

XMLHttpRequest cannot load http://www.i5r.com:8090/read.php. The 'Access-Control-Allow-Origin' header has a value 'http://www.i5r.com:8080' that is not equal to the supplied origin. Origin 'http://www.i5r.com' is therefore not allowed access.
    错误信息已经说明了原因了~
   如果是用express搭建的服务器可以如下配置:

	app.use(function(req, res, next) {
	  res.header("Access-Control-Allow-Origin", "*");
	  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
	  next();
	});
    这里的"*"是一个通配符,表示允许所以的请求源跨域。

    2) 使用JSONP完成跨域资源请求,首先编写前端测试代码:

<html>
<head>
	<meta http-equiv='Content-Type' Content='type=text/html;chatset=utf-8'>
	<script type='text/javascript' src='ajax.js'></script>
	<script type="text/javascript">
		function handleResponse(res) {
			console.log(res);				
		}

		window.onload = function() {
			var button = document.createElement('input');	
			button.setAttribute('type', 'button');
			button.setAttribute('value', 'send request');
			document.getElementsByTagName('body')[0].appendChild(button);
			button.onclick = function() {
				var script = document.createElement('script');
				script.src = 'http://www.i5r.com:8090?callback=handleResponse';
				document.body.insertBefore(script, document.body.firstChild);
			};
		};		
	</script>
</head>
<body>
</body>
</html>
    处理函数在收到服务器响应后直接打印反馈信息。

    修改服务器测试代码:

var http = require('http');
var urlParser = require('url');

var server = http.createServer(function(req, res) {
	res.writeHead(200, {'Content-Type':'text/plain'});

	console.log(req.url);
	var query = urlParser.parse(req.url, true).query;
	console.log(query);
	if(query.callback) {
		var str = query.callback + "(" +JSON.stringify({code: 0, message:'ok'})+");";
		console.log(str);
		res.end(str);	
	}
	else {
		res.end(JSON.stringify({code: 0, message:'ok'}));
	}
});

server.listen(8090);
    前端在发送请求的时候把"回调"函数的名称加到url后面,服务器在处理请求的时候直接返回需要执行回调函数的数据格式,这样浏览器在判断当前页面有同名函数的时候就会执行该函数。这样就完成了一次跨域请求。

    最后需要说明的是,限制跨域是浏览器的行为,而不是JS或DOM的行为,如果有能力可以自己开发一个浏览器来完成跨域支持:)。

    跨域请求不同方式各自都有利有弊,如果你也在研究类似的问题,欢迎交流~

    参考资料:https://github.com/drawapp8/gamebuilder/wiki/%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3

    





常用跨域资源请求分析

标签:web   跨域   html5   nodejs   

原文地址:http://blog.csdn.net/gentlycare/article/details/44138261

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!