vue跨域配置

lxf2023-03-20 19:36:01

Vue跨域配置详解

前言

跨域这个词,对前端程序员来说,可谓是屡见不鲜。正好最近在做项目时,又遇到了跨域问题,无奈只能继续去网上查询资料来查看vue如何进行配置。为了方便,最终决定自力更生,自己总结一番,省的之后遇到跨域问题去网上各种查询,浪费时间。

首先对跨域做一个简单的回顾吧。

1、什么是跨域?

当一个请求url的协议域名端口三者之间任意一个与当前页面url不同即为跨域。

比如http://www.test.com/这个url, http是协议,www.test.com是域名,80是端口。(默认端口:http端口80、https端口443、tomcat端口8080

当前页面url请求页面url是否跨域原因
http://www.test.com/http://www.test.com/index.html协议、域名、端口都相同,只是请求内容不同
http://www.test.com/https://www.test.com/协议不同,前者是hhtp,后者是https
http://www.test.com/http://www.test2.com/域名不同,前者是test,后者是test2
http://www.test.com:8080/http://www.test.com:8002/端口不同,前者是8080,后者是8002

2、为什么会出现跨域?

我们可以先假设,如果浏览器没有安全策略的话。Web世界会是变成什么样子?

这里假设有A网站,如果没有相关的安全策略,Web世界就是开放的。那么任何资源都可以接入A网站,如视频、音频、图片、可执行脚本等。但这样会造成无序或者混沌的局面,出现很多不可控的问题。

比如A网站请求到了一个恶意网站(B)的恶意脚本,就可以修改A网站的DOM、CSSOM结构,还可以插入一段JavaScript脚本,甚至可以获取A网站的用户名和密码及Cookie信息。

因此,我们就需要一种安全策略来保障我们的隐私和数据的安全,这就引出了页面中最基础、最核心的安全策略:同源策略Same-origin policy),它是浏览器最核心也是最基本的安全功能。

什么是同源策略呢?

协议、域名和端口都相同,那么这两个url就是同源的。如果两个url协议、域名、端口任意一个不相同,则这两个url就是不同源的,他们的请求就算是跨域

3、vue中配置跨域

1、首先用express模拟开一个服务

// 创建应用对象
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由规则
app.get('/data',(request,response) => {
    let obj = {
        name:'test',
        age:18
    }
    response.send(obj);
});
// 监听端口启动服务
app.listen(8002,() => {
    console.log("服务已启动,82端口监听中...");
})

服务已经开好,准备前端发请求了。

2、创建一个vue项目,前端代码如下:

# 写一个特简单的页面,只放一个按钮,用来发请求。
<template>
    <div>
        <el-button @click="getData">获取内容</el-button>
    </div>
</template>
<script>
import axios from "axios"
export default {
    nema:'Current',
    data(){
        return{
            data : null
        }
    },
    methods:{
        getData(){
            axios.get("http://localhost:82/data").then(res=>{
                console.log(`请求内容为${res.data}`);
            },
            error=>{
                console.log(`请求错误`);
            })
        }   
    }
};
</script>
<style scoped>
</style>

点击获取内容按钮,不出所料,报了跨域问题。如下图所示:

vue跨域配置

Access to XMLHttpRequest at 'http://localhost:8002/data' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Main.vue?48a8:21 请求错误

大概意思可以这样描述:我的vue服务在localhost的8080端口,express的服务在8002端口。因为端口不同,所以同源策略会生效。因此8080端口请求不到8002端口的内容。

关于跨域,需要明白的一点是。这个请求后端是可以接收到的,并不是说跨域了,请求发都发不出去。并且服务端可以将数据返回给浏览器,浏览器也可以正常接收数据,但是因为同源策略,浏览器没有进一步传递数据,浏览器将数据拦截了。

3、解决思想

配置代理服务器

正常的请求流程如下,前端直接向后端发起请求。因为端口不同,所以会触发同源策略,报跨域错误,浏览器不显示数据。

vue跨域配置

配置代理服务器之后,流程就变为:前端不再向后端发起数据请求,而是向代理服务器发请求,代理服务器收到请求之后,它会向后端发起请求,后端返回数据给代理服务器。因为前端和代理服务器之间是同源,因此前端可以直接获取到代理服务器的内容。这样前端就可以获取到后端返回的数据了,不会再报跨域问题。说白了就是代理服务器欺骗了浏览器,让浏览器以为没有跨域。

