前言
作为一名普通的程序开发者,日常开发中经常会遇到各种奇怪的问题。今天我来分享一个在 Vue3 和 Spring Boot 混合开发项目中遇到的真实跨域问题。这个问题看似简单,但实际排查过程却让我花了不少时间。本文将从问题现象、分析思路、排查步骤到最终解决方案,一步步还原整个调试过程。
问题现象
在我们团队的一个前后端分离项目中,前端使用的是 Vue3,后端是 Spring Boot。正常情况下,前端通过 axios 调用后端接口应该能成功获取数据。但在某次更新之后,发现某些接口调用失败,浏览器控制台提示如下错误:
Access to fetch at 'http://api.example.com/data' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
起初我以为是后端配置出了问题,但查看了 Spring Boot 的相关配置,发现已经添加了 @CrossOrigin
注解,并且也设置了 spring.mvc.cors.allowed-origins
。然而问题依旧存在,这就让人感到困惑了。
问题分析
首先,我回顾一下 Vue3 和 Spring Boot 的通信机制。前端通过 axios 发送请求,后端通过 Spring Boot 提供 REST 接口。跨域问题是由于浏览器的安全策略导致的,只有当后端返回的响应头中包含 Access-Control-Allow-Origin
字段时,浏览器才会允许该请求。
可能的原因包括:
- 后端没有正确设置 CORS 配置
- 前端请求的域名和后端配置的允许域名不一致
- 请求方式或请求头不符合 CORS 规范
- 使用了代理服务器,但未正确配置
排查步骤
1. 检查后端 CORS 配置
首先确认后端是否真的启用了 CORS 支持。我们在 Spring Boot 中通常有两种方式启用 CORS:
方式一:使用 @CrossOrigin
注解
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:8081")
public class DataController {
@GetMapping("/data")
public ResponseEntity<String> getData() {
return ResponseEntity.ok("Hello from backend!");
}
}
方式二:全局配置
@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("GET", "POST")
.allowedHeaders("*")
.exposedHeaders("Access-Control-Allow-Origin")
.maxAge(3600)
.allowCredentials(true);
}
}
但即使这样,仍然无法解决问题,说明可能还有其他问题。
2. 使用 Postman 测试接口
为了排除前端问题,我尝试使用 Postman 直接访问后端接口,结果发现接口可以正常返回数据,说明后端逻辑没问题。这说明问题出在浏览器的 CORS 策略上。
3. 检查前端请求配置
在 Vue3 中,我们使用 axios 发送请求,代码如下:
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'http://api.example.com',
timeout: 5000,
});
export default {
async fetchData() {
try {
const response = await apiClient.get('/data');
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
},
};
看起来没有问题,但问题还是存在。
4. 检查浏览器控制台输出
再次打开浏览器控制台,发现除了之前的错误信息外,还有一条额外的信息:
Request URL: http://api.example.com/data
Request Method: GET
Status Code: 200 OK
Remote Address: 127.0.0.1:8080
Referrer Policy: no-referrer-when-downgrade
但是,响应头中并没有 Access-Control-Allow-Origin
字段。这说明后端虽然配置了 CORS,但实际上没有生效。
5. 检查后端日志
查看 Spring Boot 应用的日志,发现确实有请求到达,但没有看到任何关于 CORS 的日志。这说明可能是 Spring Boot 的 CORS 配置没有被正确加载。
6. 检查 Spring Boot 的依赖和版本
发现我们的项目中使用的是 Spring Boot 2.6.x,而有些 CORS 相关的类在较新的版本中发生了变化。我们尝试升级到 Spring Boot 2.7.x,问题依然存在。
7. 检查是否使用了反向代理
我们项目中使用 Nginx 作为反向代理。检查 Nginx 配置文件,发现没有设置 Access-Control-Allow-Origin
头,因此导致浏览器认为请求被拦截。
8. 修改 Nginx 配置
在 Nginx 配置中添加以下内容:
location /api {
proxy_pass http://localhost:8080;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
}
保存并重启 Nginx 后,问题终于也是完美解决
总结
这次跨域问题虽然看似简单,但排查过程却非常复杂。从后端配置到前端请求,再到反向代理的设置,每一个环节都可能成为问题的根源。通过逐步排查,最终找到了问题所在——Nginx 没有正确设置 CORS 头。
对于类似的问题,建议在开发过程中尽早测试接口,避免后期出现难以定位的问题。同时,合理配置反向代理也是确保前后端分离项目顺利运行的重要一步。