一、跨域问题的由来

前端开发中,跨域问题就像一道绕不开的坎。想象一下,你在自己的网站(比如www.example.com)里想通过 JavaScript 调用另一个网站(比如api.other-site.com)的接口,浏览器会直接拒绝这个请求,并抛出一个错误。这就是著名的同源策略在作祟。

同源策略要求协议、域名、端口三者必须完全相同,否则就是跨域。它的存在是为了安全,防止恶意网站窃取用户数据。但现实中,我们经常需要跨域请求数据,比如调用第三方 API、加载 CDN 资源等。这时候,就需要一些解决方案来绕过这个限制。

二、JSONP:最古老的跨域方案

JSONP(JSON with Padding)是最早的跨域解决方案之一,它巧妙地利用了<script>标签不受同源策略限制的特性。

示例(JavaScript 技术栈)

// 前端代码
function handleResponse(data) {
  console.log("获取到的数据:", data);
}

// 动态创建 script 标签,请求 JSONP 接口
const script = document.createElement("script");
script.src = "https://api.other-site.com/data?callback=handleResponse";
document.body.appendChild(script);
// 后端返回的数据格式(假设后端支持 JSONP)
handleResponse({
  "name": "张三",
  "age": 25
});

优点:

  • 兼容性极好,连 IE6 都能用
  • 实现简单,不需要后端做复杂配置

缺点:

  • 只支持 GET 请求
  • 安全性较差,容易遭受 XSS 攻击
  • 无法获取 HTTP 状态码,错误处理困难

三、CORS:现代浏览器的标准方案

CORS(Cross-Origin Resource Sharing)是目前最推荐的跨域解决方案,它通过 HTTP 头部让浏览器和服务器进行安全通信。

示例(Node.js 后端 + 前端 JavaScript)

// 后端代码(Node.js + Express)
const express = require("express");
const app = express();

app.use((req, res, next) => {
  // 允许来自任何域的请求
  res.header("Access-Control-Allow-Origin", "*");
  // 允许的 HTTP 方法
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  // 允许的请求头
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
  next();
});

app.get("/api/data", (req, res) => {
  res.json({ message: "这是跨域数据!" });
});

app.listen(3000, () => console.log("服务器已启动"));
// 前端代码(使用 fetch API)
fetch("http://localhost:3000/api/data")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error("请求失败:", error));

优点:

  • 支持所有 HTTP 方法(GET、POST、PUT 等)
  • 安全性高,可精细控制访问权限
  • 现代浏览器广泛支持

缺点:

  • 需要后端配合设置响应头
  • 某些旧浏览器(如 IE9 及以下)不支持

四、其他跨域方案

除了 JSONP 和 CORS,还有一些其他方案,适用于不同场景:

1. 代理服务器

如果你不能修改后端代码,可以在自己的服务器上搭建一个代理,让前端请求代理服务器,再由代理服务器去请求目标 API。

示例(Nginx 反向代理)

server {
  listen 80;
  server_name my-proxy.com;

  location /api/ {
    proxy_pass https://api.other-site.com/;
    proxy_set_header Host api.other-site.com;
  }
}

2. WebSocket

WebSocket 不受同源策略限制,适合实时通信场景。

const socket = new WebSocket("wss://api.other-site.com/ws");
socket.onmessage = (event) => {
  console.log("收到消息:", event.data);
};

五、如何选择合适的方案?

  • 简单快速 → JSONP(但尽量别用)
  • 现代项目 → CORS(首选)
  • 无法修改后端 → 代理服务器
  • 实时通信 → WebSocket

六、注意事项

  1. CORS 的预检请求(Preflight):某些复杂请求(如带自定义头的 POST)会先发 OPTIONS 请求,确保后端正确处理。
  2. 携带 Cookie:CORS 默认不发送 Cookie,需设置withCredentialsAccess-Control-Allow-Credentials
  3. 缓存问题:JSONP 请求可能被缓存,可加随机参数避免。

七、总结

跨域问题看似复杂,但核心思路就几种:绕过限制(JSONP)、标准协议(CORS)、中间层(代理)。现代开发中,CORS 是最佳选择,但也要根据实际情况灵活调整。