如何解决跨域?常见解决方案浅析

青灯夜游

青灯夜游

发布时间:2023-04-25 19:57:58

|

2517人浏览过

|

来源于掘金社区

转载

如何解决跨域?常见解决方案浅析

跨域是开发中经常会遇到的一个场景,也是面试中经常会讨论的一个问题。掌握常见的跨域解决方案及其背后的原理,不仅可以提高我们的开发效率,还能在面试中表现的更加游刃有余。

因此今天就来和大家从前端的角度来聊聊解决跨域常见的几种方式。

什么是跨域

在讲跨域之前,我们先来看看URL的组成内容:

一个URL的组成,通常包含协议、主机名、端口号、路径、查询参数和锚点几个部分。

这里展示了一个URL的示例:

https://www.example.com:8080/path/resource.html?page=1&sort=desc#header

在上述示例中:
● 协议为HTTPS
● 主机名为www.example.com
● 端口号为8080
● 路径为/path/resource.html
● 查询参数为page=1&sort=desc
● 锚点为header

所谓跨域,指的是请求URL中协议、主机名、端口号中任意一个部分不相同。

以上述URL为例,下面几种写法都算是和它跨域:

http://www.example.com:8080/    // 协议不同
https://www.example.a.com:8080/ // 主机名不同
https://www.example.com:8081/   // 端口号不同

为什么会跨域

其实跨域问题的出现是受限于浏览器的同源策略

所谓同源策略,其实是浏览器的一种安全机制,用于限制一个网页中的网络请求仅能够访问来自同一源(域名、协议和端口号均相同)的资源,主要目的是防止恶意网站通过脚本窃取其他网站的敏感数据,保障用户的隐私和安全。

当浏览器端的脚本(js文件)访问了其他域的网络资源时,就会出现跨域问题。

如何解决跨域

前文说到,跨域问题的出现是受限于浏览器的同源策略,那么常见的解决跨域问题的方案,其实也是围绕着浏览器展开的:

1.代理服务器

在我们平常的开发中,解决跨域问题最常使用的方案是使用代理服务器

代理服务器解决跨域问题其实是抓住了同源策略只受限于浏览器访问服务器,对于服务器访问服务器并没有限制的特点,作为中间服务器做了一个请求转发的功能

具体来说,就是前端工程师编写的网页运行在由webpack等脚手架搭建的代理服务器上,当前端网页在浏览器中发起网络请求时,其实这个请求是发送到代理服务器上的,然后代理服务器会将请求转发给目标服务器,再将目标服务器返回的响应转发给客户端。

代理服务器在此过程中扮演了一个中转的角色,可以对请求和响应进行一些修改、过滤和拦截,以实现一些特定的功能。因为前端网页运行在代理服务器上,所以不存在跨域问题。

那么在线上环境和开发环境下,代理服务器是如何做请求转发的呢?

1.线上环境

在线上环境下,我们一般会采用nginx来做反向代理,从而把前端的请求转发到目标接口上。

nginx是一个轻量级高并发的web服务器,基于事件驱动,而且跨平台,window和Linux都可以进行配置。

它作为代理服务器来解决开发中的跨域问题的主要方法就是监听线上前端网址的运行端口,然后碰到包含特殊标记的请求后就进行请求转发

2.开发环境

在开发环境下,无论是借助于webpack还是使用vite或其他脚手架搭建的前端项目,解决跨域问题的核心是借助http-proxy-middleware中间件实现的。而http-proxy-middleware中间件的核心又是对http-proxy的进一步封装。

这里先展示一下在项目中使用http-proxy-middleware来实现请求转发功能的示例代码:

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

module.exports = {
  server: {
    proxy: {
      // 将 /api/* 的请求代理到 http://localhost:3000/*
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        pathRewrite: { '^/api': '/' }
      }
    }
  }
};

接着我们可以自己使用原生node,借助http-proxy库来搭建一个具有请求转发功能的代理服务器Demo,感兴趣的朋友可以自己测试玩玩

1. 首先需要创建一个空文件夹(全英命名)作为项目文件夹,然后使用npm init -y命令将项目升级为node的项目:

npm init -y

2. 接着在项目根目录下创建一个index.html文件用于发起跨域请求:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>请求转发测试</title>
</head>

<body>
    <h1>请求转发测试</h1>
    <p id="message"></p>
    <script>
        fetch('/api/login')
            .then(response => response.text())
            .then(data => {
                document.getElementById('message').textContent = data;
            });
    </script>
</body>

</html>

