
摘要
在前端开发中,我们经常会遇到这样一个问题:明明接口写好了,地址也对,结果一请求就报错------跨域请求被拦截。这其实不是接口坏了,而是浏览器为了安全做的限制。本文会结合实际开发的环境,从后端配置、前端代理到一些"老办法"来讲清楚如何处理跨域问题,还会提供一个可运行的小 Demo,帮你快速上手。
引言
现在的前端项目,基本都是前后端分离的模式。前端项目一般跑在 3000 或者 5173 这种端口,而后端可能是 5000、8080,甚至部署在一个独立的服务器上。只要协议、域名或者端口不一样,浏览器就会认为是跨域。这个时候,如果后端不配合,就会报错。
所以我们得找到合适的方式去处理它。常见的思路有三种:
- 让后端放行(配置 CORS 响应头)
- 在开发环境用代理转发(前端配置 dev server)
- JSONP(老方法,现在基本不用了,但了解一下也行)
接下来我们逐个展开。
后端配置 CORS 允许
为什么要这么做
从根源上解决跨域问题的方式就是让后端告诉浏览器:"这个请求是安全的,可以放行"。这需要在服务端返回响应头的时候加上一些特殊的字段,比如 Access-Control-Allow-Origin
。
代码示例
假设你后端用的是 Express,只需要写一个中间件:
js
// server.js
import express from "express";
const app = express();
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*"); // 允许所有域名
res.header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type,Authorization");
next();
});
app.get("/hello", (req, res) => {
res.json({ msg: "Hello from backend!" });
});
app.listen(5000, () => {
console.log("Server running on http://localhost:5000");
});
这个配置之后,你的前端直接请求 http://localhost:5000/hello
就能拿到结果。
适用场景
这种方式适合:
- 后端和前端都是你自己能控制的项目
- 生产环境的接口调用
前端代理转发
为什么要这么做
有时候后端不是你写的,或者你没法修改配置。那就只能在本地开发的时候想点办法。最常见的办法就是配置前端的 dev server 代理。
代码示例
比如你用 Vite:
js
// vite.config.js
export default {
server: {
proxy: {
"/api": {
target: "http://localhost:5000", // 后端服务地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
};
然后前端只需要这样写:
js
// frontend/src/main.js
import axios from "axios";
axios.get("/api/hello").then((res) => {
console.log(res.data);
});
实际请求的时候,Vite 会帮你把 /api/hello
转发到 http://localhost:5000/hello
,这样就不会触发浏览器的跨域限制。
适用场景
这种方式常见于:
- 本地开发调试
- 前端没法改后端配置的时候
JSONP(了解即可)
为什么要这么做
在 CORS 没普及之前,大家会用 JSONP 绕过跨域。它的原理很简单:<script>
标签不受跨域限制,于是把数据包装成一个函数调用,通过 script 动态加载执行。
代码示例
html
<script>
function handleData(data) {
console.log("拿到数据:", data);
}
</script>
<script src="http://localhost:5000/jsonp?callback=handleData"></script>
服务端返回:
js
handleData({ msg: "Hello JSONP" });
虽然能用,但只能用于 GET 请求,现在已经不推荐了。
实际应用场景举例
场景1:前端调用第三方 API
比如你要在前端直接调用天气预报接口,但对方没有开放跨域支持。这时候,你可以在本地搭一个代理服务器,把请求转发到第三方 API。
js
// vite.config.js
proxy: {
"/weather": {
target: "https://api.weather.com",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/weather/, ""),
},
}
请求的时候:
js
axios.get("/weather/v3/wx/forecast").then((res) => {
console.log(res.data);
});
场景2:前后端分离项目的本地调试
后端跑在 http://localhost:5000
,前端跑在 http://localhost:5173
。如果后端能改,就直接加上 CORS 配置。如果后端不能改,那就在 Vite 里加代理。
场景3:生产环境 API 网关
有些公司会在生产环境用 Nginx 做反向代理,比如:
nginx
location /api {
proxy_pass http://127.0.0.1:5000;
}
这样前端永远只请求 /api
,实际请求被网关转发到后端,也能避免跨域。
QA 环节
Q1: 如果后端没有设置 CORS,我是不是一定要用代理?
A1: 是的,因为跨域拦截发生在浏览器端,你前端没法绕过,只能靠代理或者 JSONP。
Q2: 为什么我配置了 Access-Control-Allow-Origin
还是报错?
A2: 很可能是 OPTIONS 预检请求没处理好,需要返回 200,并且带上允许的请求头。
Q3: 开发环境用代理,生产环境要怎么处理?
A3: 最好还是让后端加 CORS。如果不行,就让运维在网关层(比如 Nginx)做反向代理。
总结
跨域问题看起来麻烦,其实只要理解原理,就会发现思路很清晰:
- 如果你能改后端,那就直接加上 CORS 配置,从根源解决
- 如果你只能改前端,那就在开发环境用代理转发
- JSONP 只是历史遗留,了解一下即可
换句话说,跨域问题并不是 bug,而是浏览器的安全机制。学会这几种处理方式,就能在不同的场景下优雅地搞定它。