前言
在前端开发中,"跨域" 是一个非常常见的问题。
当你在控制台看到 "Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy" 这样的错误时,就意味着遇到了跨域问题。
本文讲解跨域的本质、常见解决方案以及各自的适用场景。
什么是跨域?
简单来说,当一个网页从一个域名的资源去请求 另一个域名的资源时,就会产生跨域。
这里的 "域" 是由协议、域名和端口共同组成的,只要其中任何一个不同,就被视为不同的域。
http://www.example.com访问https://www.example.com(协议不同)http://www.example.com访问http://www.another.com(域名不同)http://www.example.com:80访问http://www.example.com:8080(端口不同)
浏览器出于安全考虑,会限制跨域的网络请求,这就是 "同源策略"。
同源策略是浏览器的一种安全机制,它阻止不同源的网页之间进行某些交互,比如读取对方的 Cookie、LocalStorage 等。
跨域解决方案
1. CORS:现代浏览器的标准方案
CORS(Cross-Origin Resource Sharing,跨域资源共享)是基于 HTTP 1.1 的跨域解决方案,也是目前最推荐的方式。
它的核心思想是:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许。
根据请求的不同类型,CORS 定义了三种交互模式:
简单请求
当请求同时满足以下条件时,被视为简单请求:
- 请求方法为 GET、POST 或 HEAD
- 请求头仅包含安全字段(如 Accept、Content-Type 等)
- 若包含 Content-Type,只能是 text/plain、multipart/form-data 或 application/x-www-form-urlencoded
简单请求的交互流程:
- 浏览器自动在请求头添加 Origin 字段,表明请求来源
- 服务器在响应头中添加 Access-Control-Allow-Origin 字段,指定允许的来源
- 浏览器检查响应头,如果允许当前源访问,就将响应交给 JS 处理
// 简单请求示例
fetch('http://crossdomain.com/api/news');
需要预检的请求
不符合简单请求条件的请求会被视为 "需要预检的请求",这类请求会先发送一个预检请求(OPTIONS 方法),询问服务器是否允许:
-
浏览器发送预检请求,包含:
- Origin:请求来源
- Access-Control-Request-Method:后续真实请求的方法
- Access-Control-Request-Headers:后续真实请求会使用的请求头
-
服务器响应预检请求,包含:
- Access-Control-Allow-Origin:允许的来源
- Access-Control-Allow-Methods:允许的请求方法
- Access-Control-Allow-Headers:允许的请求头
- Access-Control-Max-Age:预检结果的缓存时间(秒)
-
预检通过后,浏览器发送真实请求
-
服务器响应真实请求
// 需要预检的请求示例
fetch('http://crossdomain.com/api/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ name: '张三' })
});
附带身份凭证的请求
默认情况下,跨域请求不会附带 Cookie 等身份凭证。如果需要附带,可以这样设置:
// XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// fetch API
fetch(url, {
credentials: 'include'
});
此时服务器需要在响应头中添加:
Access-Control-Allow-Credentials: true
注意:这种情况下,服务器不能将 Access-Control-Allow-Origin 设置为*,必须指定具体的源。
2. JSONP:古老但仍在使用的方案
在 CORS 出现之前,JSONP 是实现跨域的常用方法。
它利用了 script 标签不受同源策略限制的特性。
JSONP 的工作原理:
- 客户端定义一个处理数据的函数
- 动态创建一个 script 标签,其 src 指向跨域的 API 地址,并通过 URL 参数指定回调函数名
- 服务器返回一段 JS 代码,内容是调用该回调函数,并将数据作为参数传入
- 浏览器执行这段 JS 代码,从而触发回调函数,获取数据
// 客户端代码
function handleData(data) {
console.log('获取到的数据:', data);
}
// 创建script标签
var script = document.createElement('script');
script.src = 'http://crossdomain.com/api/data?callback=handleData';
document.body.appendChild(script);
服务器返回的响应会是这样的:
handleData({ name: '张三', age: 18 });
JSONP 的缺点很明显:只能支持 GET 请求,安全性较差,不推荐在新项目中使用。
3. 代理:开发环境常用方案
代理是开发阶段解决跨域问题的常用手段。
它的原理是:由于浏览器有同源策略限制,但服务器之间没有,所以可以设置一个代理服务器,让它作为中间层转发请求。
具体做法是:
- 前端请求本地服务器(同源,不会跨域)
- 本地服务器将请求转发到目标服务器
- 目标服务器返回数据给本地服务器
- 本地服务器将数据返回给前端
在开发环境中,可以很方便地配置代理:
Vue 项目配置代理(vue.config.js)
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://crossdomain.com', // 目标服务器地址
changeOrigin: true, // 改变请求头中的Origin
pathRewrite: {
'^/api': '' // 去掉请求路径中的/api前缀
}
}
}
}
};
React 项目配置代理(package.json)
{
"proxy": "http://crossdomain.com"
}
使用代理后,前端请求代码可以这样写:
// 原本需要跨域的请求
// fetch('http://crossdomain.com/data')
// 使用代理后的请求
fetch('/api/data') // 实际会被代理到http://crossdomain.com/data
代理方案只适合开发环境,生产环境通常需要在服务器端配置 CORS。
总结
- CORS:推荐在生产环境使用,支持各种 HTTP 方法,安全性好
- JSONP:兼容性好(支持古老浏览器),但只支持 GET 请求,安全性差,不推荐新项目使用
- 代理:主要用于开发环境,简单易用,无需修改后端代码

2万+





