@H_3010@前言
@H301_0@本文将从实践角度介绍如何使用jsonp和代理服务器方案解决跨域问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
@H_301_0@浏览器为了保护用户安全,引入了同源策略,即一个服务器页面无法访问另一个协议、域名、端口不同的服务器数据。当页面需要跨服务器访问另一个服务器的数据时,即产生跨域行为。以豆瓣的公开API(https://api.douban.com/v2/book/1220562)为例,当前我的服务器处于http://127.0.0.1:5000下,豆瓣的服务器很显然跟我的服务器不同源,服务器中的一个页面通过AJAX请求该接口时,浏览器会发出如下警告,并且页面获取数据失败:
<p style="text-align: center">
@H_301_0@在实际开发中,如果遇到这样的跨域问题,可以通过以下办法获得跨域的数据:
- 异源服务器的响应头部设置Access-Control-Allow-Origin允许跨域行为
- JSONP
- 设置自己的代理服务器转发异源的数据
def allow_cross_domain(fun):
@wraps(fun)
def wrapper_fun(*args,*kwargs):
rst = make_response(fun(args,*kwargs))
rst.headers['Access-Control-Allow-Origin'] = ''
rst.headers['Access-Control-Allow-Methods'] = 'PUT,GET,POST,DELETE'
allow_headers = "Referer,Accept,Origin,User-Agent"
rst.headers['Access-Control-Allow-Headers'] = allow_headers
return rst
return wrapper_fun
@H_301_0@如果在express搭建的服务器中,类似的可以加入这样一个中间件:
@H_301_0@但是设置Access-Control-Allow-Origin的方法有个致命的缺陷,就是只能在提供接口的服务器上进行添加,如果该服务器不是自己开发的话(例如上面提到的豆瓣公开API),这个方法基本可以忽略,那么留给我们自由发挥的方法就只有JSONP和代理服务器了。网上有关于很多JSONP和代理服务器解决跨域的介绍,但都缺少具体的实践案例,本文将通过具体的实际案例来了解这两个跨域方式的具体实现。
@H_301_0@撰写本文时,我手头上可以直接拿来用的后端方案为Flash搭建的RESTful服务器,前端方案为Vue 1.0 + vue-resource进行Ajax,故下面所述具体的实践操作都在这两个环境上进行,如果你的开发环境和这个有差异也没关系,本文将有最少的逻辑代码来展示跨域的实现原理,其他方案可触类旁通。
@H_301_0@@wraps(fun)
def wrapper_fun(*args,*kwargs):
rst = make_response(fun(args,*kwargs))
rst.headers['Access-Control-Allow-Origin'] = ''
rst.headers['Access-Control-Allow-Methods'] = 'PUT,GET,POST,DELETE'
allow_headers = "Referer,Accept,Origin,User-Agent"
rst.headers['Access-Control-Allow-Headers'] = allow_headers
return rst
return wrapper_fun
@app.route('/hosts/')
@allow_cross_domain
def domains():
pass
JSONP
@H_301_0@浏览器的同源策略限制的跨域的Ajax请求资源,但是script标签中的资源却可以跨域获取,很常见的就是我们通过script标签引用其他服务器的js:函数作为一个全局函数,否则在vue的生命周期中将获取不到这个回调函数
var d = null;
function handleResponse(response){
console.log(response);
d = response;
}
@H_301_0@此时刷新页面,浏览器不再发出Access-Control-Allow-Origin的跨域错误,输出通过script获取到的数据:
@H_301_0@compiled: function() {
var self = this;
// jsonp
var script = document.createElement("script"); // 动态创建标签
script.src = "https://api.douban.com/v2/book/1220562?callback=handleResponse"; // 创建的src就是请求的API,同时需要给这个src加上一个callback的query参数,参数名字就是你的回调函数名字
document.body.appendChild(script,document.body.firstChild); // 插入新创建的script标签,这里类似Ajax发起请求
// 轮询资源获取是否结束
var timer = setInterval(function () {
if (d) {
console.log('pending')
clearInterval(timer);
self.data = d; // 将获取的数据赋值给数据model中
}
},500);
}
JSONP的缺点
@H_301_0@JSONP的缺点主要源自他的script引用资源方式,JSONP的缺点如下:- JSONP是通过script标签获取资源的,也就是说JSONP注定只能用GET的方式访问资源,GET以外的请求无法做到;
- JSONP是通过src引用不同源的代码,如果其他域的代码存在恶意代码,那么这将造成严重的网络安全,如果需要跨域的服务器不足以信任,那么必须放弃JSONP;
- 要确定JSONP请求是否成功,需要启动一个计时器监测数据变动。
代理服务器
@H_301_0@代理服务器解决跨域的思路是利用代理服务器对浏览器页面的请求进行转发,因为同源策略的限制只存在在浏览器中,到了服务器端就没有这个限制了,常用的代理服务器方案有使用反向代理服务器以及服务器内转发,使用反向代理服务器的例子是Nginx的反向代理,通过修改Nginx的配置文件,将指定的不同源域名代理到当前服务器上,浏览器就可以正常访问不同源的资源了。还有个方案是不依赖反向代理服务器,在server端对不同源的API进行转发,本文主要对这种方法进行介绍。 @H_301_0@首先代理服务器需要知道浏览器页面需要请求的API,因此,页面需要把API当做参数传递给代理服务器,形如:/proxy/:api,api参数是完整的API链接,如之前提到的豆瓣公共API:https://api.douban.com/v2/book/1220562。server端对API进行转发,在Python中可以使用requests发起HTTP请求,nodejs可以使用request,server端获得响应后将响应的结果返回给浏览器,具体的实现也很简单,以Flask为例:',methods=['GET'])
def getTasks(url):
r = requests.get(url) ## 请求转发
conver_r = eval(bytes.decode(r.content)) ##进行一些类型转化
@H_301_0@在浏览器端发起请求的具体代码为:
return json.dumps(conver_r),200
与JSONP相比代理服务器的优点
@H_301_0@相比JSONP,使用代理服务器转发不同源API的优点如下:- 资源获取是通过server端进行,可以根据需要转发的API选择使用GET以外的HTTP方法进行资源请求;
- 请求的资源需要经过server端转发到浏览器端,server端可以对资源进行处理,因此可以避免一些直接的恶意代码,比JSONP更安全;
- 浏览器页面正常使用Ajax请求数据,通过回调可以得知请求是否结束,不再需要使用计时器监测。