面试官:聊聊你知道的跨域解决方案
跨域是开发中经常会遇到的一个场景,也是面试中经常会讨论的一个问题。掌握常见的跨域解决方案及其背后的原理,不仅可以提高我们的开发效率,还能在面试中表现的更加游刃有余。
因此今天就来和大家从前端的角度来聊聊解决跨域常见的几种方式。
什么是跨域
在讲跨域之前,我们先来看看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为例,下面几种写法都算是和它跨域:
ttp://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文件的内容,用于测试跨域访问:
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-Origin、Access-Control-Allow-Headers、Access-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.其他方案
比如WebSocket、postMessage等等
总结
近年来,随着前后端技术的飞速发展,前后端独立开发逐渐成为主流的开发模式。前后端程序员只需约定好接口,然后独自进行相应模块的开发,最后进行接口联调即可。在接口联调过程中,开发环境下的跨域就是一个需要解决的问题。
除此之外,当前后端项目打包上云后,前端页面通过线上地址访问后台接口时,线上环境下的跨域也是一个需要解决的问题。
本文讲述了几种常见的跨域解决方案,这些方案各有优缺点,大家可以根据实际情况选择适合的方案来解决对应的跨域问题~
相关文章:

面试官:聊聊你知道的跨域解决方案
跨域是开发中经常会遇到的一个场景,也是面试中经常会讨论的一个问题。掌握常见的跨域解决方案及其背后的原理,不仅可以提高我们的开发效率,还能在面试中表现的更加游刃有余。 因此今天就来和大家从前端的角度来聊聊解决跨域常见的几种方式。…...

SpringCloud五大核心组件
Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。 服务发现——Netflix Eureka …...
Verilog HDL语言入门(二)
强烈建议用同步设计2.在设计时总是记住时序问题3.在一个设计开始就要考虑到地电平或高电平复位、同步或异步复位、上升沿或下降沿触发等问题,在所有模块中都要遵守它4.在不同的情况下用if和case,最好少用if的多层嵌套(1层或2层比较合适&#…...

Simpleperf详细使用
一、Simpleperf介绍 Simpleperf是一个强大的命令行工具,它包含在NDK中,可以帮助我们分析应用的CPU性能。Simpleperf可以帮助我们找到应用的热点,而热点往往与性能问题相关,这样我们就可以分析修复热点源。 如果您更喜欢使用命令…...

【算法基础】二分图(染色法 匈牙利算法)
一、二分图 1. 染色法 一个图是二分图,当且仅当,图中不含奇数环。在判别一个图是否为二分图⑩,其实相当于染色问题,每条边的两个点必须是不同的颜色,一共有两种颜色,如果染色过程中出现矛盾,则说明不是二分图。 for i = 1 to n:if i 未染色DFS(i, 1); //将i号点染色未…...
Caputo 分数阶微分方程-慢扩散方程初边值问题基于L1 逼近的空间二阶方法及其Matlab程序实现
2.3.3 Caputo 分数阶一维问题基于 L1 逼近的空间二阶方法 考虑如下时间分数阶慢扩散方程初边值问题 { 0 C D t α u ( x , t ) = u...
I.MX6ULL_Linux_驱动篇(29) GPIO驱动
Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本篇的 LED 灯驱动最终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux 的驱动框架。I.MX6U-ALPHA 开发板上的 LED 连接到 I.MX6ULL 的 …...

jupyter的安装和使用
目录 ❤ Jupyter Notebook是什么? notebook jupyter 简介 notebook jupyter 组成 网页应用 文档 主要特点 ❤ jupyter notebook的安装 notebook jupyter 安装有两种途径 1.通过Anaconda进行安装 2.通过pip进行安装 启动jupyter notebook ❤ jupyter …...

Springboot新手开发 Cloud篇
前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:后端专栏 📧如果文章知识点有错误的地方,…...

Linux:函数指针做函数参数
#include <stdio.h> #include <stdlib.h> //创建带有函数指针做参数的函数框架api //调用者要先实现回调函数 //调用者再去调用函数框架 //所谓的回调是指 调用者去调用一个带有函数指针做参数的函数框架,函数框架反过来要调用调用者提供的回调函数 …...

Vue3(递归组件) + 原生Table 实现树结构复杂表格
一、递归组件 什么是递归,Javascript中经常能接触到递归函数。也就是函数自己调用自己。那对于组件来说也是一样的逻辑。平时工作中见得最多应该就是菜单组件,大部分系统里面的都是递归组件。文章中我做了按需引入的配置,所以看不到我引用组…...

ArrayList底层源码解析
Java源码系列:下方连接 http://t.csdn.cn/Nwzed 文章目录前言一、**ArrayList底层结构和源码分析**无参构造调用创建ArrayList集合无参构造总结:发文3个工作日后 up 会把总结放入前言部分,但也诚邀读者总结,可放入评论区有参构造…...

python:DIY字符画的程序使用说明.doc
目录开发环境要求运行方法具体的操作步骤如下:代码示例源码及运行程序下载地址开发环境要求 本系统的软件开发及运行环境具体如下。 操作系统:Windows 7、Windows 10。 Python版本:Python 3.7.0。 开发工具:Python IDLE。 …...

【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解
【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解 文章目录【Python/Opencv】图像权重加法函数:cv2.addWeighted()详解1. 介绍2. API3. 代码示例与效果3.1 代码3.2 效果4. 参考1. 介绍 在OpenCV图像加法cv2.add函数详解详细介绍了图像的加法运…...
容器的老祖宗LXC和Docker的关系
一、什么是LXC? LXC(Linux Container的缩写)是一个基于Linux内核的容器虚拟化技术,它提供了一种轻量级、快速、简便的方式来创建和管理系统容器。与传统虚拟化技术不同,LXC并不会模拟硬件,而是利用Linux内…...
Webpack迁移Rspack速攻实战教程(前瞻版)
前言 rspack 即将开源,但社区中不乏有已经落地的 case ,比如 rspack-migration-showcase 、 modern.js 等。 基于此,本文将介绍如何迁移一个近似于 CRA( create-react-app ) 的项目到 rspack 。 在阅读本文前&#…...

一行代码“黑”掉任意网站
文章目录只需一行代码,轻轻一点就可以把任意网站变成暗黑模式。 首先我们先做一个实验,在任意网站中,打开浏览器开发者工具(F12),在 C1onsole 控制台输入如下代码并回车: document.documentElement.style.filterinve…...

51单片机入门 -驱动 8x8 LED 点阵屏
硬件型号、软件版本、以及烧录流程 操作系统:Windows 10 x84-64单片机:STC89C52RC编译器:SDCC烧录软件:stcgal 1.6开发板:普中51单片机开发板A2套件(2022) 在 VS Code 中新建项目到烧录的过程…...

Xinlinx zynq7045国产替代 FMQL45T900全国产化 ARM 核心板+扩展板
TES745D 是一款基于 FMQL45T900 的全国产化 ARM 核心板。该核心板将 FMQL45T900(与XC7Z045-2FFG900I 兼容)的最小系统集成在了一个 87*117mm 的核心板上,可以作为一个核心模块,进行功能性扩展,能够快速的搭建起一个信号…...

硬刚ChatGPT!文心一言能否为百度止颓?中国版ChatGPT“狂飙”的机会在哪儿?
文章目录目录产品背景发展历程科技简介主要功能合作伙伴结语文心一言 (英文名:ERNIE Bot) *是百度基于文心大模型技术推出的生成式对话产品,被外界誉为“中国版ChatGPT”,将于2023年3月份面向公众开放。 [40] 百度在人…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...