上篇文章中跨域的解决方案,我们简单的介绍了下跨域的原因以及使用postMessage解决跨域,这篇我们我们再来介绍几种前端跨域的解决方案


JSONP

JSON和JSONP虽然只有一个字母之差,但是它们却压根不是一回事:
JSON是一种数据交换格式,而JSONP是一种非官方的跨域数据交互协议。它们一个是描述信息的格式,一个是信息传递双方约定的方法。

JSONP的产生

上篇文章中说到过,ajax在调用跨域请求时,存在跨域无权限访问的情况,但是后来伟大的程序员又发现,Web页面上调用js文件不受跨域问题的影响。不仅如此,我们还发现凡是拥有src这个属性的标签都拥有跨域的能力,比如<script><img><iframe>)
于是我们判断,当前阶段如果想要跨域访问数据时,我们可以把数据外面包裹上一层js代码,供客户端调用和统一处理。
为了方便客户端使用数据,逐渐形成了一种非正式的传输协议JSONP,该协议的一个要点就是允许用户传递一个callback给服务端,然后服务端返回数据时会将这个callback参数作为函数名包裹JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

JSONP的使用

1
2
3
4
5
6
<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>

JSONP使用简单而且兼容性不错,但是只限于get请求。
看过JSONP库源码的童靴想必知道,JSONP常见的代码实现其实就是document.createElement(‘script’)生成一个script标签,然后插入到body中而已。所以JSONP的实现原理就是创建一个scrip标签,然后再把需要请求的api地址放到scr里,这个请求只能是get方法,不可能是post方法。

跨域资源共享(CORS)

在出现跨域资源共享之前,我们只能通过JSONP来解决跨域问题。上面我们也说到过,JSONP只能支持GET请求,在前端日益复杂的今天,仅仅只靠JSONP怎么可能,所以这个时候,CORS应运而生了。

CORS原理

支持CORS的浏览器,一旦发现ajax在做跨域请求时,会进行一些特殊的处理,对于已经实现CORS接口的服务端,接收请求并做出回应。
浏览器对跨域请求进行了一个简单的区分–简单请求和非简单请求

简单请求

若一个请求同时满足以下条件,则将其视为简单请求。

  • 使用的方法
    • HEAD
    • GET
    • POST
  • HTTP请求头信息为以下几种字段
    • Accept
    • Accept-Lanuage
    • Content-Lanuage
    • Last-Event-ID
    • Content-Type:
      application/x-www-form-urlencoded、 multipart/form-data、text/plain

当浏览器判断一个请求是简单请求后,会在Request Header中添加Origin(协议+域名+端口)字段,表示我们的请求源,CORS会将该字段作为跨域的标志。

CORS接收到此请求后,首先会判断origin是否在允许源(由服务端决定)范围之内,如果验证通过,服务端会在Response Header 添加 Access-Control-Allow-Origin、Access-Control-Allow-Credentials等字段。

浏览器收到Response后会判断自己是否在Access-Control-Allow-Origin允许源中,如果不存在,会抛出“同源检测异常”。

非简单请求

不满足以上两个条件的请求成为非简单请求。
对于非简单请求,浏览器首先发出类型为OPTIONS的预检请求,主请求和预检请求地址相同,CORS服务端对预检请求处理,并在Response Header中添加标识字段,客户端接收到预检请求的返回值进行一次预检请求的判断,如果判断通过则发起主请求。

这里我们可以看到,浏览器连续发送了两个请求,第一个就是预检请求,类型为OPTIONS,第二个请求才是我们发出的请求。
预检请求通过后,主请求开始发送。

所以,CORS的非简单请求只是在简单请求的基础上加了一个预检请求而已。

CORS的使用

CORS在前端的使用很简单,只需要在构建axios实例时加上一行代码

1
axios.defaults.withCredentials = true

要想完整的使用CORS解决跨域问题,还需要后端童靴的支持,可以说实现CORS的关键是后端,只要后端实现了CORS,就实现了跨域。

Vue2.0 proxyTable跨域

proxyTable是Vue-cli中的一种跨域方式,主要依赖的是http-proxy-middleware中间件。实现的原理也很简单,其本质是本地开了一个服务器dev-server(所以proxyTable只能在开发环境中使用),浏览器先将跨域请求发给自己的服务端,由自己的服务端再转发给要跨域的服务端,做一层代理来实现跨域。

proxyTable的使用

这里以Vue-cli3.0举例。在Vue-cli3.0中,webpack的配置主要写在vue.config.js中

1
2
3
4
5
6
7
8
9
10
11
devServer: {
proxy: {
'/cmdb': {
target: 'http://10.4.170.42:6060/cmdb',
changeOrigin: true,
pathRewrite: {
'^/cmdb': '/cmdb'
}
}
}
}

然后在我们请求数据或者axios封装时,只需要使用’/cmdb’即可。

1
axios.defaults.baseURL = '/cmdb'

nginx代理跨域

上面我们说到的proxyTable只能够在开发环境下使用,那么我们在生产环境上怎么办呢? nginx反向代理无疑是一种很好的解决方案。
那么nginx的反向代理为什么能够实现跨域呢?
首先,我们直接在浏览器上输入地址,是不会产生跨域问题,只有在某域名页面,由该页面发起的接口请求才可能跨域。nginx就相当于这个浏览器,它接收到外部对它的请求(注意:nginx只会接收别人对它的请求,但是并不会拦截请求),在类似浏览器的地址栏中去请求某个接口,最后将请求的内容返回回去。

nginx 配置

nginx解决跨域需要配置url请求规则,主要配置信息都在nginx.conf文件中

1
2
3
4
5
6
7
location /cmdb/ {
proxy_pass http://10.4.170.42:8003;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

nginx解决跨域问题在前端开发中也经常使用,因此需要重点掌握。


本文主要介绍了几种常见的前端跨域方法,当然跨域的方法远远不止这几种,感兴趣的同学可以参考前端常见跨域解决方案(全)

参考文章:
前端常见跨域解决方案(全)
九种跨域方式实现原理(完整版)