Angular由一个bug说起之十三:Cross Origin

跨域

想要了解跨域,首要要了解源

什么是源,源等于协议加域名加端口号

只有这三个都相同,才是同源,反之则是非同源。

比如下面这四个里,只有第4个是同源

而浏览器给服务器发送请求时,他们的源一样,就是同源请求,反之就是非同源请求,非同源请求又称为跨域。

当你是跨域时,浏览器为了确保资源安全,会对跨域的访问资源做出一些限制,也就是浏览器的同源策略。

这是W3C上对同源策略的说明:https://www.w3.org/Security/wiki/Same_Origin_Policy

浏览器会对跨域做出哪些限制?

例如:源a和源b,它们是非同源的,则浏览器会有如下限制:

1. DOM访问限制:源a的脚本不能读取和操作源b的DOM。

页面1

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <h1>我是页面1</h1>
10.	    <button onclick="showDOM()">获取页面2的DOM</button>
11.	    <br>
12.	    <iframe id="framePage" src="./demo.html"></iframe>
13.	    <!-- <iframe id="framePage" src="https://www.baidu.com"></iframe> -->
14.	
15.	    <script type="text/javascript" >
16.	        function showDOM(){
17.	            const framePage = document.getElementById('framePage')
18.	            console.log(framePage.contentWindow.document.body) //同源的可以获取,非同源的无法获取
19.	        }
20.	    </script>
21.	    </body>
22.	</html>

页面2

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <h2>我是页面2</h2>
10.	</body>
11.	</html>

页面是这样的

点击获取

换一个不同源的页面

1.	<body>
2.	    <h1>我是页面1</h1>
3.	    <button onclick="showDOM()">获取页面2的DOM</button>
4.	    <br>
5.	    <!-- <iframe id="framePage" src="./demo.html"></iframe> -->
6.	    <iframe id="framePage" src="https://www.bilibili.com"></iframe>
7.	
8.	    <script type="text/javascript">
9.	        function showDOM() {
10.	            const framePage = document.getElementById('framePage')
11.	            console.log(framePage.contentWindow.document.body) //同源的可以获取,非同源的无法获取
12.	        }
13.	    </script>
14.	</body>

获取DOM

获取不到

2. 第二个限制,源a不能访问源b的cookie

3.	<!DOCTYPE html>
4.	<html lang="en">
5.	
6.	<head>
7.	    <meta charset="UTF-8">
8.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
9.	    <title>Document</title>
10.	</head>
11.	
12.	<body>
13.	    <h1>我是页面1</h1>
14.	    <button onclick="getCookie()">获取页面2的cookie</button>
15.	    <br>
16.	    <!-- <iframe id="framePage" src="./demo.html"></iframe> -->
17.	    <iframe id="framePage" src="https://www.bilibili.com"></iframe>
18.	
19.	    <script type="text/javascript">
20.	        function getCookie() {
21.	            const framePage = document.getElementById('framePage')
22.	            console.log(framePage.contentWindow.document.cookie) //同源的可以获取,非同源的无法获取
23.	        }
24.	    </script>
25.	</body>
26.	
27.	</html>

还是一样的页面,这次获取的是cookie

也是获取不到的,因为你在获取document这一步就已经失败了

3. ajax响应数据限制:源a可以给源b发请求,但是无法获取源b响应的数据。

这是一个获取头条新闻的页面

代码

28.	<!DOCTYPE html>
29.	<html lang="en">
30.	<head>
31.	    <meta charset="UTF-8">
32.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
33.	    <title>Document</title>
34.	</head>
35.	<body>
36.	    <button onclick="getNews()">获取头条新闻</button>
37.	
38.	    <script>
39.	        async function getNews() {
40.	            const result = await fetch('https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc');
41.	            const data = await result.json();
42.	            console.log('data');
43.	        }
44.	    </script>
45.	</body>
46.	</html>

页面

获取不到

备注:在上述限制中,浏览器对 Ajax 获取数据的限制是影响最大的一个,且实际开发中经常遇到。

几个注意点

  1. 跨域限制仅存在浏览器端,服务端不存在跨域。
  2. 即使跨域了,ajax请求也可以正常发出,但响应数据不会交给开发者。
  3. link,script,img这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响。

那么要如何解决跨域

想要解决跨域,一般有三种方法

第一种,用CORS解决ajax跨域问题

