跨域是指违背了浏览器同源策略的限制
简单的说,我们通常使用的请求工具都是基于XMLHttpRequest对象构建的,它是严格遵守同源策略的,所以当需要跨域时,基于它的工具就无法工作了,我们需要一些额外的手段来解决这个问题 本文主要对已知的几种跨域方案进行实践和总结
一、jsonp
jsonp虽然不是官方的跨域方案,但它是最早出现的跨域方案,另外不管你是怎么使用jsonp跨域,服务端支持这种跨域是前提。
缺点
由于jsonp是使用script、img、iframe没有同源限制的标签,所以它只支持get请求
另外jsonp错误处理机制不完善
优点
jsonp的优点是它可以兼容低版本的浏览器
1.原理
jsonp是使用script、img、iframe没有同源限制的标签,向服务端发送请求,将返回的数据作为指定的回调函数的参数,script标签引入的函数是属于window全局的,所以你只需要在另一个script中指定回调函数,这样就可以获取到服务端数据了
这是个最基础的jsonp跨域例子
// test.html
控制台打印如下
此时在newwork中查看preview,返回的是一个函数,假如回调函数存在的话就执行它
上面只是说说它的原理而已,接下来进入正题,vue是如何使用的
2.封装
vue要使用jsonp,可以借助第三方工具包,推荐使用jsonp,假如你喜欢axios风格,用起来肯定得心应手。
a.下载
cnpm i jsonp --save
b.引入
新建一个文件,用来放我们封装的jsonp工具
// http.js
根据jsonp的文档,我们知道jsonp需要三个参数
另外还需要重新定义一个配置对象
var
然后返回一个Promise风格的jsonp函数,将url作为形参,options作为实参,另外加一个回调函数
// http.js
有的时候,我们还需要给服务端传参数,这时候就需要将参数对象拼接到URL中去,我们也可以将此封装下
// 将Parma参数拼接到url上
所以完整的代码是这样的
import
3.使用
在我们的接口文件中引入封装好的jsonp,就可以根据自己情况使用了
// api/getTotal.js
接着在要使用的组建中引入接口函数
// list.vue
二、CORS(跨域资源共享)
CORS仍然是使用XMLHttpRequest发送请求,只是多了些字段告诉服务器允许跨域,同样,它也需要服务端的支持
优点
可以访问多种请求方式
处理机制完善
符合http规范,对于复杂请求,多一次验证,安全性更好
缺点
不支持IE10以下浏览器
下面为了说明CORS原理,这里给出一个测试的服务端代码
const
和一个客户端代码,如果你感兴趣可以将它在纯html页面和vue项目各放一份
let
1.原理
跨域资源共享(CORS)是http官方推荐的跨域方案,它是一种机制,使用额外的HTTP头来告诉浏览器,让运行在一个origin (domain)上的Web应用被准许访问来自不同源服务器上的指定的资源,这是MDN对CORS的标准定义,简单说是在发送请求的时候添加一个名为origin的字段给服务器,服务在判断它是可以接受的范围,并且返回一个Access-Control-Allow-Origin字段
例如,在上面的例子中,不管是vue项目还是单独的html页面,都可以在控制台看到服务端返回的"hello world",
并且字段如下
// request header
当将服务端的字段值改变为http://localhost:8080,发现只有http://localhost:8080的vue项目可以正常跨域,html页面报错
Access to XMLHttpRequest at 'http://127.0.0.1:8090/' from origin 'null'
has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header
has a value 'http://localhost:8080' that is not equal to the supplied origin.
注意,http://localhost:8080和http://127.0.0.1:8080对服务端来说是不一样的
CORS根据是否使用默认支持的字段和方法将请求分为“简单请求”与“非简单请求”,浏览器会根据不同类型作出不同处理,它们的区别是简单请求不会调用验证机制,而非简单请求则会调用验证机制
对于(CORS默认)简单请求,它的请求方法不超过下面三种,头部信息不超过以下字段
(1) 请求方法:
HEAD
GET
POST
(2)HTTP的头信息:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:
application/x-www-form-urlencoded、 multipart/form-data、text/plain
注意 简单请求:
- 不能接收或者发送cookie
- 不能使用setRequestHeader()设置自定义头部
- 调用getAllResponseHeader()得到的事空字符串
假如我们的需要使用上面列出的以外的方法或者字段,就是属于非简单请求了,对于这种请求,浏览器会先调用Preflighted Request透明服务器的验证机制,使用OPTIONS方法和服务器验证,通过验证,浏览器才会正式请求服务器。
前面说了CORS默认(简单请求)是不支持调用setRequestHeader和getAllResponseHeader,在非简单请求里,可以使用OPTIONS方法突破这种默认限制
// 客户端
这时network中会多出一个验证的请求
假如服务端没有设置这个字段权限,那它就会在控制台报错
Request header field x-pingother is not allowed by Access-Control-Allow-Headers in preflight response.
假如你需要发送或者接受cookie,可以将实例的withCredentials属性设为true,同时也需要服务器配合,允许接收带cookie的请求,响应头部信息会返回下面的字段和值
// 客户端
假如服务端将Credentials设置为false,那么客户端会触发onerror程序,并且status为0,responseText为空字符串
res.setHeader("Access-Control-Allow-Credentials", false);
2.封装
在vue项目中使用axios,对它进行业务的封装
a.下载
cnpm i axios -s
b.引入
新建http/request.js,存放我们封装之后的axios文件
// http/request.js
在项目里通常需要对请求做统一处理,比如,给每个请求带上cookie,对所以的响应作出反应,根据不同的错误状态导入不同的页面,借用axios的拦截器,可以做到我们想要的效果
// 设置拦截器
3.使用
在我们的接口文件中引入封装好的axios,就可以使用了
// api/getTotal.js
接着在要使用的组建中引入接口函数
// list.vue
三、proxy(服务端代理)
在vue项目中,webpack选项devServer有一个proxy属性,通常都是使用这个属性将所有带指定字符串的请求,发送到target目标服务器。查看文档,发现devServer调用了http-proxy-middleware插件,将请求转发给目标服务器。
下面是官网给出的示例
var
为了说明原理,我自己创建了两个node服务,一个模拟客户端,一个模拟目标服务器
1.原理
在客户端,用http创建一个服务,并返回展示的页面
const
另外在页面中,可以自己定义或者引用已经写好的ajax,向客户端服务器发送请求,客户端在接受到这个这个请求时,转发给目标服务器,实现如下
<!DOCTYPE html>
目标服务器负责接收不同的url,并分别作出响应
// 目标服务器
这时浏览器控制台可以接收到目标服务器发送的json数据了
2.封装和使用
在设置基础路径之外,我们需要指明跨域的目标服务器地址
devServer
同样是使用axios,封装和使用同cros,只是baseURL不一样,这里只给出完整的代码
// 引入
四、更多方案
除了前面提到的跨域方案,你还可以使用nginx反向代理,它和proxy十分类似,都是访问中间层,由中间层去访问目标服务器
另外还有很多讨巧的方法进行跨域,比如你在页面中使用iframe标签,使用哈希location.hash的方式,进行跨域并且传递数据;也可以使用window.name或者H5的postMessage接口
这些跨域方案在实际使用中偏冷门,我也没有实践过,所以这里这是简单的提到
文章如果有错误的地方,欢迎指出,我会及时改正