3. 接着在项目根目录下新建index.js文件来编写服务端的代码。
index.js文件是实现具有请求转发功能的代理服务器的核心文件。

const http = require('http');
const httpProxy = require('http-proxy');
const fs = require('fs');
const path = require('path');

// 创建代理服务器实例
const proxy = httpProxy.createProxyServer({});

// 创建HTTP服务器
const server = http.createServer((req, res) => {
    if (req.url === '/' || req.url.endsWith('.html')) {
        // 读取HTML文件
        const filename = path.join(__dirname, 'index.html');
        fs.readFile(filename, 'utf8', (err, data) => {
            if (err) {
                res.writeHead(500);
                res.end('Error reading HTML file');
            } else {
                res.writeHead(200, { 'Content-Type': 'text/html' });
                res.end(data);
            }
        });
    } else if (req.url.startsWith('/api')) {
        // 重写路径,替换跨域关键词
        req.url = req.url.replace(/^\/api/, '');
        // 将请求转发至目标服务器
        proxy.web(req, res, {
            target: 'http://localhost:3000/',
            changeOrigin: true,
        });    
    }
});

// 监听端口
server.listen(8080, () => {
    console.log('Server started on port 8080');
});

4. 接着编写目标服务器target.js文件的内容,用于测试跨域访问:

多面-AI面试
多面-AI面试

猎聘推出的AI面试平台

下载
const http = require('http');

const server = http.createServer((req, res) => {
    if (req.url.startsWith('/login')) {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('我是localhost主机3000端口下的方法,恭喜你访问成功!');
    } else {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Hello, world!');
    }
});

server.listen(3000, () => {
    console.log('Target server is listening on port:3000');
})

5. 打开终端,输入启动目标服务器的命令:

node ./target.js //项目根目录下执行

6. 再开一个终端启动代理服务器,等待浏览器端发起请求就可以啦:

node ./index.js //项目根目录下执行

7. 最后在浏览器里访问http://localhost:8080, 打开控制台即可查看效果:

可以发现,浏览器network模块的网络请求确实是访问的8080端口的方法,但是我们的服务器默默的做了请求转发的功能,并将请求转发获取到的内容返回到了前端页面上。

其实http-proxy是对node内置库http的进一步封装,网络请求的核心部分还是使用http创建一个服务器对象去访问的。感兴趣的同学可以再读读http-proxy的源码~

除了代理服务器这种绕过浏览器同源策略的解决方式外,从前端的角度解决跨域问题还有如下一些常见的方法:

1.借助JSONP

JSONP的原理是通过动态创建<script>标签,向服务器发送请求并在请求URL中传递一个回调函数名(通常是在本地定义的函数名),服务器在返回的数据中将这个回调函数名和实际数据一起封装成一个JavaScript函数的调用,返回给客户端,客户端利用该回调函数对数据进行处理。

JSONP之所以能够跨域请求数据,是因为浏览器对于<script>标签的请求不会受到同源策略的限制。

需要注意的是,使用JSONP技术的前提是服务器需要支持JSONP的方式,即在返回的数据中包含回调函数名和实际数据的封装,否则客户端无法处理返回的数据。

此外,JSONP只支持GET请求,不支持POST等其他HTTP请求方式,因为<script>标签只支持GET请求。

因此JSONP这种方式在我们的开发中使用的场景不多。

2.使用CORS

CORS全称为Cross-Origin Resource Sharing,它通过HTTP头部信息告诉浏览器哪些跨域请求是被允许的,从而实现安全的跨域访问。

CORS解决跨域需要浏览器端和服务器端的配合。原理是在服务器端设置HTTP头部信息,告诉浏览器允许哪些源(域名、协议、端口)访问服务器上的资源,如果请求的源不在允许的列表中,则浏览器将拒绝访
问。

服务器可以通过设置Access-Control-Allow-OriginAccess-Control-Allow-HeadersAccess-Control-Allow-Methods等HTTP头部信息来控制跨域访问权限。

具体地,当浏览器发起跨域请求时,会先发送一个OPTIONS请求(预检请求),询问服务器是否允许该跨域请求。

服务器接收到该请求后,根据请求中的HTTP头部信息判断是否允许该请求。

如果允许,则返回相应的HTTP头部信息告知浏览器可以继续发送真正的跨域请求。如果不允许,则返回一个错误状态码,告诉浏览器该请求被拒绝。

预检请求时,请求头常见参数有