CORS 概述

CORS 全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照 CORS 规范,添加特定响应头来控制浏览器校验,大致规则如下:

  • 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过
  • 服务器明确表示允许跨域请求,则浏览器校验通过

了解了CORS之后,还要了解一下简单请求和复杂请求

CORS 会把请求分为两类,分别是:1.简单请求、2.复杂请求。

简单请求要满足这个三个条件

  1. 请求方法要为 get, head或者post。
  2. 请求头字段要符合CORS安全规范(https://fetch.spec.whatwg.org/#cors-safelisted-request-header), 一般只要不手动修改请求头,都能符合改规范。
  3. 请求头的Content-Type的值只能是这三种:

text/plain

multipart/form-data

application/x-www-form-urlencoded

不满足这三个要求的请求都是复杂请求,而复杂请求会自动发送预检请求

预检请求是一种在实际跨域请求发出之前,由浏览器自动发出的一种请求。主要用于向服务器确认是否允许接下来的跨域请求。基本流程是这样的,浏览器先发起一个options请求,携带这个几个请求头,Origin(发起请求的源),Access-Control-Request-Method(实际请求的HTTP方法),Access-Control-Request-Headers(实际请求中使用的自定义头)。如果通过预检,才会继续发起实际的跨域请求。

这就是预检请求

CORS 解决简单请求跨域

前端

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <button onclick="getData()">获取数据</button>
10.	
11.	    <script>
12.	        async function getData() {
13.	            const url = 'http://127.0.0.1:8081/';
14.	            const result = await fetch(url);
15.	            const data = await result.json();
16.	            console.log('data');
17.	        }
18.	    </script>
19.	</body>
20.	</html>

服务端代码(以express框架为例):

1.	const express = require('express');
2.	const app = express();
3.	
4.	app.get('/', (req, res) => {
5.	    res.send([{ name: '张三', age: 19 }])
6.	})
7.	
8.	app.listen(8081, () => {
9.	    console.log('服务器成功启动');
10.	})

这个时候是获取不到数据的

设置下cors

1.	const express = require('express');
2.	const app = express();
3.	
4.	app.get('/', (req, res) => {
5.	    res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
6.	    res.send([{ name: '张三', age: 19 }])
7.	})
8.	
9.	app.listen(8081, () => {
10.	    console.log('服务器成功启动');
11.	})

这样就能获取到数据了

整体思路:服务器在给出响应时,通过添加Access-Control-Allow-Origin响应头,来明确表达允许某个源发起跨域请求,随后浏览器在校验时,直接通过。

像这样的设置是允许某个源跨域

1.	// 允许 http://127.0.0.1:5500 这个源发起跨域请求
2.	res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')

而这样则是允许所有源跨域

1.	// 允许所有源发起跨域请求
2.	res.setHeader('Access-Control-Allow-Origin', '*')

CORS 解决复杂请求跨域

第一步:服务器先通过浏览器的预检请求,服务器需要返回如下响应头

Access-Control-Allow-Origin(允许的源)

Access-Control-Allow-Methods(允许的方法)

Access-Control-Allow-Headers(允许的自定义头)

Access-Control-Max-Age(预检请求的结果缓存时间(可选))

第二步:处理实际的跨域请求(与处理简单请求跨域的方式相同)

服务器代码

1.	const express = require('express');
2.	const app = express();
3.	
4.	app.options('/', (req, res) => {
5.	    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
6.	    res.setHeader('Access-Control-Allow-Methods', 'GET')
7.	    res.setHeader('Access-Control-Allow-Headers', 'city')
8.	    res.send()
9.	})
10.	
11.	app.get('/', (req, res) => {
12.	    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
13.	    res.send([{ name: '张三', age: 19 }])
14.	})
15.	
16.	app.listen(8081, () => {
17.	    console.log('服务器成功启动');
18.	})

前端代码

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <button onclick="getData()">获取数据</button>
10.	
11.	    <script>
12.	        async function getData() {
13.	            const url = 'http://127.0.0.1:8081/';
14.	            const result = await fetch(url, { method: 'GET', headers: { city: 'beijing' } });
15.	            const data = await result.json();
16.	            console.log('data', data);
17.	        }
18.	    </script>
19.	</body>
20.	</html>

数据获取到了

预检请求返回的响应头

借助 cors 库快速完成配置

上述的配置中需要自己配置响应头,比较麻烦,借助cors库,可以更方便完成配置

安装cors

npm i cors

配置cors

1.	const express = require('express');
2.	const cors = require('cors');
3.	const app = express();
4.	
5.	// 使用cors中间件
6.	app.use(cors({
7.	    origin: 'http://127.0.0.1:5500', // 允许的源
8.	    methods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], // 允许的方法
9.	    allowedHeaders: ['city'], // 允许的自定义头
10.	    exposedHeaders: ['abc'], // 要暴露的响应头
11.	    optionsSuccessStatus: 200 // 预检请求成功的状态码
12.	}))
13.	
14.	app.get('/', (req, res) => {
15.	    res.setHeader('abc', '123')
16.	    res.send([{ name: '张三', age: 19 }])
17.	})
18.	
19.	app.listen(8081, () => {
20.	    console.log('服务器成功启动');
21.	})
22.	

