利用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 学习笔记 选择器之五
Jul 23 Javascript
再谈javascript面向对象编程
Mar 18 Javascript
javascript如何实现360度全景照片问题汇总
Apr 04 Javascript
跨域资源共享 CORS 详解
Apr 26 Javascript
一个字符串中出现次数最多的字符 统计这个次数【实现代码】
Apr 29 Javascript
Javascript点击按钮随机改变数字与其颜色
Sep 01 Javascript
浅谈EasyUI常用控件的禁用方法
Nov 09 Javascript
JavaScript中localStorage对象存储方式实例分析
Jan 12 Javascript
node使用UEditor富文本编辑器的方法实例
Jul 11 Javascript
探究react-native 源码的图片缓存问题
Aug 24 Javascript
Vue+axios实现统一接口管理的方法
Jul 23 Javascript
详解小程序如何避免多次点击,重复触发事件
Apr 08 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
完美解决PHP中的Cannot modify header information 问题
2013/08/12 PHP
详解Yii2.0使用AR联表查询实例
2017/06/16 PHP
Windows上php5.6操作mongodb数据库示例【配置、连接、获取实例】
2019/02/13 PHP
Laravel向公共模板赋值方法总结
2019/06/25 PHP
在IE和VB中支持png图片透明效果的实现方法(vb源码打包)
2011/04/01 Javascript
基于jQuery的前端数据通用验证库
2011/08/08 Javascript
查看大图功能代码jquery版
2013/11/05 Javascript
JS拖拽组件学习使用
2016/01/19 Javascript
jQuery解决浏览器兼容性问题案例分析
2016/04/15 Javascript
js获取Html元素的实际宽度高度的方法
2016/05/19 Javascript
jQuery动态生成Bootstrap表格
2016/11/01 Javascript
jQuery UI Draggable + Sortable 结合使用(实例讲解)
2017/09/07 jQuery
解决easyui日期时间框ie的兼容的问题
2018/03/01 Javascript
vue项目中使用ueditor的实例讲解
2018/03/05 Javascript
JS实现的Object数组去重功能示例【数组成员为Object对象】
2019/02/01 Javascript
使用 node.js 模仿 Apache 小部分功能
2019/07/07 Javascript
在vue中动态添加class类进行显示隐藏实例
2019/11/09 Javascript
vue实现购物车的小练习
2020/12/21 Vue.js
python smtplib模块自动收发邮件功能(一)
2018/05/22 Python
python实现感知机线性分类模型示例代码
2019/06/02 Python
用Python爬取QQ音乐评论并制成词云图的实例
2019/08/24 Python
python中matplotlib条件背景颜色的实现
2019/09/02 Python
Django {{ MEDIA_URL }}无法显示图片的解决方式
2020/04/07 Python
Django数据统计功能count()的使用
2020/11/30 Python
Pandas中两个dataframe的交集和差集的示例代码
2020/12/13 Python
python Protobuf定义消息类型知识点讲解
2021/03/02 Python
幼儿园中秋节活动方案
2014/02/06 职场文书
消防先进事迹材料
2014/02/10 职场文书
运动会入场式解说词
2014/02/18 职场文书
高三毕业寄语
2014/04/10 职场文书
竞选学习委员演讲稿
2014/04/28 职场文书
单位租房协议范本
2014/12/03 职场文书
幼儿园教研工作总结2015
2015/05/12 职场文书
女方离婚起诉书
2015/05/18 职场文书
Python入门之基础语法详解
2021/05/11 Python
动画《朋友游戏》公开佐藤友生绘制的开播纪念绘
2022/04/06 日漫