大白话跨域问题的原理与多种解决方法的实现
- IT业界
- 2025-09-14 19:18:02

大白话跨域问题的原理与多种解决方法的实现 跨域问题原理
简单来说,当一个网页中的JavaScript代码想要去访问另一个不同域名、端口或协议的服务器上的数据时,就会出现跨域问题。这是浏览器的一种安全机制,为了防止恶意网站窃取用户信息等。比如,你在自己家网站( .example )上写的代码,想去访问别人家网站( .other )的数据,浏览器就会觉得有风险,可能会阻止这个访问,这就是跨域问题。
解决方法及代码示例 JSONP(JSON with Padding) 原理:利用<script>标签没有跨域限制的特点,通过动态创建<script>标签来请求数据,服务器返回的是一段JavaScript代码,把数据放在一个函数调用里。代码示例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JSONP Example</title> </head> <body> <script> // 定义一个函数,用来处理服务器返回的数据 function handleData(data) { console.log('Received data:', data); } // 创建一个script标签 var script = document.createElement('script'); // 设置script标签的src属性,指向服务器接口,并且带上回调函数名 script.src = 'http://example /api/data?callback=handleData'; // 将script标签添加到页面中 document.head.appendChild(script); </script> </body> </html> - **注释**:上述代码中,先定义了`handleData`函数用于处理服务器返回的数据。然后创建`<script>`标签,设置其`src`属性为服务器接口地址,并通过`callback`参数指定回调函数名。最后将`<script>`标签添加到页面,浏览器会自动加载该脚本,服务器返回的脚本会执行`handleData`函数并传入数据。 CORS(Cross-Origin Resource Sharing) 原理:服务器通过设置响应头,告诉浏览器哪些域名可以访问它的资源,浏览器根据这些响应头来决定是否允许跨域请求。代码示例(以Express为例) const express = require('express'); const app = express(); // 允许所有域名访问,实际应用中应限制为具体域名 app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); next(); }); app.get('/api/data', (req, res) => { const data = { message: 'Hello from server' }; res.json(data); }); const port = 3000; app.listen(port, () => { console.log(`Server running on port ${port}`); }); - **注释**:首先引入`express`模块创建服务器。通过`app.use`中间件设置`Access-Control-Allow-Origin`响应头为`*`,表示允许所有域名访问,还设置了允许的请求方法和请求头。当访问`/api/data`接口时,服务器返回一个包含消息的JSON数据。 代理服务器 原理:在本地搭建一个代理服务器,浏览器先向代理服务器发送请求,代理服务器再向目标服务器请求数据,然后把数据返回给浏览器,这样就不存在跨域问题了,因为浏览器和代理服务器在同一个域。代码示例(以webpack-dev-server代理为例) // webpack.config.js module.exports = { // 其他配置项... devServer: { proxy: { '/api': { target: 'http://example ', changeOrigin: true } } } }; - **注释**:在`webpack`配置文件中,通过`devServer.proxy`设置代理。当请求以`/api`开头时,`webpack-dev-server`会将请求转发到`http://example `,`changeOrigin`设置为`true`表示修改请求头中的`Origin`字段,使其与目标服务器的域名一致。 postMessage 原理:用于窗口之间的通信,比如一个页面中的iframe和父页面之间。可以通过postMessage方法发送消息,在接收方通过message事件监听接收消息。代码示例 <!-- 父页面 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Parent Page</title> </head> <body> <iframe id="myFrame" src="http://example /child.html"></iframe> <script> // 监听来自子窗口的消息 window.addEventListener('message', function (event) { console.log('Received message from child:', event.data); }); // 向子窗口发送消息 const frame = document.getElementById('myFrame'); frame.contentWindow.postMessage('Hello from parent', 'http://example '); </script> </body> </html> <!-- 子页面child.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Child Page</title> </head> <body> <script> // 监听来自父窗口的消息 window.addEventListener('message', function (event) { console.log('Received message from parent:', event.data); // 向父窗口回复消息 event.source.postMessage('Hello from child', event.origin); }); </script> </body> </html>跨域问题常见的错误提示有哪些? 跨域问题出现时,浏览器或服务器通常会给出一些错误提示,以下是一些常见的错误提示及通俗解释:
Access to XMLHttpRequest at ‘xxx’ from origin ‘xxx’ has been blocked by CORS policy 解释:这句话的意思是,你的JavaScript代码通过XMLHttpRequest(一种常用的网络请求方式)去请求一个网址(‘xxx’)的数据,但是浏览器发现这个请求的来源网址(也就是你当前网页所在的网址’xxx’)和要请求的网址不一样,而且服务器那边没有设置允许你这个来源网址来请求数据,所以就按照跨域的规则把你的请求给拦住了,不允许访问。 No ‘Access-Control-Allow-Origin’ header is present on the requested resource 解释:浏览器在请求数据时,发现服务器返回的信息里面没有Access-Control-Allow-Origin这个响应头。这个响应头本来是用来告诉浏览器,哪些网址可以访问这个服务器上的资源。现在没有这个头,浏览器就不知道让不让访问,所以就认为是跨域问题,阻止了请求。 Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at xxx 解释:浏览器有一个“同源策略”,就是说一个网页里的代码通常只能访问和它在同一个域名、端口和协议下的资源。这里就是说你要访问的那个远程资源(‘xxx’)和当前网页不是同一个来源,违反了“同源策略”,所以浏览器把你的请求给拦住了,不让你读取那个远程资源的数据。 Failed to load xxx: No ‘Access-Control-Allow-Methods’ header is present on the requested resource 解释:和前面没有Access-Control-Allow-Origin头类似,这里是服务器返回的信息里没有Access-Control-Allow-Methods这个头。这个头是用来告诉浏览器,允许使用哪些HTTP方法(比如GET、POST等)来请求资源。没有这个头,浏览器就不知道能不能用你代码里的那种方法去请求,所以就报错说加载资源失败了,也是因为跨域问题导致的。 Request header field xxx is not allowed by Access-Control-Allow-Headers in preflight response 解释:在正式发送请求之前,浏览器可能会先发送一个“预检”请求,看看服务器支不支持你要发送的请求。这里说的是你请求头里面有个字段(‘xxx’),服务器在“预检”响应的Access-Control-Allow-Headers里没有允许这个字段。也就是说,服务器不允许你用这个字段来请求数据,所以就报错了,这也是跨域相关的一个错误提示。除了JSONP,还有哪些方法可以解决跨域问题?
除了JSONP之外,解决跨域问题还有以下几种常见方法:
CORS(跨域资源共享) 原理:就是服务器设置一些响应头,告诉浏览器哪些域名的网页可以访问它的资源,以及允许使用哪些HTTP方法、请求头字段等。浏览器根据这些信息来判断这个跨域请求是否合法。实现步骤 服务端配置:在服务器端设置Access-Control-Allow-Origin响应头,指定允许访问的源。比如,如果要允许http://example 这个域名访问,就设置Access-Control-Allow-Origin: http://example 。如果想允许所有域名访问,就设置为*。还可以设置Access-Control-Allow-Methods来指定允许的HTTP方法,如GET、POST等;设置Access-Control-Allow-Headers来指定允许的请求头字段。客户端请求:客户端正常发送跨域请求就行,不用做特殊处理,浏览器会根据服务器返回的响应头来判断是否允许访问。 代码示例(以Node.js为例) const express = require('express'); const app = express(); // 设置允许跨域访问该服务的域名 app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', 'http://example '); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); next(); }); // 处理请求的路由 app.get('/data', (req, res) => { // 返回数据 res.json({ message: '这是来自服务器的数据' }); }); const port = 3000; app.listen(port, () => { console.log(`服务器在端口 ${port} 上运行`); }); 代理服务器 原理:把跨域请求先发给自己服务器上的一个代理接口,然后由这个代理接口再去请求目标服务器的数据,最后把数据返回给客户端。因为代理接口和客户端代码在同一个域名下,所以就不存在跨域问题了。实现步骤 搭建代理服务器:在自己的服务器上设置一个代理服务,它可以接收客户端的请求,然后转发到目标服务器。配置代理规则:告诉代理服务器哪些请求需要转发,以及转发到哪里。比如,把客户端对/api/*路径的请求转发到http://targetserver /api/*。客户端请求:客户端把原本要发给目标服务器的请求,改成发给代理服务器的接口。 代码示例(以使用http-proxy-middleware为例) const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); // 创建代理中间件 const proxy = createProxyMiddleware('/api', { target: 'http://targetserver ', // 目标服务器地址 changeOrigin: true }); // 使用代理中间件 app.use(proxy); const port = 3000; app.listen(port, () => { console.log(`服务器在端口 ${port} 上运行`); }); WebSocket 原理:WebSocket是一种在单个TCP连接上进行全双工通信的协议,它不受同源策略的限制,可以在不同源的客户端和服务器之间进行通信。实现步骤 服务端实现:在服务器端使用WebSocket库来创建WebSocket服务器,监听客户端的连接请求,接收和处理客户端发送的消息,向客户端发送消息。客户端实现:在客户端使用WebSocket API来连接服务器,发送和接收消息。 代码示例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebSocket示例</title> </head> <body> <script> // 创建WebSocket连接 const socket = new WebSocket('ws://example :8080'); // 连接成功后的回调 socket.onopen = function () { console.log('连接成功'); // 发送消息 socket.send('这是客户端发送的消息'); }; // 接收消息的回调 socket.onmessage = function (event) { console.log('收到服务器消息:', event.data); }; // 连接关闭的回调 socket.onclose = function () { console.log('连接关闭'); }; </script> </body> </html> const WebSocket = require('ws'); // 创建WebSocket服务器 const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', function (ws) { console.log('有客户端连接'); // 接收客户端消息 ws.on('message', function (message) { console.log('收到客户端消息:', message); // 向客户端发送消息 ws.send('这是服务器回复的消息'); }); // 客户端关闭连接的处理 ws.on('close', function () { console.log('客户端连接关闭'); }); }); postMessage 原理:HTML5的postMessage API允许在不同源的窗口之间进行安全的跨域通信。比如在一个网页中嵌入了另一个不同源的iframe,就可以通过postMessage来实现它们之间的通信。实现步骤 发送消息:在发送消息的窗口中,使用postMessage方法发送消息,指定要发送的数据和目标窗口的源。接收消息:在接收消息的窗口中,监听message事件,在事件回调中处理接收到的消息。 代码示例 <!-- 主页面 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>主页面</title> </head> <body> <iframe id="myFrame" src="http://example /iframe.html"></iframe> <script> const iframe = document.getElementById('myFrame'); // 向iframe发送消息 iframe.contentWindow.postMessage('这是主页面发送的消息', 'http://example '); // 接收来自iframe的消息 window.addEventListener('message', function (event) { if (event.origin === 'http://example ') { console.log('收到iframe的消息:', event.data); } }); </script> </body> </html> <!-- iframe页面 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>iframe页面</title> </head> <body> <script> // 接收来自主页面的消息 window.addEventListener('message', function (event) { if (event.origin === 'http://main ') { console.log('收到主页面的消息:', event.data); // 向主页面发送回复消息 window.parent.postMessage('这是iframe页面的回复', 'http://main '); } }); </script> </body> </html>如何在前后端分离的项目中处理跨域问题? 在前后端分离的项目里,处理跨域问题主要有在前端处理和在后端处理两种思路,以下是一些常见的方法及具体做法:
前端处理 使用代理服务器 原理:利用开发服务器提供的代理功能,将前端对后端接口的请求转发到真实的后端服务器地址,让浏览器以为所有请求都在同一个域内,从而避开浏览器的跨域限制。操作:以Vue项目为例,在vue.config.js文件中可以进行如下配置: module.exports = { devServer: { proxy: { '/api': { target: 'http://backendserver ', // 后端服务器地址 changeOrigin: true, pathRewrite: { '^/api': '' // 重写路径,去除/api前缀 } } } } }; - **效果**:这样配置后,前端代码中所有以`/api`开头的请求都会被代理到`http://backendserver `,就像请求本地资源一样,解决了跨域问题。 后端处理 CORS(跨域资源共享) 原理:服务器通过设置特定的响应头,告诉浏览器哪些域名的前端页面可以访问它的资源,以及允许使用哪些HTTP方法、请求头字段等,让浏览器能够判断这个跨域请求是否合法。操作:以使用Express框架的Node.js后端为例,可以这样设置: const express = require('express'); const app = express(); // 设置允许跨域访问该服务的域名 app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', 'http://frontend '); // 替换为前端域名 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); next(); }); // 处理请求的路由 app.get('/data', (req, res) => { // 返回数据 res.json({ message: '这是来自服务器的数据' }); }); const port = 3000; app.listen(port, () => { console.log(`服务器在端口 ${port} 上运行`); }); - **效果**:上述代码设置了允许`http://frontend `这个域名的前端页面访问后端的`/data`接口,并且允许使用`GET`、`POST`、`PUT`、`DELETE`这些HTTP方法,请求头中可以包含`Content-Type`字段。 JSONP 原理:利用<script>标签不受同源策略限制的特点,通过动态创建<script>标签来请求跨域数据,服务器返回的是一段JavaScript代码,通常是一个函数调用,把数据作为参数传递给这个函数,前端通过定义这个函数来处理接收到的数据。操作:后端代码示例(以Express为例): const express = require('express'); const app = express(); app.get('/data.jsonp', (req, res) => { const callback = req.query.callback; const data = { message: '这是来自服务器的JSONP数据' }; // 将数据包装成函数调用的形式返回 res.send(`${callback}(${JSON.stringify(data)})`); }); const port = 3000; app.listen(port, () => { console.log(`服务器在端口 ${port} 上运行`); }); - **效果**:前端页面可以通过以下方式请求数据: <!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 = 'http://backendserver /data.jsonp?callback=handleData'; document.head.appendChild(script); </script> </body> </html>上述代码中,前端通过动态创建<script>标签,请求后端的/data.jsonp接口,并指定callback参数为handleData,后端将数据包装成handleData({ message: '这是来自服务器的JSONP数据' })的形式返回,前端的handleData函数就可以接收到并处理数据。不过要注意,JSONP只支持GET请求。
大白话跨域问题的原理与多种解决方法的实现由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“大白话跨域问题的原理与多种解决方法的实现”
 
               
               
               
               
               
               
               
               
   
   
   
   
   
   
   
   
   
   
   
  