成功获取数据

第二种,JSONP 解决跨域问题

JSONP 概述: JSONP 是利用了<script>标签可以跨域加载脚本,且不受严格限制的特性,可以说是程序员智慧的结晶,早期一些浏览器不支持 CORS 的时,可以靠 JSONP 解决跨域(现在基本不用了)。

基本流程:

    • 第一步:客户端创建一个<script>标签,并将其src属性设置为包含跨域请求的 URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。
    • 第二步:服务端接收到请求后,将数据封装在回调函数中并返回。
    • 第三步:客户端的回调函数被调用,数据以参数的形势传入回调函数。

示例代码

前端

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <script>
10.	        function callback(data) {
11.	            console.log('data', data)
12.	        }
13.	    </script>
14.	    <script src="http://127.0.0.1:8081/users"></script>
15.	</body>
16.	</html>

后端

1.	const express = require('express');
2.	const app = express();
3.	
4.	app.get('/users', (req, res) => {
5.	    res.send(`callback([{ name: '张三', age: 19 }])`)
6.	})
7.	
8.	app.listen(8081, () => {
9.	    console.log('服务器成功启动');
10.	})

结果

优化下代码

前端

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	
4.	<head>
5.	    <meta charset="UTF-8">
6.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
7.	    <title>Document</title>
8.	</head>
9.	
10.	<body>
11.	    <button onclick="getUsers()">获取用户</button>
12.	
13.	    <script>
14.	        function test(data) {
15.	            console.log('data', data)
16.	        }
17.	        function getUsers() {
18.	            // 创建script元素
19.	            const script = document.createElement('script')
20.	            // 指定script的src属性
21.	            script.src = 'http://127.0.0.1:8081/users?callback=test'
22.	            // 将script元素添加到body中触发脚本加载
23.	            document.body.appendChild(script)
24.	            // script标签加载完毕后移除该标签
25.	            script.onload = () => {
26.	                script.remove()
27.	            }
28.	        }
29.	    </script>
30.	</body>
31.	
32.	</html>

后端

1.	const express = require('express');
2.	const app = express();
3.	
4.	const users = [
5.	    { name: '张三', age: 19 },
6.	    { name: '李四', age: 20 }
7.	]
8.	
9.	app.get('/users', (req, res) => {
10.	    const { callback } = req.query
11.	    res.send(`${callback}(${JSON.stringify(users)})`)
12.	})
13.	
14.	app.listen(8081, () => {
15.	    console.log('服务器成功启动');
16.	})

效果

用jQuery封装jsonp

1.	$.getJSON('http://127.0.0.1:8081/teachers?callback=?', (data) => {
2.	            console.log(data)
3.	        })

第三种,配置代理服务器解决跨域

这里需要用到http-proxy-middleware这个包来配置代理服务器

下载http-proxy-middleware

npm i http-proxy-middleware

前端

1.	<!DOCTYPE html>
2.	<html lang="en">
3.	<head>
4.	    <meta charset="UTF-8">
5.	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6.	    <title>Document</title>
7.	</head>
8.	<body>
9.	    <button onclick="getNews()">获取头条数据</button>
10.	
11.	    <script>
12.	        async function getNews() {
13.	            const url = 'http://127.0.0.1:8081/api/hot-event/hot-board/?origin=toutiao_pc';
14.	            const result = await fetch(url);
15.	            const data = result.json();
16.	            console.log('data', data);
17.	        }
18.	    </script>
19.	</body>
20.	</html>