这里可能会有人问,粉色的代理服务器端口(8080)和后端(82)也是不一样的啊,他们不会报跨域错误吗?答案是不会的,跨域只是浏览器的一种安全策略,代理服务器它是一个服务器,服务器与服务器之间通信没有跨域这个问题的,也就是说跨域这个问题只存在于浏览器。

关于两个8080端口问题,浏览器是访问8080端口,而不是占用端口,8080上只有一个代理服务器在监听。

vue跨域配置

4、配置跨域,解决问题。

4.1、配置代理一

在vue.config.js中配置devServer,详细配置如下:

module.exports = {
  pages:{
    index:{
      entry:"src/main.js"
    }
  },
  // 关闭语法检查
  lintOnSave:false,
  // 配置跨域
  devServer:{
    proxy: 'http://localhost:8002'
  }
}

组件代码如下,注意,我们请求的地址不再是http://localhost:8002/data,而是http://localhost:8080/data。因为我们请求的是代理服务器,我们是通过代理服务器去后端请求数据,因此这里注意,请求地址中端口一定要改成8080

<template>
  <div class="hello">
    <el-button @click="getData">获取内容</el-button>
  </div>
</template>
<script>
import axios from "axios"
export default {
    nema:'Current',
    data(){
        return{
            data : null
        }
    },
    methods:{
        getData(){
            axios.get("http://localhost:8080/data").then(res=>{
                console.log("请求内容为",res.data);
            },
            err=>{
                console.log("请求错误",);
            })
        }   
    }
};
</script>

运行实例

可以看到,请求成功,不再报跨域错误,成功取到{ name:'test', age:18 }

vue跨域配置

注意,代理服务器收到的任何请求并不是都会转发出去的,如果服务器自身有相关资源,则不会转发请求,而是直接返回相关资源。如何理解呢?就是说,如果这个代理服务器有一个test数据(不管它是什么文件类型的),你正好请求的也是test数据,它就不会将你的请求转发出去,而是直接将其自身有的test数据给你返回去。来验证一下。

首先将组件中请求路径改为"http://localhost:8080/test"

<template>
  <div class="hello">
    <el-button @click="getData">获取内容</el-button>
  </div>
</template>
<script>
import axios from "axios"
export default {
    nema:'Current',
    data(){
        return{
            data : null
        }
    },
    methods:{
        getData(){
            axios.get("http://localhost:8080/test").then(res=>{
                console.log("请求内容为",res.data);
            },
            err=>{
                console.log("请求错误",);
            })
        }   
    }
};
</script>

之后在public目录下添加一个test文件,并写入如下内容{ "name":"test2", "msg":"本地数据" }

vue跨域配置

运行实例:

vue跨域配置

这是需要注意的一个点

以上方式配置代理有如下两个缺点:

  • 只能配置一个代理,不能配置多个代理。
  • 无法灵活控制请求是否走代理。

4.2 配置代理二

为了解决方式一配置方式存在的问题,我们需要进一步了解新的代理配置方式。参考Vue官方代理配置方案。

因此,我们的配置方式可以改为:

module.exports = {
  pages:{
    index:{
      entry:"src/main.js"
    }
  },
  // 关闭语法检查
  lintOnSave:false,
  devServer:{
    proxy: {
    //匹配前缀,也就是说只有当请求的前缀是/api,才让代理去转发该请求,如果不是/api,则代理不进行转发。
      "/api":{
        target:"http://localhost:8002",
        // 必须有pathRewrite属性,否则代理转发的请求路径中会包含/api,会出现404错误。必须将/api再转化成空字符串
        pathRewrite:{"^/api":""},
        // 支持websocket
        ws: true,
        // 控制请求头中的host值
        changeOrigin:false
      }
    }
  }
}

组件代码如下:我们在请求路径中添加了请求前缀/api。当代理服务器识别到有/api这个请求前缀,虽然本地也有test文件,但是代理服务器依旧会将这个请求发出。

<template>
  <div class="hello">
    <el-button @click="getData">获取内容</el-button>
  </div>
</template>
<script>
import axios from "axios"
export default {
    nema:'Current',
    data(){
        return{
            data : null
        }
    },
    methods:{
        getData(){
            axios.get("http://localhost:8080/api/test").then(res=>{
                console.log("请求内容为",res.data);
            },
            err=>{
                console.log("请求错误",);
            })
        }   
    }
};
</script>
// 创建应用对象
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由规则
app.get('/data',(request,response) => {
    console.log("访问data请求");
    let obj = {
        name:'test',
        age:18
    }
    response.send(obj);
});

