本文将从实践角度介绍如何使用jsonp和代理服务器方案解决跨域问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
浏览器为了保护用户安全,引入了同源策略,即一个服务器页面无法访问另一个协议、域名、端口不同的服务器数据。当页面需要跨服务器访问另一个服务器的数据时,即产生跨域行为。以豆瓣的公开API(https://api.douban.com/v2/book/1220562)为例,当前我的服务器处于:5000下,豆瓣的服务器很显然跟我的服务器不同源,服务器中的一个页面通过AJAX请求该接口时,浏览器会发出如下警告,并且页面获取数据失败:
在实际开发中,如果遇到这样的跨域问题,可以通过以下办法获得跨域的数据:
异源服务器的响应头部设置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的缺点主要源自他的script引用资源方式,JSONP的缺点如下:
JSONP是通过script标签获取资源的,也就是说JSONP注定只能用GET的方式访问资源,GET以外的请求无法做到;