利用jsonp与代理服务器方案解决跨域问题


Posted in Javascript onSeptember 14, 2017

前言

本文将从实践角度介绍如何使用jsonp和代理服务器方案解决跨域问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

浏览器为了保护用户安全,引入了同源策略,即一个服务器页面无法访问另一个协议、域名、端口不同的服务器数据。当页面需要跨服务器访问另一个服务器的数据时,即产生跨域行为。以豆瓣的公开API(https://api.douban.com/v2/book/1220562)为例,当前我的服务器处于http://127.0.0.1:5000下,豆瓣的服务器很显然跟我的服务器不同源,服务器中的一个页面通过AJAX请求该接口时,浏览器会发出如下警告,并且页面获取数据失败:

利用jsonp与代理服务器方案解决跨域问题

在实际开发中,如果遇到这样的跨域问题,可以通过以下办法获得跨域的数据:

  • 异源服务器的响应头部设置Access-Control-Allow-Origin允许跨域行为
  • JSONP
  • 设置自己的代理服务器转发异源的数据

对于第一种设置Access-Control-Allow-Origin的方法,如果在Python Flask搭建的服务器下,可以设置一个简单的修饰器:

from functools import wraps
from flask import make_response


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

@app.route('/hosts/')
@allow_cross_domain
def domains():
 pass

如果在express搭建的服务器中,类似的可以加入这样一个中间件:

//allow custom header and CORS
app.all('*',function (req, res, next) {
 res.header('Access-Control-Allow-Origin', '*');
 res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
 res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');

 if (req.method == 'OPTIONS') {
  res.send(200); /让options请求快速返回/
 }
 else {
  next();
 }
});

但是设置Access-Control-Allow-Origin的方法有个致命的缺陷,就是只能在提供接口的服务器上进行添加,如果该服务器不是自己开发的话(例如上面提到的豆瓣公开API),这个方法基本可以忽略,那么留给我们自由发挥的方法就只有JSONP和代理服务器了。网上有关于很多JSONP和代理服务器解决跨域的介绍,但都缺少具体的实践案例,本文将通过具体的实际案例来了解这两个跨域方式的具体实现。

撰写本文时,我手头上可以直接拿来用的后端方案为Flash搭建的RESTful服务器,前端方案为Vue 1.0 + vue-resource进行Ajax,故下面所述具体的实践操作都在这两个环境上进行,如果你的开发环境和这个有差异也没关系,本文将有最少的逻辑代码来展示跨域的实现原理,其他方案可触类旁通。

JSONP

浏览器的同源策略限制的跨域的Ajax请求资源,但是script标签中的资源却可以跨域获取,很常见的就是我们通过script标签引用其他服务器的js:

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

JSONP的原理就是利用浏览器对script标签没有同源限制,动态创建script标签,把需要请求的API放在script标签的src不受同源策略限制的特性来获得数据。

JSONP由回调函数(callback)和返回的数据(response)两部分组成。回调函数(callback)是当script创建src引入资源结束时调用,返回的数据(response)作为回调函数的第一个参数传入,在回调函数里即可保存获得的不同源数据。具体来看例子,我们在页面加载的时候获取数据,请求https://api.douban.com/v2/book/1220562:

// 在vue中需要将回调函数作为一个全局函数,否则在vue的生命周期中将获取不到这个回调函数
var d = null;
function handleResponse(response){
 console.log(response);
 d = response;
}

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);
}

此时刷新页面,浏览器不再发出Access-Control-Allow-Origin的跨域错误,输出通过script获取到的数据:

利用jsonp与代理服务器方案解决跨域问题

JSONP的缺点

JSONP的缺点主要源自他的script引用资源方式,JSONP的缺点如下:

  • JSONP是通过script标签获取资源的,也就是说JSONP注定只能用GET的方式访问资源,GET以外的请求无法做到;
  • JSONP是通过src引用不同源的代码,如果其他域的代码存在恶意代码,那么这将造成严重的网络安全,如果需要跨域的服务器不足以信任,那么必须放弃JSONP;
  • 要确定JSONP请求是否成功,需要启动一个计时器监测数据变动。

针对以上JSONP的缺点,如果需要进行改进,就需要使用代理服务器了。

代理服务器

代理服务器解决跨域的思路是利用代理服务器对浏览器页面的请求进行转发,因为同源策略的限制只存在在浏览器中,到了服务器端就没有这个限制了,常用的代理服务器方案有使用反向代理服务器以及服务器内转发,使用反向代理服务器的例子是Nginx的反向代理,通过修改Nginx的配置文件,将指定的不同源域名代理到当前服务器上,浏览器就可以正常访问不同源的资源了。还有个方案是不依赖反向代理服务器,在server端对不同源的API进行转发,本文主要对这种方法进行介绍。

首先代理服务器需要知道浏览器页面需要请求的API,因此,页面需要把API当做参数传递给代理服务器,形如:/proxy/:api,api参数是完整的API链接,如之前提到的豆瓣公共API:https://api.douban.com/v2/book/1220562。server端对API进行转发,在Python中可以使用requests发起HTTP请求,nodejs可以使用request,server端获得响应后将响应的结果返回给浏览器,具体的实现也很简单,以Flask为例:

@app.route('/proxy/<path:url>', methods=['GET'])
def getTasks(url):
 r = requests.get(url) ## 请求转发
 conver_r = eval(bytes.decode(r.content)) ##进行一些类型转化

 return json.dumps(conver_r), 200

在浏览器端发起请求的具体代码为:

self.$http.get('/proxy/https://api.douban.com/v2/book/1220562').then(function(res) {
 self.data = JSON.parse(res.data)
});

