一:什么是跨域

为了网站的安全考虑,浏览器对我们的请求做了同源策略的限制,即不同源之间的请求是不允许的。何为同源呢,就是我们url的协议,域名,端口一致,否则就被视为不同源。以我们最常见的 url格式 https://www.baidu.com 为例,https 是协议,www.baidu.com 是域名。需要说明的是,我们很少在 url 中看见端口,这是因为我们提前做了约定,http 协议的默认端口为 80,而 https 的默认端口为443,所以当你访问 https://www.baidu.com 的时候,实则访问的是 https://www.baidu.com:443。另外,我们平常说的域名,一般是把顶级域名和二级域名看做一个整体的。以www.baidu.com 为例做一下说明,我们说的域名应该是 baidu.com,也称二级域名,.com是顶级域名,前面的 www 是三级域名。值得注意的是,域名相同(一二级),子域名不同,也算跨越。一张图概括就是:

 

二、跨域带来的问题

同源策略的限制,本是为了我们网站的安全考虑,但有时候也带来了一些不便。对于开发者来说,可能遇到最多的问题就是ajax请求跨域了。除此之外,还有cookie,sessionStorage、localstorage、IndexDB 等缓存数据,以及 dom 和 js 对象无法操作的限制。

 

三、跨域解决方案

关于上面说到的跨域带来的诸多不便,我们只讲request的解决方案,其他几个问题用得不多,不做讨论,有兴趣的可以看一下这篇文章

特别需要说明的一点:对于我们的ajax请求被拦截来说,拦截的不是请求,而是响应,我们的请求其实是已经发出去了的,只是响应回来的内容,当浏览器识别是非同源时,给我们拦截了。

 

1、后端代码设置允许跨域,大致代码实现如下

 

2、启用代理服务,代理服务又分前端和后端两种情况。下面举个例子说一下代理服务的工作原理。

假如有三个人,你,小花,小花的弟弟,你跟小花的弟弟关系很好(其实好不好无所谓了,反正有钱就好办事)。小花呢,有一本日记,但她只给家里人看(这就相当于同源),你是看不到的(因为你不是她的家人,不同源)。你想看小花的日记,直接找小花,会被拒绝(小花是有原则的人,给钱人家也不愿意,同源策略的限制)。所以呢,你就找到小花的弟弟(代理),说你想看小花的日记,他弟弟再去找小花要日记本,拿到日记本后,再把日记本给你,你就看到了。

前端的代理叫正向代理,正向代理帮助客户端访问自己访问不到的资源,然后将结果返回给客户端。上面举的例子就属于正向代理。

后端的代理叫反向代理,反向代理帮其它的服务器拿到请求,然后选择一个合适的服务器,将请求转交给它。

反向代理最常用的就是Nginx代理了,比如说现在客户端的域名为client.com,服务器的域名为server.com,客户端向服务器发送 Ajax 请求,便是跨域了,那这个时候 Nginx就登场了,我们通过下面这个配置便能解决。

Nginx 相当于起了一个跳板机,这个跳板机的域名也是client.com,让客户端首先访问 client.com/api,这当然没有跨域,然后 Nginx 服务器作为反向代理,将请求转发给server.com,当响应返回时又将响应给到客户端,这就完成整个跨域请求的过程。

3、jsonp

虽然XMLHttpRequest对象遵循同源政策,但是script标签不一样,它可以通过 src 填上目标地址从而发出 GET 请求,实现跨域请求并拿到响应,这也就是 JSONP 得以实现的关键点。

它的实现原理,就是在script标签的src属性后面添加一些查询参数(我们需要传递过去的参数,0个或N个),和一个callback方法(这个方法必须是全局的),当我们通过 src 请求 script 的时候,服务器便可以拿到我们传递的参数,根据参数找到我们想要的结果数据,最后把数据作为参数传入 callback 方法中,返回 ‘callback(data) ‘ 的 js 代码,前端获取到响应后,就会执行一次callback(data)(方法必须是全局的原因,不是全局的方法没法执行),如此整个过程便根据请求参数返回了我们想要的结果。代码实现如下:

首先,封装一个jsonp方法

然后,在服务端对请求做相应的处理,以 express 为例:

最后,前端这样简单地调用一下就好了:

 

注:由于jsonp是通过url发请求传参,所以只支持get请求。