面试必考:全面解析跨域及其解决方案

跨域问题是前端开发中常见且必须掌握的知识点之一。本文将详细介绍跨域的概念、手写JSONP和CORS跨域代码及其原理,如何在Vue3项目中替换Mock数据接口为真实后端数据接口,以及总结九种常见的跨域解决方案。

一、什么是跨域?

跨域是指浏览器因同源策略的限制,无法访问不同源(协议、域名、端口任一不同)的资源。例如,前端页面运行在 http://example.com,但需要访问 http://api.example.com 的数据时,就会遇到跨域问题。

二、手写JSONP跨域代码及原理讲解

原理

JSONP(JSON with Padding)是一种非正式的数据传输格式,它通过 <script> 标签的 src 属性来实现跨域请求。因为 <script> 标签不受同源策略限制。

实现代码

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSONP Example</title>
</head>
<body>
    <h1>JSONP Cross-Origin Request</h1>
    <button onclick="fetchData()">Fetch Data</button>
    <script>
        function fetchData() {
            const script = document.createElement('script');
            script.src = 'http://localhost:3000/data?callback=handleResponse';
            document.body.appendChild(script);
        }

        function handleResponse(data) {
            console.log('Received data:', data);
        }
    </script>
</body>
</html>

 

后端代码(Node.js示例):

const express = require('express');
const app = express();
const port = 3000;

app.get('/data', (req, res) => {
    const callback = req.query.callback;
    const data = { message: 'Hello, JSONP!' };
    res.send(`${callback}(${JSON.stringify(data)})`);
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});

 

三、手写跨域资源共享(CORS)处理跨域及原理

原理

CORS(Cross-Origin Resource Sharing)是一种机制,它使用额外的HTTP头来告诉浏览器,允许从其他域加载资源。服务器通过设置适当的HTTP响应头,来告诉浏览器哪些域可以访问资源。

实现代码

前端代码:

fetch('http://localhost:3000/data', {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json'
    }
})
.then(response => response.json())
.then(data => console.log('Received data:', data))
.catch(error => console.error('Error:', error));

 

后端代码(Node.js示例):

const express = require('express');
const app = express();
const port = 3000;

app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*'); // 允许所有域访问
    res.header('Access-Control-Allow-Methods', 'GET, POST'); // 允许的方法
    res.header('Access-Control-Allow-Headers', 'Content-Type'); // 允许的请求头
    next();
});

app.get('/data', (req, res) => {
    res.json({ message: 'Hello, CORS!' });
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});

 

四、Vue3通用后台管理如何替换Mock数据接口为真实的后端数据接口

在Vue3项目中,我们可以通过修改API请求地址,将Mock数据接口替换为真实后端数据接口。

示例

假设我们有一个Vue3项目,之前使用Mock数据:

// api.js (使用Mock数据)
export const getData = () => {
    return new Promise((resolve) => {
        resolve({ data: 'Mock data' });
    });
};

 

现在,我们将其替换为真实后端接口:

// api.js (使用真实后端数据)
import axios from 'axios';

const BASE_URL = 'http://localhost:3000';

export const getData = () => {
    return axios.get(`${BASE_URL}/data`);
};

 

然后在组件中调用:

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <p>{{ data }}</p>
  </div>
</template>

<script>
import { getData } from './api';

export default {
  data() {
    return {
      data: ''
    };
  },
  methods: {
    fetchData() {
      getData()
        .then(response => {
          this.data = response.data.message;
        })
        .catch(error => {
          console.error('Error:', error);
        });
    }
  }
};
</script>

 

五、九种跨域方案的汇总

  1. JSONP:通过 <script> 标签实现跨域,只支持GET请求。
  2. CORS:通过设置HTTP头允许跨域请求,支持复杂请求。
  3. 服务器代理:如使用Node.js中间件(http-proxy-middleware)或Nginx代理请求,绕过浏览器的同源策略。
  4. WebSocket:WebSocket协议不受同源策略限制,可以实现跨域通信。
  5. PostMessage:通过 window.postMessage 实现不同窗口间的数据传递。
  6. 跨域资源嵌入:通过 <iframe><img><link><script> 等标签加载跨域资源。
  7. document.domain:适用于主域相同子域不同的跨域,通过设置相同的 document.domain 实现。
  8. window.name:通过改变窗口的 name 属性实现跨域数据传递。
  9. 跨域请求伪造(CORS Preflight):通过简单请求或预检请求绕过CORS限制。

1. JSONP示例

详见上文。

2. CORS示例

详见上文。

3. 服务器代理示例(Node.js中间件)

const { createProxyMiddleware } = require('http-proxy-middleware');
const express = require('express');
const app = express();

app.use('/api', createProxyMiddleware({
    target: 'http://backend-server.com',
    changeOrigin: true,
}));

app.listen(3000);

 

4. WebSocket示例

const socket = new WebSocket('ws://localhost:3000');

socket.onopen = () => {
    console.log('WebSocket connection opened');
    socket.send('Hello Server!');
};

socket.onmessage = (event) => {
    console.log('Received:', event.data);
};

 

后端代码(Node.js示例):

const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3000 });

server.on('connection', ws => {
    ws.on('message', message => {
        console.log('Received:', message);
        ws.send('Hello Client!');
    });
});

 

5. PostMessage示例

<!-- parent.html -->
<iframe id="child" src="child.html" style="display:none;"></iframe>
<script>
    const child = document.getElementById('child').contentWindow;
    child.postMessage('Hello from parent!', 'http://child.com');

    window.addEventListener('message', (event) => {
        if (event.origin === 'http://child.com') {
            console.log('Received from child:', event.data);
        }
    });
</script>

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

 

6. 跨域资源嵌入示例

<!-- index.html -->
<script src="http://cross-origin.com/script.js"></script>

 

7. document.domain示例

<!-- parent.example.com -->
<script>
    document.domain = 'example.com';
    // 访问子域内容
</script>

 

8. window.name示例

<!-- page1.html -->
<script>
    window.name = 'data from page1';
    location.href = 'http://cross-origin.com/page2.html';
</script>

<!-- page2.html -->
<script>
    console.log(window.name); // 'data from page1'
</script>

 

9. 跨域请求伪造(CORS Preflight)示例

后端代码:

const express = require('express');
const app = express();

app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    if (req.method === 'OPTIONS') {
        return res.sendStatus(204); // 对预检请求直接返回204状态码
    }
    next();
});

app.post('/api', (req, res) => {
    res.json({ message: 'Data received' });
});

app.listen(3000, () => {
    console.log('Server running at http://localhost:3000');
});

 

posted @ 2024-07-27 18:23  最小生成树  阅读(27)  评论(0编辑  收藏  举报