Interview AiBoxInterview AiBox 实时 AI 助手,让你自信应答每一场面试
什么是跨域问题?如何解决前端跨域请求?
题型摘要
跨域问题是浏览器的同源策略导致的,限制了一个域的文档或脚本获取另一个域的资源。常见解决方案包括:JSONP(利用script标签无跨域限制)、CORS(通过HTTP头部控制访问权限)、代理服务器(同源转发)、WebSocket(双向通信协议)、postMessage(跨文档通信)、document.domain(设置相同主域)和window.name(利用窗口名称特性)。选择方案时需考虑兼容性、安全性和场景需求,现代应用首选CORS,实时通信可选WebSocket,无法控制服务器时可考虑代理。
跨域问题及解决方案
1. 跨域问题的定义和原因
跨域问题(Cross-Origin Resource Sharing, CORS)是指浏览器的同源策略限制,阻止一个域的文档或脚本获取另一个域的资源。当协议、域名或端口有任何不同时,都被视为跨域。
同源策略
同源策略是浏览器的一种安全机制,用于限制一个源的文档或脚本如何能与另一个源的资源进行交互。所谓"同源"指的是"协议+域名+端口"三者相同。
2. 常见的跨域解决方案
2.1 JSONP (JSON with Padding)
JSONP是一种利用<script>标签没有跨域限制的特性来实现跨域请求的方法。
原理:动态创建<script>标签,通过src属性指定跨域的API地址,并在URL中携带一个回调函数名。服务器返回的数据会被包装在这个回调函数中,从而在客户端执行。
代码示例:
// 前端代码
function handleResponse(data) {
console.log('获取到的数据:', data);
}
const script = document.createElement('script');
script.src = 'http://example.com/api/data?callback=handleResponse';
document.body.appendChild(script);
// 服务器返回的数据格式
// handleResponse({"name": "张三", "age": 25});
优点:
- 兼容性好,支持老版本浏览器
- 实现简单
缺点:
- 只支持GET请求
- 安全性较低,容易受到XSS攻击
- 服务器需要配合修改返回数据格式
2.2 CORS (Cross-Origin Resource Sharing)
CORS是W3C标准,是一种跨域资源共享的机制,允许服务器声明哪些源站可以通过浏览器访问该服务器上的资源。
原理:通过HTTP头部字段,告诉浏览器哪些跨域请求是被允许的。
代码示例:
// 前端代码(使用fetch API)
fetch('http://example.com/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include' // 如果需要携带cookie
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
服务器端设置示例(Node.js):
const express = require('express');
const app = express();
// 设置CORS中间件
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://your-frontend-domain.com'); // 允许的源
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); // 允许的方法
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 允许的头部
res.header('Access-Control-Allow-Credentials', 'true'); // 允许携带cookie
// 处理预检请求
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.get('/api/data', (req, res) => {
res.json({ name: '张三', age: 25 });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
优点:
- 支持所有类型的HTTP请求
- 更加安全,服务器可以精确控制哪些源可以访问
- 现代浏览器广泛支持
缺点:
- 不支持非常老的浏览器(如IE9及以下)
- 需要服务器端配合设置
2.3 代理服务器
代理服务器方案是通过在同源域名下设置一个代理服务器,由这个代理服务器转发请求到目标服务器,从而绕过浏览器的同源策略。
原理:前端请求同源的代理服务器,代理服务器再将请求转发到目标服务器,获取数据后再返回给前端。
代码示例(Node.js代理服务器):
const express = require('express');
const request = require('request');
const app = express();
// 代理接口
app.use('/api', (req, res) => {
const url = 'http://target-server.com' + req.url;
req.pipe(request(url)).pipe(res);
});
app.listen(3000, () => {
console.log('Proxy server running on port 3000');
});
前端请求代码:
// 请求同源的代理服务器
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
优点:
- 前端代码无需特殊处理
- 可以处理各种类型的请求
- 可以在代理层添加额外的逻辑(如缓存、日志等)
缺点:
- 需要额外的服务器资源
- 增加了请求的延迟
- 需要维护代理服务器
2.4 WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议,它不受同源策略的限制。
原理:建立WebSocket连接后,双方可以随时发送消息,不受同源策略限制。
代码示例:
// 前端代码
const socket = new WebSocket('ws://example.com/socket');
socket.onopen = function(event) {
console.log('WebSocket连接已建立');
// 发送消息
socket.send(JSON.stringify({ type: 'getData' }));
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('收到数据:', data);
};
socket.onerror = function(error) {
console.error('WebSocket错误:', error);
};
socket.onclose = function(event) {
console.log('WebSocket连接已关闭');
};
优点:
- 支持双向通信
- 实时性好
- 不受同源策略限制
缺点:
- 需要服务器支持WebSocket
- 连接建立和维持需要额外资源
- 不适用于简单的请求-响应场景
2.5 postMessage
postMessage是HTML5引入的一种跨文档通信机制,允许来自不同源的脚本采用异步方式进行有限的通信。
原理:通过window.postMessage方法,可以在不同窗口(包括iframe)之间安全地传递消息。
代码示例:
// 页面A (http://example.com/pageA)
const popup = window.open('http://another-domain.com/pageB', 'popup');
// 发送消息到页面B
popup.postMessage({ type: 'getData', params: { id: 123 } }, 'http://another-domain.com');
// 监听来自页面B的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'http://another-domain.com') return;
console.log('收到消息:', event.data);
});
// 页面B (http://another-domain.com/pageB)
// 监听来自页面A的消息
window.addEventListener('message', function(event) {
// 验证消息来源
if (event.origin !== 'http://example.com') return;
const { type, params } = event.data;
if (type === 'getData') {
// 处理请求
fetchData(params.id).then(data => {
// 发送响应回页面A
event.source.postMessage({ type: 'response', data }, event.origin);
});
}
});
优点:
- 安全性高,可以精确控制消息的来源和目的地
- 支持不同源之间的通信
- 可以传递复杂的数据结构
缺点:
- 主要用于窗口/iframe之间的通信
- 需要双方都配合实现
- 不适合直接的API请求
2.6 document.domain
document.domain方法适用于主域相同但子域不同的情况,通过设置document.domain为相同的主域来实现跨域。
原理:将两个页面的document.domain设置为相同的主域,这样它们就可以互相访问对方的DOM。
代码示例:
// 页面A (http://sub1.example.com/pageA)
document.domain = 'example.com';
// 页面B (http://sub2.example.com/pageB)
document.domain = 'example.com';
// 现在页面A可以访问页面B的DOM
const iframe = document.createElement('iframe');
iframe.src = 'http://sub2.example.com/pageB';
document.body.appendChild(iframe);
iframe.onload = function() {
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
const element = iframeDocument.getElementById('someElement');
console.log(element.innerHTML);
};
优点:
- 实现简单
- 适用于主域相同但子域不同的情况
缺点:
- 只适用于主域相同的情况
- 有安全风险,不建议使用
- 现代浏览器有更多限制
2.7 window.name
window.name方法利用window对象在不同页面加载时保持其name属性的特性来实现跨域数据传递。
原理:在一个页面中将数据存储在window.name中,然后导航到同源的页面,通过window.name获取数据。
代码示例:
// 页面A (http://example.com/pageA)
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'http://another-domain.com/pageB'; // 跨域页面
document.body.appendChild(iframe);
iframe.onload = function() {
// 获取数据后,将iframe导航到同源页面
iframe.src = 'http://example.com/empty.html';
iframe.onload = function() {
// 现在可以安全地访问window.name
const data = JSON.parse(iframe.contentWindow.name);
console.log('获取到的数据:', data);
// 清理
document.body.removeChild(iframe);
};
};
// 页面B (http://another-domain.com/pageB)
// 将数据存储在window.name中
window.name = JSON.stringify({ name: '张三', age: 25 });
优点:
- 可以传递大量数据(window.name可以存储约2MB的数据)
- 实现相对简单
缺点:
- 需要一个中间的同源页面
- 不适合频繁的数据交换
- 安全性较低
3. 跨域解决方案的选择
选择哪种跨域解决方案取决于具体的应用场景和需求:
- JSONP:适用于简单的GET请求,且需要兼容老浏览器的情况。
- CORS:现代Web应用的首选方案,特别是当你有服务器控制权时。
- 代理服务器:适用于无法修改目标服务器配置的情况,或者需要在代理层添加额外逻辑的场景。
- WebSocket:适用于需要实时双向通信的应用,如聊天应用、实时数据更新等。
- postMessage:适用于不同窗口/iframe之间的通信,如嵌入第三方内容。
- document.domain:仅适用于主域相同但子域不同的情况,不推荐使用。
- window.name:适用于一次性传输大量数据的场景,但不常用。
4. 安全考虑
在解决跨域问题时,也需要考虑安全性:
- 验证请求来源:确保只接受来自可信源的请求。
- 避免暴露敏感信息:不要在跨域响应中返回敏感信息。
- 使用HTTPS:防止中间人攻击。
- 限制HTTP方法:只允许必要的HTTP方法。
- 设置适当的CORS头部:精确控制哪些源可以访问资源。
思维导图
Interview AiBoxInterview AiBox — 面试搭档
不只是准备,更是实时陪练
Interview AiBox 在面试过程中提供实时屏幕提示、AI 模拟面试和智能复盘,让你每一次回答都更有信心。
AI 助读
一键发送到常用 AI
跨域问题是浏览器的同源策略导致的,限制了一个域的文档或脚本获取另一个域的资源。常见解决方案包括:JSONP(利用script标签无跨域限制)、CORS(通过HTTP头部控制访问权限)、代理服务器(同源转发)、WebSocket(双向通信协议)、postMessage(跨文档通信)、document.domain(设置相同主域)和window.name(利用窗口名称特性)。选择方案时需考虑兼容性、安全性和场景需求,现代应用首选CORS,实时通信可选WebSocket,无法控制服务器时可考虑代理。
智能总结
深度解读
考点定位
思路启发
相关题目
请做一个自我介绍
自我介绍是面试的开场环节,应遵循"三段式"结构:基本信息与教育背景、核心能力与项目经验、求职动机与个人特质。重点突出与岗位相关的技能和经验,用具体数据和成果支撑,保持真诚自然的表达,控制在2-3分钟内。针对不同公司和岗位进行个性化调整,展示自己的匹配度和价值。
你有什么问题想问我们公司或团队的吗?
面试结尾提问是展示面试者思考深度和职业素养的重要机会。应提前准备3-5个有深度的问题,围绕团队技术、个人成长、公司文化和业务发展四个方面。好的问题能体现你对公司的了解、对职位的重视以及你的职业规划,避免问基础信息类问题。
请做一个自我介绍
自我介绍应遵循“我是谁-我为什么能胜任-我为什么想来”的逻辑框架。在“能胜任”部分,要通过STAR法则和量化结果来突出技术亮点和项目经验。在“想来”部分,要表达对华为技术、文化或业务的认同,展现匹配度和诚意。整个过程应简洁有力,控制在1-3分钟内。
请做一个自我介绍
自我介绍是面试的开场环节,应简洁明了地展示个人基本信息、教育背景、项目经验、技术特长、个人特质和求职动机。优秀的自我介绍应结构清晰、重点突出,与应聘岗位高度匹配,并表达出对公司的了解和加入的强烈意愿。
请做一个自我介绍,包括你的技术背景、项目经验和学习方向。
自我介绍应包含四个核心部分:个人背景、技术能力、项目经验和学习规划。技术背景需突出前端技术栈掌握程度;项目经验应选择代表性案例,说明技术实现和个人贡献;学习方向要体现职业规划与公司发展的契合度。整体表达应简洁有力,重点突出,时间控制在3-5分钟内。