在前端开发的过程中,跨域问题就像一道坎儿,时不时地就会蹦出来给咱找麻烦。不过别怕,今天咱就来好好唠唠从 JSONP 到 CORS 这两种解决跨域问题的方法,咱用最接地气的话,再配上详细的例子,让大家都能整明白。

一、啥是跨域问题

咱先搞清楚啥叫跨域问题。简单来说,浏览器为了安全起见,搞了个“同源策略”。啥是同源策略呢?就是如果两个 URL 的协议、域名和端口号都一样,那它们就是同源的,能随便互相访问;要是有一个不一样,那就属于跨域了,浏览器就会限制它们之间的访问。

比如说,你在 http://www.example1.com 这个页面上,想从 http://www.example2.com 那里拿点数据,这就属于跨域访问了,浏览器就可能会给你使绊子,不让你顺利拿到数据。

二、JSONP 解决方案

1. 原理

JSONP 的全称是 JSON with Padding,它的核心原理是利用了 <script> 标签的 src 属性不受同源策略限制这一特点。简单来说,就是服务器返回的数据不是单纯的 JSON 数据,而是把 JSON 数据包装在一个回调函数里,然后前端页面通过 <script> 标签去请求这个带有回调函数的 URL,等请求回来,就会自动执行这个回调函数,这样就能拿到服务器返回的数据了。

2. 示例

下面咱来看一个具体的例子,这里用 Node.js 来搭建服务器,前端页面用 HTML 和 JavaScript。

服务器端(Node.js)

// 引入 Node.js 的 http 模块
const http = require('http');

// 创建一个 HTTP 服务器
const server = http.createServer((req, res) => {
    // 获取请求的 URL
    const url = req.url;
    if (url.includes('/getData')) {
        // 从 URL 中提取回调函数名
        const callback = url.split('=')[1];
        // 要返回的数据
        const data = { message: '这是从服务器返回的数据' };
        // 把数据包装在回调函数里
        const jsonp = `${callback}(${JSON.stringify(data)})`;

        // 设置响应头
        res.writeHead(200, { 'Content-Type': 'application/javascript' });
        // 发送响应数据
        res.end(jsonp);
    }
});

// 监听 3000 端口
server.listen(3000, () => {
    console.log('服务器在 3000 端口启动');
});

客户端(HTML + JavaScript)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSONP 示例</title>
</head>

<body>
    <script>
        // 定义回调函数
        function handleData(data) {
            console.log(data.message);
        }

        // 创建一个 script 标签
        const script = document.createElement('script');
        // 设置 script 标签的 src 属性,带上回调函数名
        script.src = 'http://localhost:3000/getData?callback=handleData';
        // 把 script 标签添加到页面的 head 里
        document.head.appendChild(script);
    </script>
</body>

</html>

3. 应用场景

JSONP 比较适合那些只需要进行简单的数据获取,而且服务器端支持返回 JSONP 格式数据的场景。比如说,你要从第三方 API 获取一些公开的数据,像天气信息、新闻资讯啥的。

4. 优缺点

  • 优点:兼容性好,几乎所有的浏览器都支持。因为它是利用 <script> 标签,所以不存在浏览器兼容性的问题。
  • 缺点:只能处理 GET 请求,安全性也比较低,因为它是通过动态创建 <script> 标签来实现的,容易受到 XSS 攻击。只能拿到服务器返回的 JSON 数据,没办法处理其他类型的响应,比如 XML 啥的。

5. 注意事项

  • 服务器端要正确处理回调函数名,要保证返回的数据格式是正确的 JSONP 格式。
  • 前端页面要确保回调函数名和 URL 中传递的回调函数名一致。

三、CORS 解决方案

1. 原理

CORS 就是跨域资源共享,它是一种现代的跨域解决方案,是 W3C 标准。它允许浏览器和服务器之间进行跨域通信,主要是通过服务器设置响应头来告诉浏览器,哪些来源的请求是被允许的。简单来说,就是服务器在响应头里声明了哪些域名可以访问它的资源,浏览器看到这些响应头后,就会允许这些跨域请求。

2. 示例

同样,咱还是用 Node.js 来搭建服务器,前端页面用 HTML 和 JavaScript。

服务器端(Node.js)

const http = require('http');

const server = http.createServer((req, res) => {
    if (req.url === '/getCorsData') {
        // 设置允许跨域的请求头
        res.setHeader('Access-Control-Allow-Origin', '*');// 允许所有来源的请求
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');// 允许的请求方法
        res.setHeader('Access-Control-Allow-Headers', 'Content-Type');// 允许的请求头

        const data = { corsMessage: '这是通过 CORS 返回的数据' };
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(data));
    }
});

server.listen(3001, () => {
    console.log('服务器在 3001 端口启动');
});

客户端(HTML + JavaScript)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CORS 示例</title>
</head>

<body>
    <button id="fetchData">获取数据</button>
    <script>
        const button = document.getElementById('fetchData');
        button.addEventListener('click', () => {
            // 使用 fetch API 发送跨域请求
            fetch('http://localhost:3001/getCorsData')
              .then(response => response.json())
              .then(data => console.log(data.corsMessage))
              .catch(error => console.error(error));
        });
    </script>
</body>

</html>

3. 应用场景

CORS 适用于现代 Web 应用的各种场景,尤其是需要进行复杂的跨域交互,比如前后端分离的项目,前端页面和后端 API 可能部署在不同的域名下,这时候就可以用 CORS 来解决跨域问题。

4. 优缺点

  • 优点:支持所有的 HTTP 请求方法,像 GET、POST、PUT、DELETE 等。安全性高,因为服务器可以精确控制哪些来源的请求是被允许的。可以传递自定义的请求头,功能更强大。
  • 缺点:需要服务器端进行配置,如果服务器端没有正确配置响应头,跨域请求还是会失败。对于一些旧版本的浏览器,可能存在兼容性问题。

5. 注意事项

  • 服务器端要根据实际情况设置 Access-Control-Allow-Origin 响应头,如果设置为 *,表示允许所有来源的请求,这样虽然方便,但是安全性较低。如果要限制特定的来源,可以设置为具体的域名。
  • 对于复杂的请求,比如带有自定义请求头或者使用了 PUT、DELETE 等非简单请求方法的请求,浏览器会先发送一个预检请求(OPTIONS 请求),服务器要正确处理这个预检请求。

四、JSONP 和 CORS 的对比

1. 兼容性

JSONP 兼容性好,几乎所有的浏览器都支持;CORS 对于现代浏览器支持得比较好,但是对于一些旧版本的浏览器可能存在兼容性问题。

2. 安全性

JSONP 安全性较低,容易受到 XSS 攻击;CORS 安全性较高,因为服务器可以精确控制哪些来源的请求是被允许的。

3. 功能

JSONP 只能处理 GET 请求,功能比较单一;CORS 支持所有的 HTTP 请求方法,还可以传递自定义的请求头,功能更强大。

五、总结

跨域问题在前端开发中是比较常见的问题,JSONP 和 CORS 是两种比较常用的解决方案。JSONP 适用于一些简单的数据获取场景,兼容性好,但是安全性和功能上有一定的局限性;CORS 是现代的跨域解决方案,功能强大,安全性高,适用于各种复杂的跨域交互场景。在实际开发中,我们要根据具体的需求和场景来选择合适的解决方案。