通过jsonp解决跨域请求问题

处于安全考虑,浏览器采用同源策略限制不同协议、域名、端口之间的ajax请求,但是当我们有需求这样做的时候该怎么办呢?经过前辈们的尝试发现script标签不受同源策略的限制,因此制定了一套方案用来解决跨域的问题.如下,我们在site1.dev中向site2.dev发起一个ajax跨域请求:

<!DOCTYPE html>
<html>
<head>
	<title>jsonp</title>
</head>
<body>
	<script type="text/javascript">
		var xhr = new XMLHttpRequest ();
		xhr.onreadystatechange = function () {
			if (this.readyState === 4 && this.status === 200) {
				console.log (this.responseText);
			}
		}
		xhr.open('GET', 'http://site2.dev/test.php');
		xhr.send ();
	</script>
</body>
</html>

可以看到,我们什么数据也没有取到,而且还报了一个错:

Access to XMLHttpRequest at 'http://site2.dev/test.php' from origin 'http://site1.dev' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

也就是跨域的问题,下面我们用jsonp做一个案例,解决这个问题.总的来说,分为以下几个步骤:

1.客户端声明一个全局函数,用于接收并处理服务端数据.

2.新建script标签,链接到跨域的接口,并把全局函数的名字传递给服务端.

3.服务端设置Content-Type为text/javascript,然后返回一段js代码,在代码中调用全局函数,把数据传递进去.另外,服务端可以判断是否有函数名称传递过来,如果没有的话,按照非跨域请求正常处理即可.

下面用代码实现一下以上逻辑,客户端:

<!DOCTYPE html>
<html>
<head>
	<title>jsonp</title>
</head>
<body>
	<script type="text/javascript">
		//把跨域请求封装为一个函数
		function crossRequest (url, callback, datas = {}) {
			//生成随机函数名
			var funcName = 'jsonp' + Date.now ().toString (36) + Math.random ().toString (36).substr (3, 5);

			//拼接url和参数,把函数名称也传递到服务端
			var tempArr = [];
			for (var key in datas) {
				tempArr.push (key + '=' + datas[key]);
			}
			url = url + '?' + tempArr.join('&') + '&callback=' + funcName;

			//创建script标签,先不渲染
			var scrNode = document.createElement('script');
			scrNode.setAttribute ('type', 'text/javascript');
			scrNode.setAttribute ('src', url);

			//声明全局函数,处理服务端返回的数据
			window[funcName] = function (datas) {
				//回调处理返回数据
				callback (datas);
				//到此,整个流程结束,释放内存空间:1.删除script标签 2.释放全局函数
				document.body.removeChild (scrNode);
				delete window[funcName];
			}

			//所有准备工作已经做好,可以开始渲染标签,发起请求
			document.body.appendChild (scrNode);
		}

		//测试
		crossRequest ('http:\/\/site2.dev\/test.php', datas => {
			console.log (datas);
		});
	</script>
</body>
</html>

php服务端:

<?php 
	if ($_SERVER['REQUEST_METHOD'] === 'GET') {
		//处理数据
		$datas = "{'name': '周星星', 'age': 18}";

		//判断参数中是否有callback名称,有的话为跨域请求;没有的话则为普通请求
		if (isset($_GET['callback'])) {
			//设置响应类型为js
			header('Content-Type: text/javascript');
			//返回一段js代码,代码中调用callback,并把数据传递过去
			$func_name = $_GET['callback'];
			echo $_GET['callback'] . '(' . $datas . ')';
		} else {
			//设置相应类型为json
			header ('Content-Type: application/json');
			echo $datas;
		}
	}
?>

打印结果:

然后在site2中非跨域请求这个接口:

同样好使.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值