后端

1.	const express = require('express');
2.	const app = express();
3.	const { createProxyMiddleware } = require('http-proxy-middleware');
4.	
5.	app.use(express.static('./public'))
6.	
7.	app.use('/api', createProxyMiddleware({ // 拦截所有带有/api的请求
8.	    target:'https://www.toutiao.com', // 转发的目标
9.	    changeOrigin:true, // 允许跨域
10.	    pathRewrite:{
11.	      '^/api':'' // 把路径中的/api去掉
12.	    }
13.	  }))
14.	
15.	app.listen(8081, () => {
16.	    console.log('服务器成功启动');
17.	})
18.	

需要把前端代码当成静态资源部署在服务器上

目录

把服务器启动了,需要去http://127.0.0.1:8081获取页面

点击获取数据

数据成功获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502855.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nacos安装集群

本示例是安装在本地虚拟机linux环境。 &#xff08;1&#xff09;下载nacos https://download.csdn.net/download/lft18/90231054 &#xff08;2&#xff09;上传服务器并修改配置 放到/app/nacos目录下&#xff1a; 解压&#xff1a; tar -zxvf nacos-server-1.4.1.tar.…

taro转H5端踩坑

项目场景&#xff1a; 在利用taro进行多端开发时踩坑随记&#xff1a; 问题描述 在编译h5端的时候提示&#xff1a; Uncaught TypeError: (prevProps.className || prevProps.class || “”).split is not a function" return <ScrollView scrollY onScrollToLower{…

REVERSE-COMPETITION-CCSSSC-2025

REVERSE-COMPETITION-CCSSSC-2025 donntyouseeHappyLockkernel_traffic donntyousee elf64&#xff0c;ida反编译不太行&#xff0c;有花指令&#xff0c;直接调汇编 读输入 读输入前有条打印”plz input your flag”&#xff0c;肯定是在.init_array&#xff0c;确实有很多 …

海外招聘丨 弗拉瑞克商学院—博士研究员:智能家居技术业务和能源管理中的数据分析和人工智能

雇主简介 Vlerick 是一所领先的国际商学院……与众不同。是的&#xff0c;我们提供完全认可的世界一流教育课程&#xff0c;将理论知识和实践见解完美结合。是的&#xff0c;我们是一家领先的学术机构&#xff0c;拥有创新和独立研究的悠久传统。是的&#xff0c;我们拥有国际…

设计模式 行为型 策略模式(Strategy Pattern)与 常见技术框架应用 解析

策略模式&#xff08;Strategy Pattern&#xff09;核心思想是将算法的实现从使用该算法的类中分离出来&#xff0c;作为独立的对象&#xff0c;通过接口来定义算法家族&#xff0c;这样就可以很容易地改变或扩展算法。通过这种方式&#xff0c;可以避免在客户端代码中使用大量…

如何使用脚手架工具开始,快速搭建一个 Express 项目的基础架构

前言 将从如何使用脚手架工具开始&#xff0c;快速搭建一个 Express 项目的基础架构。接着&#xff0c;文章将详细讲解 Express 中间件的概念、分类以及如何有效地使用中间件来增强应用的功能和性能。最后&#xff0c;我们将讨论如何制定合理的接口规范&#xff0c;以确保 API …

《Opencv》基础操作详解(5)

接上篇&#xff1a;《Opencv》基础操作详解&#xff08;4&#xff09;-CSDN博客 目录 接上篇&#xff1a;《Opencv》基础操作详解&#xff08;4&#xff09;-CSDN博客 25、轮廓近似 简介 接口用法 参数说明 返回值 代码示例 结果展示 26、轮廓最小外接圆 简介 接口用…

Java虚拟机面试题:内存管理(上)

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

测试用例颗粒度说明

当我们在编写测试用例时&#xff0c;总是会遇到一个问题&#xff1a;如何确定测试用例的颗粒度&#xff1f;测试用例过于粗糙&#xff0c;可能无法全面覆盖系统的细节&#xff1b;而颗粒度过细&#xff0c;又会导致测试重复、冗余。掌握合适的颗粒度&#xff0c;不仅可以提高测…

