优雅的解决跨域问题

想要解决跨域问题,我们首先要清楚什么是跨域

每当我们请求后端识别后打开控制台出现了No ‘Access-Control-Allow-Origin’ header is present on the requesting resource

跨域是只在网页中,当一个网页试图去访问不同域名下的资源时(比如发送ajax请求,使用iframe加载其他网页等)会受到同源策略的限制,从而导致无法正常获取数据的情况

同源策略是指:协议、域名、端口都相同的两个网址

为什么浏览器需要跨域

浏览器之所以限制跨域,是因为处于安全顾虑。

如果不限制跨域访问,那么攻击者就可以伪造请求,访问用户在其他网站上的敏感信息或只执行恶意操作

第一种解决方法:

JSONP是一种利用script标签的GET请求实现跨域的技术

简答的案例:

假设我们有一个网站A,想要获取另一个网站B的数据,但是B和A不在同一个域,因此不能直接访问。

B网站的服务器端,提供了一个名为getData的接口,可以返回一些数据。为了让A网站可以获取这些数据,B网站在返回数据时将其封装在一个名为callback的回调函数中,并将回调函数的参数作为需要的数据传给A网的。A网站使用JSONP来获取B网站的数据,并指定一个名为handleResponse的回调函数。

B网站后端返回数据的代码如下:

var data = {
  "name": "Alice",
  "age": 18
};
var callback = req.query.callback; // 获取回调函数的名称
res.send(callback + '(' + JSON.stringify(data) + ')'); // 将数据封装在回调函数中返回
A网站前端请求代码如下:
function handleResponse(data) {
  console.log(data);
}
var script = document.createElement('script');
script.src = 'http://www.domain2.com/getData?callback=handleResponse';
document.head.appendChild(script);

JSONP的优点是可以实现跨域请求,而且兼容性好,几乎所有浏览器都支持。但是它也有一些弊端:

1.只支持GET请求,因为jsonp是通过动态创建script标签来实现跨预期扭曲的,而script标签只支持GET请求

2.数据格式限制,JSONP只能返回JSON格式的数据,无法返回XML格式的数据

代理的方式

就是自己搭一个本地服务器,通过访问服务器与服务器的关系,跨过浏览器这的同源策略

Webpack devserver

如果是大型配置,并且配置了webpack的话

1.如果在Webpack的配置文件中添加devServer.proxy属性来实现跨域代理

// webpack.config.js
module.exports = {
  // ...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://www.domain2.com', //需要跨域的url
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

注意,如果在代理配置中设置了changeOrigin为true,则在代理请求时会自动将请求头中的Host字段设置为代理目标的域名,这样就可以绕过浏览器的同源策略,实现跨域请求。

axios.get('/api/data')
  .then(response => {
    console.log(response.data)
  })
  .catch(error => {
    console.log(error)
  })

CORS

我们不需要为每个WebAPI做额外的处理,而是需要在后端程序启动时,增加一些处理工作。主流的后端服务都有处理CORS的类库,这里就不再做展开介绍了。

这个方案的核心原理,是在发起正式的请求前,先发送一个OPTIONS谓词的HTTP请求,询问发起请求的页面是否有调用该域服务的权限;如果后端说OK,浏览器就继续请求,否则提示错误。

使用这种方案的开发工作量小,如果直接使用成熟类库的话,开发和测试的工作量甚至可以忽略不计。不过,因为每个跨域的请求都会触发一次往外的OPTIONS请求,对服务器会造成额外的开销和压力。