青训营X豆包MarsCode 技术训练营之HTTP实践的跨域问题

62 阅读3分钟

常见的解决跨域的方法

CORS(跨域资源共享)

原理: CORS 是一种基于 HTTP 头的机制,允许服务器在响应中添加特定的头信息来告诉浏览器哪些源可以访问该资源。 当浏览器发起跨域请求时,会在请求头中添加Origin字段,表明请求的来源。服务器根据这个Origin字段和自身的配置来决定是否允许该请求。

实现:

在服务器端,需要配置响应头。例如,在 Node.js + Express 中,可以使用cors中间件:

const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());

服务器可以更精细地配置 CORS,比如只允许特定的源访问,或者允许特定的 HTTP 方法(如GET、POST等):

app.use(cors({
  origin: '[稀土掘金 (juejin.cn)](https://juejin.cn/)',
  methods: ['GET', 'POST']
}));

JSONP(JSON with Padding)原理:

JSONP 利用了

    function handleData(data) {
  console.log(data);
}
const script = document.createElement('script');
script.src = '[稀土掘金 (juejin.cn)](https://juejin.cn/)?callback=handleData';
document.body.appendChild(script);

在服务器端,需要将数据包装在回调函数中返回,例如(假设使用 Node.js):

    const http = require('http');
const server = http.createServer((req, res) => {
  if (req.url === '/data') {
    const data = { message: 'Hello from server' };
    const callback = req.query.callback;
    res.writeHead(200, { 'Content-Type': 'text/javascript' });
    res.end(`${callback}(${JSON.stringify(data)})`);
  }
});
server.listen(8080);
局限性:

JSONP 只支持GET方法,不适合需要POST等其他方法的场景。 存在安全风险,因为它本质上是在执行远程脚本。 代理服务器 原理: 通过在同源的服务器上设置代理,前端应用先向同源的代理服务器发送请求,代理服务器再将请求转发到目标服务器,然后将目标服务器的响应返回给前端应用。由于前端应用和代理服务器是同源的,不存在跨域问题。 实现: 在前端开发中,常用的工具如webpack-dev-server可以配置代理:

    // webpack.config.js
module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

这意味着,前端应用中所有以/api开头的请求都会被转发到api.example.com。

WebSocket原理:

WebSocket 是一种全双工通信协议,它不受同源策略的限制。它通过建立一个持久的连接,允许客户端和服务器之间进行双向数据传输。 实现: 在客户端:

    const socket = new WebSocket('ws://api.example.com/socket');
socket.onopen = () => {
  socket.send('Hello from client');
};
socket.onmessage = (event) => {
  console.log('Received: ', event.data);
};

在服务器端(以 Node.js 为例,使用ws库):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    console.log('Received: ', message);
    ws.send('Hello from server');
  });
});
postMessage原理:

postMessage方法允许在不同源的窗口(如iframe、window.open打开的窗口等)之间安全地传递数据。它在发送数据时会进行序列化,接收方收到数据后会进行反序列化。 实现: 在发送方(例如父窗口):

    const childWindow = window.open('http://other-domain.com');
childWindow.postMessage('Hello from parent', 'http://other-domain.com');
//在接收方(例如子窗口):

window.addEventListener('message', (event) => {
  if (event.origin === 'http://parent-domain.com') {
    console.log('Received: ', event.data);
    event.source.postMessage('Hello from child', event.origin);
  }
});

这些方法各有优缺点,在实际应用中需要根据具体的场景和需求来选择合适的跨域解决方案。