预备
在网络中,受到浏览器内部机制的限制,本域内,也就是自身服务器下的服务的js是不能操作其他域下的页面对象,或者去请求域下的数据,这个时候就会产生跨域问题。
有些人会奇怪,为什么cdn或者图片加载的时候,不是本域的东西,却可以请求的来,这个请大家要仔细理解一下,跨域是指js不能操作和请求其他域的,浏览器本身请求和
标签src内部的请求是不受限制的。
下面我们来说如何解决问题。
解决方案:
1.jsonp
最初用来解决跨域问题的方式,叫做JSONP,它的基本原理是:跨域的“资源嵌入”是被浏览器允许的。所以,可以通过一个script标签来嵌入一段来自其他服务器的脚本。由于这个脚本完全运行在当前域,无法访问第三方服务器的cookie等敏感信息,所以是安全的。
JSONP的缺点是它只能支持GET操作,没法支持POST等操作,但是由于兼容性好等优点,仍然有很多网站采用JSONP的方式公开自己的API供第三方调用。
在Angular中,$http内置了对JSONP的支持,它的调用接口也和其他方法没什么区别,使用起来非常简单。但是也仅仅支持get请求
2.gulp-connect与gulp-connect-proxy
当前端开发集成使用gulp的时候,可以使用这个方式进行跨域,他的原理类似于反向代理,但却有不同,内部实际做的操作,实际只是反向代理转发了请求,这样就造成了一个问题,就是也仅仅是支持get请求,无法支持post操作
3.CORS
这是W3C提供的另一种跨域方式。作为一项标准的跨域规范,CORS本应该是最值得采用的。 问题在于,老式浏览器支持CORS,而我们显然还没到可以无视老式浏览器的时候。
CORS的原理是基于服务方授权的模式,也就是说提供服务的程序要主动通过CORS回应头来声明自己信任哪些源(协议+域名+端口)。 由于得到了服务方的授权,浏览器就可以放行来自这些域的请求了。
CORS的原理是基于服务方授权的模式,也就是说提供服务的程序要主动通过CORS回应头来声明自己信任哪些源(协议+域名+端口)。 由于得到了服务方的授权,浏览器就可以放行来自这些域的请求了。
并且有个弊端问题是,该方式必须要服务器端开发和前端开发配合,也就是服务端的所有接口需要设置请求头,来放行,这样造成的问题是,如果项目在开发阶段或者线上项目与开发项目不是同一个版本,还好,不然,一旦服务端采用该设置,并且在设置上有问题,有很大的安全隐患,但是如果能够控制好设置,通过token控制好,也是可以得。
4.反向代理
要想解决跨域问题,最简单彻底的方法当然是把他们拉到一个域下,而这就是该“反向代理”发挥作用的时候了。
所谓反向代理,就是在自己的域名下架设一个Web服务器,这个服务器会把请求转发给第三方服务器,然后把结果返回给客户端。
这时候,在客户端看来,自己就是在和这台反向代理服务器打交道,而不知道第三方服务器的存在。
所以,如果有一个Web服务程序,它同时提供了反向代理功能和静态文件服务功能,静态文件服务负责渲染前端页面,反向代理则提供对第三方服务器的透明访问。那么前端和后端就变成了同源的,不再受同源策略的约束。
那么,有这样的Web服务程序吗?有,而且不止一个。事实上,几乎所有的主流Web服务器都提供了反向代理功能。这里仅以Nginx为例来示范反向代理的配置方式,其他Web服务器请搜相应的文档自行研究。
所谓反向代理,就是在自己的域名下架设一个Web服务器,这个服务器会把请求转发给第三方服务器,然后把结果返回给客户端。
这时候,在客户端看来,自己就是在和这台反向代理服务器打交道,而不知道第三方服务器的存在。
所以,如果有一个Web服务程序,它同时提供了反向代理功能和静态文件服务功能,静态文件服务负责渲染前端页面,反向代理则提供对第三方服务器的透明访问。那么前端和后端就变成了同源的,不再受同源策略的约束。
那么,有这样的Web服务程序吗?有,而且不止一个。事实上,几乎所有的主流Web服务器都提供了反向代理功能。这里仅以Nginx为例来示范反向代理的配置方式,其他Web服务器请搜相应的文档自行研究。
server { listen 80; server_name your.domain.name; location / { proxy_pass http://localhost:5000/; # 把根路径下的请求转发给前端工具链(如gulp)打开的开发服务器,如果是产品环境,则使用root等指令配置为静态文件服务器 } location /api/ { proxy_pass http://localhost:8080/service/; # 吧 /api 路径下的请求转发给真正的后端服务器 proxy_set_header Host $http_host; # 把host头传过去,后端服务程序将收到your.domain.name,否则收到的是localhost:8080 proxy_cookie_path /api /service; # 把cookie中的path部分从/api替换成/service proxy_cookie_domain localhost:8080 your.domain.name; # 把cookie的path部分从localhost:8080替换成your.domain.name } }这个解决方案,有时候还是会遇到一些问题,比如你们app的前端使用的是Angular开发,而pc端是使用的jquery开发,且在开发过程中强制规定contentType,这个时候,jquery的post默认的contentType与Angular默认的是不一样的,所以为了统一,还需要做一些修改,这里服务端,以java,框架为springmvc为例,
app.config(function($httpProvider) { $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded'; $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; // Override $http service's default transformRequest $httpProvider.defaults.transformRequest = [function(data) { /** * The workhorse; converts an object to x-www-form-urlencoded serialization. * @param {Object} obj * @return {String} */ var param = function(obj) { var query = ''; var name,value,fullSubName,subName,subValue,innerObj,i; for (name in obj) { value = obj[name]; if (value instanceof Array) { for (i = 0; i < value.length; ++i) { subValue = value[i]; fullSubName = name + '[' + i + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if (value instanceof Object) { for (subName in value) { subValue = value[subName]; fullSubName = name + '[' + subName + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if (value !== undefined && value !== null) { query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&'; } } return query.length ? query.substr(0,query.length - 1) : query; }; return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data; }]; });
在angular的配置中加上这些,这样他的post请求就和jquery默认的一样了,都默认为 Content-Type: x-www-form-urlencodedand