app.get('/test',(request,response) => {
    console.log("访问test请求");
    let obj = {
        name:'test',
        age:20
    }
    response.send(obj);
});
// 监听端口启动服务
app.listen(8002,() => {
    console.log("服务已启动,8002端口监听中...");
})

运行实例如下:获得到服务端的数据{ name:'test', age:20 },而不是本地的{ "name":"test2", "msg":"本地数据" }

vue跨域配置

解决配置多个代理这一问题,只需在proxy属性中再添加一个配置即可。

module.exports = {
  pages:{
    index:{
      entry:"src/main.js"
    }
  },
  // 关闭语法检查
  lintOnSave:false,
  devServer:{
    proxy: {
      //匹配前缀
      "/api":{
        target:"http://localhost:8002",
        pathRewrite:{"^/api":""},
        // 支持websocket
        ws:true,
        changeOrigin:false
      },
      // 在8002的基础上,再增加一个8003端口的代理
      "/newapi":{
        target:"http://localhost:8003",
        pathRewrite:{"^/newapi":""},
        // 支持websocket
        ws:true,
        changeOrigin:false
      }
    }
  }
}

这边开80028003两个服务器

//8002服务器
// 创建应用对象
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由规则
app.get('/test',(request,response) => {
    console.log("访问test请求");
    let obj = {
        name:'test',
        msg:"8002端口信息"
    }
    response.send(obj);
});
// 监听端口启动服务
app.listen(8002,() => {
    console.log("服务已启动,8002端口监听中...");
})
// 8003服务器
// 创建应用对象
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由规则
app.get('/test',(request,response) => {
    console.log("访问test请求");
    let obj = {
        name:'test',
        msg:"8003端口信息"
    }
    response.send(obj);
});
// 监听端口启动服务
app.listen(8003,() => {
    console.log("服务已启动,8003端口监听中...");
})

组件代码,新增了一个端口号为8003的数据请求。

template>
  <div class="hello">
    <el-button @click="getData">获取内容</el-button>
    <el-button @click="getData2">获取内容2</el-button>
  </div>
</template>
<script>
import axios from "axios"
export default {
    nema:'Current',
    data(){
        return{
            data : null
        }
    },
    methods:{
        getData(){
            axios.get("http://localhost:8080/api/test").then(res=>{
                console.log("请求内容为",res.data);
            },
            err=>{
                console.log("请求错误",);
            })
        },
        getData2(){
            axios.get("http://localhost:8080/newapi/test").then(res=>{
                console.log("请求内容为",res.data);
            },
            err=>{
                console.log("请求错误",);
            })
        }    
    }
};
</script>

运行实例,成功获取80028003端口数据,成功配置多个代理。

vue跨域配置

5、附-服务端跨域配置

当然,我们也可以直接在后端服务器做相应配置,这里就以express为例:

// 配置关键代码Access-Control-Allow-Origin与Access-Control-Allow-Methods
app.use((req,res,next)=>{
    //实验验证,只需要设置这一个就可以进行get请求
    res.header('Access-Control-Allow-Origin', 'http://localhost:8080')//配置8080端口跨域
    //res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, OPTIONS') // 允许的 http 请求的方法
    // res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With')
    next()
})
  1. Access-Control-Allow-Origin:设定请求源,就告诉了浏览器。如果请求我的资源的页面是我这个响应头里记录了的"源",则不要拦截此响应,允许数据通行。如上配置,由于服务端设置了res.header('Access-Control-Allow-Origin', 'http://localhost:8080'),如果请求数据的源是 http://localhost:8080则可以允许访问返回的数据。这样浏览器就不会抛出错误提示,而是正确的将数据交给你。
  2. Access-Control-Allow-Methods:允许请求的方法。
  3. Access-Control-Allow-Headers:用于preflight request(预检请求)中,列出了将会在正式请求的 Access-Control-Expose-Headers 字段中出现的首部信息。

4、总结

以上就是关于跨域及Vue配置跨域的基本内容。首先介绍了什么是跨域?为什么会出现跨域?接着重点介绍了Vue中如何配置跨域。最后还提供了服务端(express)的跨域配置。