【C++】深入解析二维数组初始化与越界问题

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;问题代码背景问题现象 &#x1f4af;初步分析与发现的问题1. 二维数组的初始化问题补充说明 2. 数组越界访问为什么数组越界问题没有直接报错&#xff1f; &#x1f4af;解…

Unity性能优化总结

目录 前言 移动端常见性能优化指标​编辑 包体大小优化 FPS CPU占用率 GPU占用率 内存 发热和耗电量 流量优化 前言 终于有时间了&#xff0c;我将在最近两个项目中进行优化的一些经验进行归纳总结以飨读者。因为我习惯用思维导图&#xff0c;所以归纳的内容主要以图来…

用QT实现 端口扫描工具1

安装在线QT&#xff0c;尽量是完整地自己进行安装&#xff0c;不然会少包 参考【保姆级图文教程】QT下载、安装、入门、配置VS Qt环境-CSDN博客 临时存储空间不够。 Windows系统通常会使用C盘来存储临时文件。 修改临时文件存储位置 打开系统属性&#xff1a; 右键点击“此电…

鸿蒙HarmonyOS开发:基于Swiper组件和自定义指示器实现多图片进度条轮播功能

文章目录 一、概述1、场景介绍2、技术选型 二、实现方案1、图片区域实现2、底部导航点设计3、手动切换 三、所有代码1、设置沉浸式2、外层Tabs效果3、ImageSwiper组件 四、效果展示 一、概述 在短视频平台上&#xff0c;经常可以见到多图片合集。它的特点是&#xff1a;由多张…

【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析

文章目录 类的加载器ClassLoader自定义类加载器双亲委派机制概念源码分析优势劣势如何打破Tomcat 沙箱安全机制JDK9 双亲委派机制变化 类的加载器 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoa…

nginx学习之路-nginx配置https服务器

文章目录 1. 生成证书2. 配置证书1. 拷贝证书文件2. 修改conf/nginx.conf文件内容 3. 查看效果1. 重载配置2. 访问 1. 生成证书 在linux系统下执行&#xff0c;使用openssl命令。&#xff08;windows环境也可以使用cmder&#xff09; # 1. 生成私钥 server2025.key(无密码保护…

鸿蒙应用开发搬砖经验之—使用DevTools工具调试前端页面

环境说明&#xff1a; 系统环境&#xff1a;Mac mini M2 14.5 (23F79) 开发IDE&#xff1a;DevEco Studio 5.0.1 Release 配置步骤&#xff1a; 按着官方的指引来慢慢一步一步来&#xff0c;但前提是要配置好SDK的路径&#xff08;没有配置的话&#xff0c;可能先看下面的配…

【NLP高频面题 - 分布式训练篇】ZeRO主要为了解决什么问题?

【NLP高频面题 - 分布式训练篇】ZeRO主要为了解决什么问题&#xff1f; 重要性&#xff1a;★★ 零冗余优化器技术由 DeepSpeed 代码库提出&#xff0c;主要用于解决数据并行中的模型冗余问题&#xff0c;即每张 GPU 均需要复制一份模型参数。 ZeRO的全称是Zero Redundancy …

《探秘计算机视觉与深度学习:开启智能视觉新时代》

《探秘计算机视觉与深度学习&#xff1a;开启智能视觉新时代》 一、追溯起源&#xff1a;从萌芽到崭露头角二、核心技术&#xff1a;解锁智能视觉的密码&#xff08;一&#xff09;卷积神经网络&#xff08;CNN&#xff09;&#xff1a;图像识别的利器&#xff08;二&#xff0…

[paddle] 非线性拟合问题的训练

利用paddlepaddle建立神经网络&#xff0c;模拟有限个数据的非线性拟合 本文仍然考虑 f ( x ) sin ⁡ ( x ) x f(x)\frac{\sin(x)}{x} f(x)xsin(x)​ 函数在区间 [-10,10] 上固定数据的拟合。 import paddle import paddle.nn as nn import numpy as np import matplotlib.…

详解GPT-信息抽取任务 (GPT-3 FAMILY LARGE LANGUAGE MODELS)

GPT-3 FAMILY LARGE LANGUAGE MODELS Information Extraction 自然语言处理信息提取任务&#xff08;NLP-IE&#xff09;&#xff1a;从非结构化文本数据中提取结构化数据&#xff0c;例如提取实体、关系和事件 [164]。将非结构化文本数据转换为结构化数据可以实现高效的数据处…