利用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 相关文章推荐
prototype 1.5相关知识及他人笔记
Dec 16 Javascript
javascript中encodeURI和decodeURI方法使用介绍
May 06 Javascript
juery框架写的弹窗效果适合新手
Nov 27 Javascript
jquery高级编程的最佳实践详解
Mar 23 Javascript
通过JQuery将DIV的滚动条滚动到指定的位置方便自动定位
May 05 Javascript
使用jQuery判断浏览器滚动条位置的方法
May 30 Javascript
BootStrap Fileinput初始化时的一些参数
Dec 30 Javascript
jQuery插件ajaxFileUpload使用详解
Jan 10 Javascript
使用 Node.js 模拟滑动拼图验证码操作的示例代码
Nov 02 Javascript
JavaScript实现的简单加密解密操作示例
Jun 01 Javascript
微信小程序中使用ECharts 异步加载数据实现图表功能
Jul 13 Javascript
简单了解微信小程序的目录结构
Jul 01 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 Smarty date_format [格式化时间日期]
2010/03/15 PHP
php中定义网站根目录的常用方法
2010/08/08 PHP
解决PHP程序运行时:Fatal error: Maximum execution time of 30 seconds exceeded in的错误提示
2016/11/25 PHP
Yii输入正确验证码却验证失败的解决方法
2017/06/06 PHP
php生成短网址/短链接原理和用法实例分析
2020/05/29 PHP
JavaScript入门教程(1) 什么是JS
2009/01/31 Javascript
在模板页面的js使用办法
2010/04/01 Javascript
div拖拽插件——JQ.MoveBox.js(自制JQ插件)
2013/05/17 Javascript
JavaScript将相对地址转换为绝对地址示例代码
2013/07/19 Javascript
gridview生成时如何去掉style属性中的border-collapse
2014/09/30 Javascript
JavaScript通过字典进行字符串翻译转换的方法
2015/03/19 Javascript
基于Bootstrap实现下拉菜单项和表单导航条(两个菜单项,一个下拉菜单和登录表单导航条)
2016/07/22 Javascript
使用Bootstrap + Vue.js实现添加删除数据示例
2017/02/27 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
2019/06/18 Javascript
微信小程序云函数使用mysql数据库过程详解
2019/08/07 Javascript
vue实现计步器功能
2019/11/01 Javascript
electron踩坑之dialog中的callback解决
2020/10/06 Javascript
vue在图片上传的时候压缩图片
2020/11/18 Vue.js
[02:41]DOTA2亚洲邀请赛小组赛第三日 赛事回顾
2015/02/01 DOTA
python操作数据库之sqlite3打开数据库、删除、修改示例
2014/03/13 Python
实例讲解python函数式编程
2014/06/09 Python
python的迭代器与生成器实例详解
2014/07/16 Python
python实现Decorator模式实例代码
2018/02/09 Python
Tensorflow的可视化工具Tensorboard的初步使用详解
2018/02/11 Python
用pycharm开发django项目示例代码
2019/06/13 Python
树莓派实现移动拍照
2019/06/22 Python
python中struct模块之字节型数据的处理方法
2019/08/27 Python
tornado+celery的简单使用详解
2019/12/21 Python
Python+Kepler.gl实现时间轮播地图过程解析
2020/07/20 Python
SEPHORA新西兰官方网站:购买化妆品和护肤品
2016/12/02 全球购物
人力资源行政经理自我评价
2013/10/23 职场文书
甜品店的创业计划书范文
2014/01/02 职场文书
会计学生自我鉴定
2014/02/06 职场文书
2014年3.15团委活动总结
2014/03/16 职场文书
2015元旦晚会主持词(开场白+结束语)
2014/12/14 职场文书
创业计划书之香辣虾火锅
2019/09/23 职场文书