前言
跨域问题
跨域请求描述
前端跨域,指的是在浏览器端,通过 XMLHttpRequest 或 Fetch API 等方式向不同域名、不同端口或不同协议的资源发起请求时,会受到同源策略(Same-Origin Policy)的限制,导致请求失败。
解决方案
1、JSONP
描述
利用 script 标签的跨域特性,将数据包装成 JavaScript 函数的调用,从而实现跨域请求。但该方法只支持 GET 请求,且容易受到 XSS 攻击
代码示例(react)
使用 React 的函数组件来实现,使用了 useState 和 useEffect 两个 Hook。在 useEffect 中,我们动态创建一个 作为参数添加到 URL 中,从而实现跨域请求。在回调函数 handleResponse 中,我们将返回的数据保存到组件的状态中,从而触发组件的重新渲染。
需要注意的是,由于 JSONP 跨域请求是通过动态创建 script 标签来实现的,因此我们需要在组件卸载时手动移除该标签,以避免内存泄漏。在本例中,我们通过返回一个清理函数来实现该功能
import React, { useState, useEffect } from 'react';
function JsonpExample() {
const [data, setData] = useState(null);
useEffect(() => {
const script = document.createElement('script');
script.src = 'http://example.com/data?callback=handleResponse';
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
}
}, []);
function handleResponse(response) {
setData(response);
}
return (
<div>
{data ? (
<p>Hello, {data.name}!</p>
) : (
<p>Loading...</p>
)}
</div>
);
}
export default JsonpExample;
2、CORS
描述
通过在服务器端设置响应头,允许指定的域名访问资源。需要服务器端的支持,且需要浏览器支持 CORS。credentials 属性为 'include',表示允许跨域请求携带 Cookie 或认证信息;同时设置了 Origin 请求头,表示请求的源地址
代码示例(react)
import React, { useState, useEffect } from 'react';
function CorsExample() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('http://example.com/api', {
method: 'GET',
credentials: 'include',
headers: {
'Origin': 'http://localhost:3000'
}
})
.then(response => response.json())
.then(response => setData(response))
.catch(error => console.error(error));
}, []);
return (
<div>
{data ? (
<p>Hello, {data.name}!</p>
) : (
<p>Loading...</p>
)}
</div>
);
}
export default CorsExample;
3、代理
描述
在同一域名下,前端发送请求到自己的服务器,再由服务器转发请求到另一个域名,最后将响应结果返回给前端
代码示例(react)
import React, { useState, useEffect } from "react";
function App() {
const [data, setData] = useState(null);
useEffect(() => {
fetch("/api/data")
.then((res) => res.json())
.then((result) => {
setData(result);
})
.catch((error) => console.log(error));
}, []);
return (
<div>
{data ? (
<ul>
{data.map((item, index) => (
<li key={index}>{item.title}</li>
))}
</ul>
) : (
<p>Loading...</p>
)}
</div>
);
}
export default App;
转发原理示例代码
在这个文件中,我们使用 createProxyMiddleware 函数来创建一个代理,将 /api 路径下的请求转发到 http://localhost:3000。changeOrigin 选项设置为 true,以确保代理服务器设置正确的 Host 头部。
这样,我们就可以在 React 组件中使用 fetch('/api/data') 来获取数据,代理服务器会将请求转发到后端服务器,并返回响应,我们的 React 组件就可以正常处理响应数据了。
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:3000',
changeOrigin: true,
})
);
};
框架集成代理Proxy 示例代码
前端框架中集成的代理Proxy,通常是基于ES6中新增的Proxy对象。通过创建一个Proxy对象,我们可以定义一个目标对象(被代理对象),并在目标对象上定义一组拦截器(handler),这些拦截器会拦截对目标对象的(类似重定向)各种操作。
const target = { name: "Alice", age: 30 };
const handler = {
get: function(target, prop) {
console.log(`Reading property "${prop}"`);
return target[prop];
},
set: function(target, prop, value) {
console.log(`Setting property "${prop}" to "${value}"`);
target[prop] = value;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Reading property "name"; "Alice"
proxy.age = 31; // Setting property "age" to "31"
4、postMessage
描述
使用 HTML5 中的 postMessage 方法,在不同的窗口之间传递数据。这种方式需要在目标页面中添加监听事件
代码示例(react)
在这个示例中,我们创建了一个简单的 React 应用,其中包含一个按钮,点击按钮后会向目标窗口发送一条消息。同时,在页面中还展示了接收到的消息内容。
在 sendMessage 函数中,我们获取了目标窗口的引用,并使用 postMessage 方法向目标窗口发送了一条消息。在 useEffect 中,我们使用 addEventListener 方法监听 message 事件,并在事件处理函数中根据消息的来源进行了验证,并将消息内容保存到组件状态中以便在页面上展示。
需要注意的是,在实际应用中,需要根据实际情况设置目标窗口的源,以确保消息只会被发送到指定的窗口中
import React, { useState, useEffect } from 'react';
const TargetOrigin = 'http://localhost:3000'; // 目标窗口的源
function App() {
const [message, setMessage] = useState('');
// 发送消息到目标窗口
const sendMessage = () => {
const targetWindow = window.opener || window.parent;
if (targetWindow) {
targetWindow.postMessage('Hello from React!', TargetOrigin);
}
};
// 监听消息事件
useEffect(() => {
const receiveMessage = (event) => {
if (event.origin === TargetOrigin) {
setMessage(event.data);
}
};
window.addEventListener('message', receiveMessage);
return () => {
window.removeEventListener('message', receiveMessage);
};
}, []);
return (
<div>
<h1>React App</h1>
<button onClick={sendMessage}>Send Message</button>
<p>Received Message: {message}</p>
</div>
);
}
export default App;