请求头
Origin 表示请求的源地址,即发起跨域请求的域名
Access-Control-Request-Method 表示实际请求采用的HTTP方法
Access-Control-Request-Headers 表示实际请求中所携带的额外请求头信息,比如自定义请求头等

预检请求时,响应头常见参数有

响应头
Access-Control-Allow-Origin *、origin...
Access-Control-Allow-Headers POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Methods Content-Type, Authorization..
Access-Control-Allow-Credentials true
Access-Control-Max-Age 86400

需要注意的是,使用CORS的前提是服务器需要设置相关的HTTP头部信息,且浏览器支持CORS。此外,CORS只支持现代浏览器,对于一些老旧的浏览器可能不支持CORS。

3.其他方案

比如WebSocketpostMessage等等

总结

近年来,随着前后端技术的飞速发展,前后端独立开发逐渐成为主流的开发模式。前后端程序员只需约定好接口,然后独自进行相应模块的开发,最后进行接口联调即可。在接口联调过程中,开发环境下的跨域就是一个需要解决的问题。

除此之外,当前后端项目打包上云后,前端页面通过线上地址访问后台接口时,线上环境下的跨域也是一个需要解决的问题。

本文讲述了几种常见的跨域解决方案,这些方案各有优缺点,大家可以根据实际情况选择适合的方案来解决对应的跨域问题~

推荐教程:nginx教程

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

通义千问
通义千问

阿里巴巴推出的全能AI助手

Claude
Claude

Anthropic发布的与ChatGPT竞争的聊天机器人

Cursor
Cursor

一个新的IDE,使用AI来帮助您重构、理解、调试和编写代码。

Hermes Agent
Hermes Agent

一位与您共同成长的Agent

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
搜狐简单AI文案写作与运营办公教程
搜狐简单AI文案写作与运营办公教程

本专题整理搜狐简单AI文案写作功能,覆盖营销文案、小红书笔记、公众号文章、短视频脚本、商品文案、活动文案和办公写作场景。

9

2026.07.15

搜狐简单AI绘图与图片生成提示词教程
搜狐简单AI绘图与图片生成提示词教程

本专题聚焦搜狐简单AI绘图和AI图片生成能力,整理头像、海报、插画、国风、写实、电商图等场景的提示词模板和出图技巧。

5

2026.07.15

搜狐简单AI官网入口与新手使用教程
搜狐简单AI官网入口与新手使用教程

本专题整理搜狐简单AI官网入口、网页版登录、新手使用流程、核心功能和常见问题,帮助用户快速了解AI绘图、AI文案、图片编辑和素材社区功能。

4

2026.07.15

万兴天幕AI图片生成与设计创作教程
万兴天幕AI图片生成与设计创作教程

本专题围绕万兴天幕文生图片、局部重绘和参考生图能力,整理AI绘图教程、设计提案玩法、图片修改技巧和视觉素材生成方法。

0

2026.07.15

万兴天幕AI视频生成教程与Prompt提示词
万兴天幕AI视频生成教程与Prompt提示词

本专题聚焦万兴天幕文生视频、图生视频、视频续写和AI特效功能,整理提示词写法、镜头语言模板、案例教程和视频生成效果优化方法。

7

2026.07.15

Vidu图生视频教程大全
Vidu图生视频教程大全

Vidu图生视频专题围绕上传图片生成动态视频展开,整理参考图选择、运动描述、镜头推进、人物表情、产品展示、照片动起来和生成失败排查方法,适合把海报、头像、产品图和插画转成短视频的用户。

6

2026.07.15

问小白AI写作与内容创作教程
问小白AI写作与内容创作教程

本专题详解问小白AI写作功能,涵盖文章生成、文案创作、论文辅助、报告整理及创意写作等内容生产技巧,助您高效产出优质内容。

5

2026.07.15

Vidu文生视频教程大全
Vidu文生视频教程大全

Vidu文生视频专题聚焦通过文字描述生成视频,覆盖场景设定、人物动作、镜头语言、画面风格、时长控制、运动效果和提示词优化方法,帮助用户用Vidu快速生成短视频、广告片段和创意素材。

2

2026.07.15

万兴天幕AI官网入口与新手使用教程
万兴天幕AI官网入口与新手使用教程

本专题整理万兴天幕AI官网入口、创作广场登录方式、APP下载、新手使用流程、核心功能和常见问题,帮助用户快速了解这款AI视频图片音乐生成工具。

0

2026.07.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MyEclipse学习中心
MyEclipse学习中心

共0课时 | 0人学习

vscode手册
vscode手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号