跨域问题常用的两种解决办法———CORS跨域资源共享和JSONP技术

写在前面

截止到目前,《JavaScript高级程序设计》这本书我基本已经看完了,但是学习的过程总是少不了不断地回顾,我原以为我掌握了的知识,实际上手操练的时候却发现依然有很多问题。于是我决定将跨域常用的两种方式总结下来,并且附上自己的代码,希望能在加深自己对知识的理解的同时,也为大家提供便利。废话不多说,让我们进入正题。

大家在使用Ajax技术的时候肯定多少都遇到过跨域问题,可能有的同学还不自知。跨域安全策略是通过xhr对象实现Ajax通信的主要限制,默认情况下xhr对象只能访问同源资源(协议,域名,端口完全相同)。只要有一个不同,请求都不能正常完成。

1.利用CORS实现跨域:

CROS(跨域资源共享)是W3C的一个工作草案,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。其背后的思想在于使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或相应是应该成功还是失败。

CORS的实现需要浏览器和服务器双方同时支持,但主要还是在后端进行操作。浏览器检测到跨域请求后会自动为其添加额外的Orgin头部,其中包含请求页面的源信息(协议,域名,端口等),以便于服务器根据这个头部信息来决定是否给予回应。而前端的请求代码并不需要改变。

这里我们首先利用Nodejs的express框架创建一个后台服务程序:

var express = require('express')
var app = express()

app.get('/cors_xhr', function(req, resp){
	resp.end('这是你要的数据')
})

app.listen(3000, function(){
	console.log('Server running at http://127.0.0.1:3000/');
})

这里部署了一个get请求的接口,访问该接口时返回数据。

接下来让我们创建前端页面:

<html>
<head>
	<title>跨域接口测试</title>
</head>
<body>

</body>
<script>
	//跨域资源共享
	var xhr = new XMLHttpRequest()
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4 && xhr.status == 200){
			console.log(xhr.responseText)
		}
	}
	xhr.onerror = function(){
		console.error("请求错误")
	}
	xhr.open('get', 'http://localhost:3000/cors_xhr', true)
	xhr.send(null)
</script>
</html>

运行发现,控制台报错:
在这里插入图片描述
一看到Access-Control-Allow-Origin字样就知道是跨域问题。那么如何解决呢?

方法其实很简单,只需要服务端在返回数据之前加一行代码:

resp.writeHead(200, {'Access-Control-Allow-Origin':'http://127.0.0.1:8848'})

注意http://127.0.0.1:8848是该项目中html页面所部署的端口,即发送请求的页面的源信息。

最终代码为:

var express = require('express')
var app = express()

app.get('/cors_xhr', function(req, resp){
	// 这是添加的一行代码,设置头部信息
	resp.writeHead(200, {
		'Access-Control-Allow-Origin':'http://127.0.0.1:8848'
	})
	
	resp.end('这是你要的数据')
})

app.listen(3000, function(){
	console.log('Server running at http://127.0.0.1:3000/');
})

如前面提到的,如果服务器认为请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源信息,即请求页面的源信息。当然,如果是公共资源的话,可以回发“ * ”。如下:

resp.writeHead(200, {'Access-Control-Allow-Origin':'*'})

这样便可以实现跨域请求,但是要注意安全问题,毕竟跨域安全策略就是为了预防一些恶意行为。如果没有这个头部,或者有这个头部但是源信息不匹配,浏览器就会驳回请求。

执行以上代码查看控制台可以看到:
在这里插入图片描述
于是乎,跨域请求成功了,怎么样,也没有想象中那么难吧?

其实跨域请求都是可以发送成功的,只是返回的时候因为源信息不匹配导致浏览器拦截掉了。即请求可以发送成功,但响应被拦截。这会导致一个问题,有的请求携带参数可能会对后端的数据造成影响。所以在有些情况下会多出一次附加请求,在确认可以跨域的前提下,再发送主请求来获取或更改数据。这便是Preflighted Requests(预检请求),是一种透明服务器验证机制,感兴趣的小伙伴可以去了解下哦。

2.JSONP技术:

JSONP技术又称为填充式JSON或参数式JSON,是应用JSON的一种新方法,在Web服务中非常流行,这是开发者们通过自己的聪明才智而发明出的一种方式。我们知道通过<script>元素的src属性访问资源可以不受同源策略的影响,于是这种方法便应运而生。

其实类似的方法还有一种,叫做图像Ping,是利用<img>元素的src属性来实现的,但是这种方法局限性大,不能访问服务器的响应文本,只适合一些与服务器进行的简单的单行跨域通信。有兴趣的朋友们可以自己去了解下。

JSONP看起来与JSON差不多,只不过是被包含在函数调用中的JSON,它由两部分组成:回调函数和数据。其实现的大致过程是这样的:

  1. 前端利用<script>元素通过参数将回调函数名传给后端,类似于
    http://localhost:3000/jsonp?callback=handler
    而handler则是本地已经定义好的回调函数的函数名
  2. 后端解析字符串,获得callback参数的值,并返回一段函数调用的字符串
  3. 前端接收到这段字符串之后,由于是<script>元素,所以这段字符将被当作脚本执行,调用本地定义的构造函数并传入参数,于是我们便获得了想要的数据

接下来让我们看看具体的代码实现:

var express = require('express')
var app = express()

app.get('/jsonp', function(req, resp){
	// 这部分主要是提取参数,大家知道怎么提取,采用的方式可以很多样化
	// 后端的核心在于提取出callback参数的值并返回函数调用字符串
	var params = url.parse(req.url).query, //获取请求参数字符串
	    query = params.split("&"), //将所有参数分离
	    queryMap = new Map()
	    
	// 提取参数,并以键值对方式保存
	for(var i=0; i<query.length; i++){
		var map = query[i].split("="),
		    key = map[0],
		    value = map[1]
		queryMap[key] = value
	}
	
	//这是你要发送的数据
	var data = {
		name: "白居易",
		age: 20
	}
	
 	// 由于需要返回字符串,这里需要将数据转化为JSON类型
	data = JSON.stringify(data)
	
	// 构造返回的函数调用字符串
	// 这里采用es6写法,不懂的朋友可以了解一下es6关于字符串的知识哦
	var back = `${queryMap['callback']}(${data})`
	
	// 结束请求并返回函数调用字符串
	resp.end(back)
})

app.listen(3000, function(){
	// 控制台会输出以下信息
	console.log('Server running at http://localhost:3000/');
})

接下来是前端代码部分,我们有两种方式可以选择:

一种是直接在网页内嵌入<script>元素,另一种是动态插入<script>元素

<!DOCTYPE html>
<html>
<head>
	<title>跨域接口测试</title>
</head>
<body>

</body>
<script>
	//JSONP回调函数
	function handler(result){
		console.log(result)
	}
	
	//采用动态插入的方式
	var script = document.createElement("script")
	script.src = "http://localhost:3000/jsonp?callback=handler"
	document.body.appendChild(script)
</script>

<!-- 采用直接插入的方式 -->
<script src="http://localhost:3000/jsonp?callback=handler"></script>
</html>

这两种方式都可以实现,一般我们采用动态插入的方式,这样可以在我们需要的时候再发送请求。下面让我们运行看看效果
效果
可以看到两种方式都能成功。

跨域的实现还有一些方法,但是这两种比较常用,所以这里重点讲解这两种方式,如果这篇文章对你产生了帮助,记得帮我点个赞哈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值