此时打开浏览器控制台,可以看到server转发的请求结果,跨域成功。

利用jsonp与代理服务器方案解决跨域问题

与JSONP相比代理服务器的优点

相比JSONP,使用代理服务器转发不同源API的优点如下:

  • 资源获取是通过server端进行,可以根据需要转发的API选择使用GET以外的HTTP方法进行资源请求;
  • 请求的资源需要经过server端转发到浏览器端,server端可以对资源进行处理,因此可以避免一些直接的恶意代码,比JSONP更安全;
  • 浏览器页面正常使用Ajax请求数据,通过回调可以得知请求是否结束,不再需要使用计时器监测。

代理服务器的缺点

使用代理服务器的缺点是对不同源资源的转发请求,如果同时多个用户进行跨域请求,因为服务器内部需要进行额外的HTTP请求,那么服务器端的处理压力降会变大,从而导致阻塞等一系列性能问题,如需更好的方案,还是得使用Nginx等反向代理服务器进行端口代理处理。

总结

本文从实践的角度介绍了JSONP和代理服务器的处理跨域的做法,并对比了两种方案的优缺点,如果是一些安全要求性不高的场景,可以直接使用JSONP进行跨域请求,如果是需要额外的HTTP请求并且安全性要求较高,跨域请求还是从server端发起为佳,当然还有其他跨域方案,需要读者根据自身的能力和判断去舍取。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jQuery学习基础知识小结
Nov 25 Javascript
运算符&amp;&amp;的三个不同层次
Apr 07 Javascript
Jquery 数组操作大全个人总结
Nov 13 Javascript
JavaScript解八皇后问题的方法总结
Jun 12 Javascript
JavaScript严格模式详解
Jan 16 Javascript
jquery实现tab选项卡切换效果(悬停、下方横线动画位移)
May 05 jQuery
vue+node+webpack环境搭建教程
Nov 05 Javascript
vue按需加载组件webpack require.ensure的方法
Dec 13 Javascript
新手快速入门微信小程序组件库 iView Weapp
Jun 24 Javascript
JavaScript修改注册表实例代码
Jan 05 Javascript
详解Vue Cli浏览器兼容性实践
Jun 08 Javascript
vue使用svg文件补充-svg放大缩小操作(使用d3.js)
Sep 22 Javascript
基于js中document.cookie全面解析
Sep 14 #Javascript
基于Vue过渡状态实例讲解
Sep 14 #Javascript
JavaScript使用atan2来绘制箭头和曲线的实例
Sep 14 #Javascript
Vue2.0基于vue-cli+webpack同级组件之间的通信教程(推荐)
Sep 14 #Javascript
Vue2.0基于vue-cli+webpack父子组件通信(实例讲解)
Sep 14 #Javascript
对于js垃圾回收机制的理解
Sep 14 #Javascript
使用SVG基本操作API的实例讲解
Sep 14 #Javascript
You might like
老机欣赏|中国60年代精品收音机
2021/03/02 无线电
Zend studio for eclipse中使php可以调用mysql相关函数的设置方法
2008/10/13 PHP
PHP如何实现跨域
2016/05/30 PHP
PHP微信红包生成代码分享
2016/10/06 PHP
js修改table中Td的值(定义td的单击事件)
2013/01/10 Javascript
文本域中换行符的替换示例
2014/03/04 Javascript
AngularJS实现根据变量改变动态加载模板的方法
2016/11/04 Javascript
jquery文字填写自动高度的实现方法
2016/11/07 Javascript
JavaScript仿微信打飞机游戏
2020/07/05 Javascript
vue 打包后的文件部署到express服务器上的方法
2017/08/09 Javascript
vue+iview写个弹框的示例代码
2017/12/05 Javascript
解决vue build打包之后首页白屏的问题
2018/03/06 Javascript
antd组件Upload实现自己上传的实现示例
2018/12/18 Javascript
150行代码带你实现微信小程序中的数据侦听
2019/05/17 Javascript
JavaScript canvas基于数组生成柱状图代码实例
2020/03/06 Javascript
JavaScript动画实例之粒子文本的实现方法详解
2020/07/28 Javascript
[01:08:48]LGD vs OG 2018国际邀请赛淘汰赛BO3 第三场 8.25
2018/08/29 DOTA
Python实现读取TXT文件数据并存进内置数据库SQLite3的方法
2017/08/08 Python
PyQt5实现下载进度条效果
2018/04/19 Python
python 设置输出图像的像素大小方法
2019/07/04 Python
Python3enumrate和range对比及示例详解
2019/07/13 Python
使用python实现离散时间傅里叶变换的方法
2019/09/02 Python
Python模块_PyLibTiff读取tif文件的实例
2020/01/13 Python
keras自定义回调函数查看训练的loss和accuracy方式
2020/05/23 Python
给keras层命名,并提取中间层输出值,保存到文档的实例
2020/05/23 Python
Python爬虫教程知识点总结
2020/10/19 Python
html5利用canvas绘画二级树形结构图的示例
2017/09/27 HTML / CSS
日本最大的购物网站:日本乐天市场(Rakuten Ichiba)
2020/11/04 全球购物
Wiggle新西兰:自行车、跑步、游泳
2020/05/06 全球购物
大学生未来职业生涯规划书
2014/02/15 职场文书
物联网工程专业推荐信
2014/09/08 职场文书
大学生党员个人剖析材料
2014/10/08 职场文书
单位领导婚礼致辞
2015/07/28 职场文书
2016年大学生实习单位评语
2015/12/01 职场文书
职场新人知识:如何制定一份合理的工作计划?
2019/09/11 职场文书
python使用pycharm安装pyqt5以及相关配置
2022/04/22 Python