问题引出
我在 http://localhost:8080 放了一台后端服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.pushihao.controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/hello") public class HelloController {
@RequestMapping("/h1") public String h1() { return "Hello, world!"; }
}
|
通过调用 http://localhost:8080/hello/h1 可以获得一个字符串

我又在 http://localhost:5173 放了一台前端前端服务器,通过 xhr 请求调用后端的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup> import axios from 'axios' axios.defaults.timeout = 5000
axios.get("http://localhost:8080/hello/h1").then( response => { console.log(response) }, error => { console.log(error) } ) </script>
<template></template> <style scoped></style>
|
结果这时,浏览器报错

以上便是 xhr 请求跨域问题的一个经典例子
为什么会有请求跨域问题
什么导致的跨域请求问题?简单来说,浏览器的 同源策略 导致了跨域请求问题。
为什么要制定同源策略?官方是这样解释的:同源策略是一个重要的安全策略,它用于限制一个 origin 的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
什么样的 url 才算是同源呢?当两个 url 的**协议(protocol)、域名(host)、端口(port)**三者都相同时才算是同源。只要其中有一个不同,那么 url 就不算同源,进行的 http 请求就称为跨域请求。
跨域请求有什么限制?无法读取非同源网页的 cookie、localstorage 等、无法接触非同源网页的 DOM 和 js 对象、无法向非同源地址发送 Ajax 请求。
解决跨域请求问题
我们知道,同源策略是一个重要的安全策略,它可以减少我们被攻击的风险。但是对于正常的请求,我们希望可以接触跨域带来的一些限制。解决跨域请求问题的方法有很多,以下给出一些常用的解决方案。
后端使用 nginx 解决跨域问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| server { listen 3500; server_name localhost; location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') { return 204; } proxy_pass http://localhost:8080; } }
|
启动 Nginx 服务后,3500 端口被代理到 8080 端口,并且开启了允许所有域进行跨域请求
此时,修改前端请求的 url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup> import axios from 'axios' axios.defaults.timeout = 5000
axios.get("http://localhost:3500/hello/h1").then( response => { console.log(response) }, error => { console.log(error) } ) </script>
<template></template> <style scoped></style>
|
启动服务后,请求响应正常

使用前端框架提供的代理服务器
注意:这种方法仅在调试阶段有效,部署到服务器上之后就没用了
使用原理:
只有前端在发送 Ajax 请求时会触发跨域请求,而两台后端服务器进行通信时是不会存在跨域的问题的,因此我们可以在前端与后端之间设置一台代理服务器,这台代理服务器与我们的前端项目保持同源,即:localhost:5173
,我们的前端项目只负责请求这台代理服务器,由代理服务器向后端接口请求数据。

- 对于 webpack + vue 的前端项目,可以使用 devServer 配置代理解决跨域问题
vue.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true,
devServer: { proxy: { '/api': { target: 'http://localhost:8080', pathRewrite: { '^/api': '' }, ws: true,
changeOrigin: true },
'/teacher': { target: 'http://localhost:8082', pathRewrite: { '^/teacher': '' }, ws: true, changeOrigin: true } }, } })
|
AxiosTest.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script setup> import axios from 'axios' axios.defaults.timeout = 5000
// 由于我们制定了重写规则,所以此处的 /api 会在发送请求时被略去 axios.get("/api/hello/h1").then( response => { console.log(response) }, error => { console.log(error) } ) </script>
<template></template> <style scoped></style>
|
- 对于 vite + vue 的前端项目
vite.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue'
export default defineConfig({ plugins: [vue()], server: { proxy: { "/api": { target: "http://localhost:8080", changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ""), }, }, }, })
|
AxiosTest.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup> import axios from 'axios' axios.defaults.timeout = 5000
axios.get("/api/hello/h1").then( response => { console.log(response) }, error => { console.log(error) } ) </script>
<template></template> <style scoped></style>
|
至此,问题解决!